From 731cd6091672e6c061a01789962defb35c524066 Mon Sep 17 00:00:00 2001 From: mb Date: Tue, 26 Mar 2024 14:18:31 +0530 Subject: [PATCH] Initial Commit --- .coveragerc | 23 + .editorconfig | 21 + .eslintignore | 8 + .eslintrc | 124 + .git-blame-ignore-revs | 42 + .github/CONTRIBUTING.md | 36 + .github/ISSUE_TEMPLATE/bug_report.md | 47 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.md | 28 + .../question-about-using-frappe.md | 19 + .github/PULL_REQUEST_TEMPLATE.md | 33 + .github/dependabot.yml | 6 + .github/frappe-framework-logo.svg | 5 + .github/helper/db/mariadb.json | 19 + .github/helper/db/postgres.json | 18 + .github/helper/documentation.py | 64 + .github/helper/install.sh | 71 + .github/helper/install_dependencies.sh | 14 + .github/helper/roulette.py | 144 + .github/labeler.yml | 4 + .github/stale.yml | 34 + .github/try-on-f-cloud-button.svg | 32 + .github/workflows/backport.yml | 26 + .github/workflows/create-release.yml | 34 + .github/workflows/initiate_release.yml | 32 + .github/workflows/labeller.yml | 12 + .github/workflows/linters.yml | 97 + .github/workflows/lock.yml | 21 + .github/workflows/on_release.yml | 65 + .github/workflows/patch-mariadb-tests.yml | 169 + .github/workflows/pre-commit.yml | 26 + .github/workflows/publish-assets-develop.yml | 45 + .github/workflows/release_notes.yml | 42 + .github/workflows/server-tests.yml | 164 + .github/workflows/ui-tests.yml | 162 + .gitignore | 196 + .mergify.yml | 73 + .pre-commit-config.yaml | 72 + .releaserc | 24 + .semgrepignore | 0 CODEOWNERS | 6 + CODE_OF_CONDUCT.md | 46 + LICENSE | 21 + README.md | 84 + SECURITY.md | 7 + attributions.md | 32 + codecov.yml | 60 + commitlint.config.js | 25 + cypress.config.js | 27 + cypress/fixtures/child_table_doctype.js | 30 + cypress/fixtures/child_table_doctype_1.js | 59 + .../fixtures/custom_submittable_doctype.js | 53 + .../fixtures/data_field_validation_doctype.js | 65 + cypress/fixtures/datetime_doctype.js | 48 + cypress/fixtures/doctype_to_link.js | 45 + cypress/fixtures/doctype_with_child_table.js | 52 + cypress/fixtures/doctype_with_phone.js | 46 + cypress/fixtures/doctype_with_tab_break.js | 54 + cypress/fixtures/example.json | 5 + cypress/fixtures/form_builder_doctype.js | 65 + .../sample_attachments/attachment-1.jpg | Bin 0 -> 19986 bytes .../sample_attachments/attachment-10.txt | 1 + .../sample_attachments/attachment-11.txt | 1 + .../sample_attachments/attachment-2.txt | 1 + .../sample_attachments/attachment-3.txt | 1 + .../sample_attachments/attachment-4.txt | 1 + .../sample_attachments/attachment-5.txt | 1 + .../sample_attachments/attachment-6.txt | 1 + .../sample_attachments/attachment-7.txt | 1 + .../sample_attachments/attachment-8.txt | 1 + .../sample_attachments/attachment-9.txt | 1 + cypress/fixtures/sample_image.jpg | Bin 0 -> 249403 bytes cypress/integration/api.js | 44 + cypress/integration/assignment_rule.js | 16 + cypress/integration/awesome_bar.js | 48 + cypress/integration/control_attach.js | 251 + cypress/integration/control_autocomplete.js | 64 + cypress/integration/control_barcode.js | 57 + cypress/integration/control_color.js | 80 + cypress/integration/control_currency.js | 88 + cypress/integration/control_data.js | 148 + cypress/integration/control_date.js | 86 + cypress/integration/control_date_range.js | 48 + cypress/integration/control_duration.js | 46 + cypress/integration/control_dynamic_link.js | 159 + cypress/integration/control_float.js | 110 + cypress/integration/control_icon.js | 55 + cypress/integration/control_link.js | 334 + .../integration/control_markdown_editor.js | 22 + cypress/integration/control_phone.js | 99 + cypress/integration/control_rating.js | 54 + cypress/integration/control_select.js | 41 + cypress/integration/custom_buttons.js | 57 + cypress/integration/customize_form.js | 24 + cypress/integration/dashboard.js | 50 + cypress/integration/dashboard_chart.js | 22 + cypress/integration/dashboard_links.js | 94 + .../integration/data_field_form_validation.js | 45 + cypress/integration/datetime.js | 126 + .../datetime_field_form_validation.js | 19 + cypress/integration/depends_on.js | 152 + cypress/integration/discussions.js | 85 + cypress/integration/file_uploader.js | 86 + cypress/integration/first_day_of_the_week.js | 51 + cypress/integration/folder_navigation.js | 97 + cypress/integration/form.js | 171 + cypress/integration/form_builder.js | 282 + cypress/integration/form_tab_break.js | 30 + cypress/integration/form_tour.js | 94 + cypress/integration/grid.js | 114 + cypress/integration/grid_configuration.js | 25 + cypress/integration/grid_keyboard_shortcut.js | 54 + cypress/integration/grid_pagination.js | 80 + cypress/integration/grid_search.js | 133 + cypress/integration/kanban.js | 134 + cypress/integration/list_paging.js | 42 + cypress/integration/list_view.js | 55 + cypress/integration/list_view_drag_select.js | 49 + cypress/integration/list_view_settings.js | 42 + cypress/integration/login.js | 67 + cypress/integration/multi_select_dialog.js | 107 + cypress/integration/navigation.js | 36 + cypress/integration/number_card.js | 22 + cypress/integration/permissions.js | 41 + cypress/integration/query_report.js | 91 + cypress/integration/recorder.js | 72 + cypress/integration/relative_time_filters.js | 47 + cypress/integration/report_view.js | 47 + cypress/integration/rounding.js | 104 + cypress/integration/routing.js | 40 + cypress/integration/sidebar.js | 144 + cypress/integration/socket_updates.js | 80 + cypress/integration/table_multiselect.js | 59 + cypress/integration/url_data_field.js | 42 + cypress/integration/view_routing.js | 231 + cypress/integration/web_form.js | 278 + cypress/integration/workspace.js | 254 + cypress/integration/workspace_blocks.js | 185 + cypress/plugins/index.js | 17 + cypress/support/commands.js | 550 ++ cypress/support/e2e.js | 25 + cypress/tsconfig.json | 12 + esbuild/build-cleanup.js | 30 + esbuild/esbuild.js | 525 ++ esbuild/ignore-assets.js | 11 + esbuild/index.js | 1 + esbuild/sass_options.js | 24 + esbuild/utils.js | 146 + esbuild/xhiveframework-html.js | 44 + esbuild/xhiveframework-vue-style.js | 59 + generate_bootstrap_theme.js | 28 + hooks.md | 36 + node_utils.js | 60 + package.json | 91 + pyproject.toml | 138 + realtime/handlers/xhiveframework_handlers.js | 193 + realtime/index.js | 60 + realtime/middlewares/authenticate.js | 81 + realtime/utils.js | 23 + sider.yml | 3 + socketio.js | 1 + xhiveframework/__init__.py | 2481 +++++++ xhiveframework/api/__init__.py | 80 + xhiveframework/api/utils.py | 0 xhiveframework/api/v1.py | 118 + xhiveframework/api/v2.py | 193 + xhiveframework/app.py | 532 ++ xhiveframework/auth.py | 685 ++ xhiveframework/automation/__init__.py | 0 xhiveframework/automation/doctype/__init__.py | 0 .../doctype/assignment_rule/__init__.py | 0 .../assignment_rule/assignment_rule.js | 77 + .../assignment_rule/assignment_rule.json | 179 + .../assignment_rule/assignment_rule.py | 399 ++ .../assignment_rule/test_assignment_rule.py | 452 ++ .../doctype/assignment_rule_day/__init__.py | 0 .../assignment_rule_day.json | 28 + .../assignment_rule_day.py | 22 + .../doctype/assignment_rule_user/__init__.py | 0 .../assignment_rule_user.json | 34 + .../assignment_rule_user.py | 22 + .../doctype/auto_repeat/__init__.py | 0 .../doctype/auto_repeat/auto_repeat.js | 122 + .../doctype/auto_repeat/auto_repeat.json | 262 + .../doctype/auto_repeat/auto_repeat.py | 575 ++ .../doctype/auto_repeat/auto_repeat_list.js | 11 + .../auto_repeat/auto_repeat_schedule.html | 19 + .../doctype/auto_repeat/test_auto_repeat.py | 283 + .../doctype/auto_repeat_day/__init__.py | 0 .../auto_repeat_day/auto_repeat_day.json | 33 + .../auto_repeat_day/auto_repeat_day.py | 22 + .../automation/doctype/milestone/__init__.py | 0 .../automation/doctype/milestone/milestone.js | 7 + .../doctype/milestone/milestone.json | 81 + .../automation/doctype/milestone/milestone.py | 27 + .../doctype/milestone/test_milestone.py | 8 + .../doctype/milestone_tracker/__init__.py | 0 .../milestone_tracker/milestone_tracker.js | 31 + .../milestone_tracker/milestone_tracker.json | 61 + .../milestone_tracker/milestone_tracker.py | 64 + .../test_milestone_tracker.py | 46 + .../automation/doctype/reminder/__init__.py | 0 .../automation/doctype/reminder/reminder.js | 8 + .../automation/doctype/reminder/reminder.json | 90 + .../automation/doctype/reminder/reminder.py | 94 + .../doctype/reminder/test_reminder.py | 27 + .../automation/workspace/tools/tools.json | 360 + xhiveframework/boot.py | 470 ++ xhiveframework/build.py | 419 ++ xhiveframework/cache_manager.py | 241 + xhiveframework/change_log/__init__.py | 0 xhiveframework/change_log/current/readme.md | 3 + xhiveframework/change_log/v10/v10_0_0.md | 10 + xhiveframework/change_log/v11/v11_1_0.md | 19 + xhiveframework/change_log/v12/v12_0_0.md | 29 + xhiveframework/change_log/v13/v13_0_0.md | 54 + xhiveframework/change_log/v13/v13_1_0.md | 22 + xhiveframework/change_log/v13/v13_2_0.md | 32 + xhiveframework/change_log/v13/v13_3_0.md | 49 + xhiveframework/change_log/v5/v5_0_18.md | 6 + xhiveframework/change_log/v5/v5_0_20.md | 1 + xhiveframework/change_log/v5/v5_0_32.md | 5 + xhiveframework/change_log/v5/v5_1_0.md | 3 + xhiveframework/change_log/v5/v5_1_1.md | 1 + xhiveframework/change_log/v5/v5_3_0.md | 40 + xhiveframework/change_log/v5/v5_4_0.md | 1 + xhiveframework/change_log/v6/v6_0_0.md | 3 + xhiveframework/change_log/v6/v6_0_8.md | 1 + xhiveframework/change_log/v6/v6_12_0.md | 1 + xhiveframework/change_log/v6/v6_13_0.md | 4 + xhiveframework/change_log/v6/v6_14_1.md | 1 + xhiveframework/change_log/v6/v6_15_0.md | 4 + xhiveframework/change_log/v6/v6_16_1.md | 1 + xhiveframework/change_log/v6/v6_16_4.md | 1 + xhiveframework/change_log/v6/v6_17_0.md | 4 + xhiveframework/change_log/v6/v6_1_0.md | 7 + xhiveframework/change_log/v6/v6_20_0.md | 5 + xhiveframework/change_log/v6/v6_21_0.md | 1 + xhiveframework/change_log/v6/v6_22_0.md | 2 + xhiveframework/change_log/v6/v6_23_0.md | 2 + xhiveframework/change_log/v6/v6_25_0.md | 4 + xhiveframework/change_log/v6/v6_26_0.md | 2 + xhiveframework/change_log/v6/v6_26_6.md | 1 + xhiveframework/change_log/v6/v6_27_1.md | 6 + xhiveframework/change_log/v6/v6_27_11.md | 2 + xhiveframework/change_log/v6/v6_2_0.md | 3 + xhiveframework/change_log/v6/v6_3_0.md | 2 + xhiveframework/change_log/v6/v6_4_0.md | 1 + xhiveframework/change_log/v6/v6_4_8.md | 20 + xhiveframework/change_log/v6/v6_5_0.md | 2 + xhiveframework/change_log/v6/v6_6_0.md | 1 + xhiveframework/change_log/v6/v6_7_0.md | 3 + xhiveframework/change_log/v6/v6_8_0.md | 2 + xhiveframework/change_log/v7/v7_0_0.md | 36 + xhiveframework/change_log/v7/v7_0_18.md | 2 + xhiveframework/change_log/v7/v7_1_0.md | 24 + xhiveframework/change_log/v7/v7_2_0.md | 18 + xhiveframework/change_log/v8/v8_0_0.md | 41 + xhiveframework/change_log/v8/v8_7_0.md | 2 + xhiveframework/change_log/v8/v8_8_0.md | 2 + xhiveframework/client.py | 506 ++ xhiveframework/commands/__init__.py | 124 + xhiveframework/commands/redis_utils.py | 69 + xhiveframework/commands/scheduler.py | 271 + xhiveframework/commands/site.py | 1543 +++++ xhiveframework/commands/translate.py | 123 + xhiveframework/commands/utils.py | 1181 ++++ xhiveframework/config/__init__.py | 64 + xhiveframework/contacts/__init__.py | 0 .../contacts/address_and_contact.py | 149 + xhiveframework/contacts/doctype/__init__.py | 0 .../contacts/doctype/address/__init__.py | 0 .../contacts/doctype/address/address.js | 75 + .../contacts/doctype/address/address.json | 226 + .../contacts/doctype/address/address.py | 356 + .../contacts/doctype/address/test_address.py | 58 + .../doctype/address_template/__init__.py | 0 .../address_template/address_template.jinja | 10 + .../address_template/address_template.js | 16 + .../address_template/address_template.json | 64 + .../address_template/address_template.py | 52 + .../address_template/test_address_template.py | 37 + .../contacts/doctype/contact/__init__.py | 0 .../contacts/doctype/contact/contact.js | 156 + .../contacts/doctype/contact/contact.json | 398 ++ .../contacts/doctype/contact/contact.py | 414 ++ .../contacts/doctype/contact/contact_list.js | 3 + .../contacts/doctype/contact/test_contact.py | 78 + .../doctype/contact/test_records.json | 39 + .../doctype/contact_email/__init__.py | 0 .../doctype/contact_email/contact_email.json | 39 + .../doctype/contact_email/contact_email.py | 23 + .../doctype/contact_phone/__init__.py | 0 .../doctype/contact_phone/contact_phone.json | 50 + .../doctype/contact_phone/contact_phone.py | 24 + .../contacts/doctype/gender/__init__.py | 0 .../contacts/doctype/gender/gender.js | 6 + .../contacts/doctype/gender/gender.json | 48 + .../contacts/doctype/gender/gender.py | 18 + .../contacts/doctype/gender/test_gender.py | 7 + .../contacts/doctype/salutation/__init__.py | 0 .../contacts/doctype/salutation/salutation.js | 6 + .../doctype/salutation/salutation.json | 61 + .../contacts/doctype/salutation/salutation.py | 18 + .../doctype/salutation/test_records.json | 8 + .../doctype/salutation/test_salutation.py | 7 + xhiveframework/contacts/report/__init__.py | 0 .../report/addresses_and_contacts/__init__.py | 0 .../addresses_and_contacts.js | 33 + .../addresses_and_contacts.json | 32 + .../addresses_and_contacts.py | 123 + .../test_addresses_and_contacts.py | 113 + xhiveframework/core/README.md | 1 + xhiveframework/core/__init__.py | 2 + xhiveframework/core/api/__init__.py | 0 xhiveframework/core/api/file.py | 124 + xhiveframework/core/doctype/__init__.py | 2 + .../core/doctype/access_log/__init__.py | 0 .../core/doctype/access_log/access_log.js | 17 + .../core/doctype/access_log/access_log.json | 156 + .../core/doctype/access_log/access_log.py | 107 + .../doctype/access_log/access_log_list.js | 7 + .../doctype/access_log/test_access_log.py | 175 + .../core/doctype/activity_log/__init__.py | 0 .../core/doctype/activity_log/activity_log.js | 9 + .../doctype/activity_log/activity_log.json | 181 + .../core/doctype/activity_log/activity_log.py | 81 + .../doctype/activity_log/activity_log_list.js | 12 + .../core/doctype/activity_log/feed.py | 20 + .../doctype/activity_log/test_activity_log.py | 95 + .../__init__.py | 0 .../amended_document_naming_settings.json | 44 + .../amended_document_naming_settings.py | 23 + .../core/doctype/audit_trail/__init__.py | 0 .../core/doctype/audit_trail/audit_trail.html | 77 + .../core/doctype/audit_trail/audit_trail.js | 67 + .../core/doctype/audit_trail/audit_trail.json | 95 + .../core/doctype/audit_trail/audit_trail.py | 129 + .../audit_trail_rows_added_removed.html | 34 + .../doctype/audit_trail/test_audit_trail.py | 134 + .../core/doctype/block_module/__init__.py | 0 .../doctype/block_module/block_module.json | 31 + .../core/doctype/block_module/block_module.py | 21 + .../core/doctype/comment/__init__.py | 0 .../core/doctype/comment/comment.js | 7 + .../core/doctype/comment/comment.json | 139 + .../core/doctype/comment/comment.py | 207 + .../core/doctype/comment/test_comment.py | 120 + .../core/doctype/communication/README.md | 1 + .../core/doctype/communication/__init__.py | 2 + .../doctype/communication/communication.js | 359 + .../doctype/communication/communication.json | 468 ++ .../doctype/communication/communication.py | 702 ++ .../communication/communication_list.js | 30 + .../core/doctype/communication/email.py | 300 + .../core/doctype/communication/mixins.py | 321 + .../communication/test_communication.py | 465 ++ .../doctype/communication/test_records.json | 10 + .../doctype/communication_link/__init__.py | 0 .../communication_link.json | 47 + .../communication_link/communication_link.py | 28 + .../core/doctype/custom_docperm/__init__.py | 0 .../doctype/custom_docperm/custom_docperm.js | 6 + .../custom_docperm/custom_docperm.json | 241 + .../doctype/custom_docperm/custom_docperm.py | 37 + .../custom_docperm/test_custom_docperm.py | 9 + .../core/doctype/custom_role/__init__.py | 0 .../core/doctype/custom_role/custom_role.js | 6 + .../core/doctype/custom_role/custom_role.json | 76 + .../core/doctype/custom_role/custom_role.py | 36 + .../doctype/custom_role/test_custom_role.py | 9 + .../core/doctype/data_export/__init__.py | 0 .../core/doctype/data_export/data_export.js | 180 + .../core/doctype/data_export/data_export.json | 84 + .../core/doctype/data_export/data_export.py | 20 + .../core/doctype/data_export/exporter.py | 480 ++ .../doctype/data_export/test_data_exporter.py | 113 + .../core/doctype/data_import/__init__.py | 0 .../core/doctype/data_import/data_import.css | 3 + .../core/doctype/data_import/data_import.js | 536 ++ .../core/doctype/data_import/data_import.json | 198 + .../core/doctype/data_import/data_import.py | 308 + .../doctype/data_import/data_import_list.js | 45 + .../core/doctype/data_import/exporter.py | 249 + .../fixtures/sample_import_file.csv | 5 + .../sample_import_file_for_update.csv | 2 + .../sample_import_file_without_mandatory.csv | 5 + .../core/doctype/data_import/importer.py | 1263 ++++ .../doctype/data_import/patches/__init__.py | 0 ...ove_stale_docfields_from_legacy_version.py | 6 + .../doctype/data_import/test_data_import.py | 8 + .../core/doctype/data_import/test_exporter.py | 100 + .../core/doctype/data_import/test_importer.py | 251 + .../core/doctype/data_import_log/__init__.py | 0 .../data_import_log/data_import_log.js | 7 + .../data_import_log/data_import_log.json | 84 + .../data_import_log/data_import_log.py | 25 + .../data_import_log/test_data_import_log.py | 9 + .../core/doctype/defaultvalue/README.md | 1 + .../core/doctype/defaultvalue/__init__.py | 2 + .../doctype/defaultvalue/defaultvalue.json | 47 + .../core/doctype/defaultvalue/defaultvalue.py | 39 + .../core/doctype/deleted_document/__init__.py | 0 .../deleted_document/deleted_document.js | 22 + .../deleted_document/deleted_document.json | 81 + .../deleted_document/deleted_document.py | 92 + .../deleted_document/deleted_document_list.js | 50 + .../deleted_document/test_deleted_document.py | 9 + .../core/doctype/docfield/README.md | 3 + .../core/doctype/docfield/__init__.py | 2 + .../core/doctype/docfield/docfield.json | 580 ++ .../core/doctype/docfield/docfield.py | 153 + .../core/doctype/docperm/__init__.py | 2 + .../core/doctype/docperm/docperm.json | 221 + .../core/doctype/docperm/docperm.py | 36 + .../core/doctype/docshare/__init__.py | 0 .../core/doctype/docshare/docshare.js | 6 + .../core/doctype/docshare/docshare.json | 113 + .../core/doctype/docshare/docshare.py | 97 + .../core/doctype/docshare/test_docshare.py | 226 + .../core/doctype/docshare/test_records.json | 1 + xhiveframework/core/doctype/doctype/README.md | 15 + .../core/doctype/doctype/__init__.py | 2 + .../doctype/doctype/boilerplate/__init__.py | 0 .../doctype/boilerplate/controller._py | 9 + .../doctype/doctype/boilerplate/controller.js | 8 + .../doctype/boilerplate/controller_list.html | 34 + .../doctype/boilerplate/controller_list.js | 5 + .../boilerplate/templates/controller.html | 7 + .../boilerplate/templates/controller_row.html | 4 + .../doctype/boilerplate/test_controller._py | 9 + .../core/doctype/doctype/doctype.js | 186 + .../core/doctype/doctype/doctype.json | 790 +++ .../core/doctype/doctype/doctype.py | 1895 ++++++ .../core/doctype/doctype/doctype_list.js | 123 + .../core/doctype/doctype/patches/set_route.py | 8 + .../core/doctype/doctype/test_doctype.py | 819 +++ .../core/doctype/doctype_action/__init__.py | 0 .../doctype_action/doctype_action.json | 74 + .../doctype/doctype_action/doctype_action.py | 27 + .../core/doctype/doctype_link/__init__.py | 0 .../doctype/doctype_link/doctype_link.json | 87 + .../core/doctype/doctype_link/doctype_link.py | 29 + .../core/doctype/doctype_state/__init__.py | 0 .../doctype/doctype_state/doctype_state.json | 50 + .../doctype/doctype_state/doctype_state.py | 26 + .../doctype/document_naming_rule/__init__.py | 0 .../document_naming_rule.js | 46 + .../document_naming_rule.json | 115 + .../document_naming_rule.py | 69 + .../test_document_naming_rule.py | 70 + .../__init__.py | 0 .../document_naming_rule_condition.js | 7 + .../document_naming_rule_condition.json | 49 + .../document_naming_rule_condition.py | 24 + .../test_document_naming_rule_condition.py | 8 + .../document_naming_settings/__init__.py | 0 .../document_naming_settings.js | 80 + .../document_naming_settings.json | 165 + .../document_naming_settings.py | 256 + .../test_document_naming_settings.py | 117 + .../doctype/document_share_key/__init__.py | 0 .../document_share_key/document_share_key.js | 7 + .../document_share_key.json | 73 + .../document_share_key/document_share_key.py | 34 + .../test_document_share_key.py | 9 + .../core/doctype/domain/__init__.py | 0 xhiveframework/core/doctype/domain/domain.js | 6 + .../core/doctype/domain/domain.json | 54 + xhiveframework/core/doctype/domain/domain.py | 136 + .../core/doctype/domain/test_domain.py | 7 + .../core/doctype/domain_settings/__init__.py | 0 .../domain_settings/domain_settings.js | 69 + .../domain_settings/domain_settings.json | 56 + .../domain_settings/domain_settings.py | 102 + .../core/doctype/dynamic_link/__init__.py | 0 .../doctype/dynamic_link/dynamic_link.json | 47 + .../core/doctype/dynamic_link/dynamic_link.py | 43 + .../core/doctype/error_log/__init__.py | 0 .../core/doctype/error_log/error_log.js | 17 + .../core/doctype/error_log/error_log.json | 95 + .../core/doctype/error_log/error_log.py | 42 + .../core/doctype/error_log/error_log_list.js | 25 + .../core/doctype/error_log/test_error_log.py | 72 + xhiveframework/core/doctype/file/__init__.py | 2 + .../core/doctype/file/exceptions.py | 16 + xhiveframework/core/doctype/file/file.js | 106 + xhiveframework/core/doctype/file/file.json | 224 + xhiveframework/core/doctype/file/file.py | 835 +++ xhiveframework/core/doctype/file/test_file.py | 878 +++ xhiveframework/core/doctype/file/utils.py | 429 ++ .../core/doctype/has_domain/__init__.py | 0 .../core/doctype/has_domain/has_domain.json | 32 + .../core/doctype/has_domain/has_domain.py | 21 + .../core/doctype/has_role/__init__.py | 0 .../core/doctype/has_role/has_role.json | 34 + .../core/doctype/has_role/has_role.py | 25 + .../doctype/installed_application/__init__.py | 0 .../installed_application.json | 49 + .../installed_application.py | 24 + .../installed_applications/__init__.py | 0 .../installed_applications.js | 67 + .../installed_applications.json | 42 + .../installed_applications.py | 87 + .../test_installed_applications.py | 16 + .../core/doctype/language/__init__.py | 0 .../core/doctype/language/language.js | 6 + .../core/doctype/language/language.json | 80 + .../core/doctype/language/language.py | 84 + .../core/doctype/language/test_language.py | 9 + .../core/doctype/log_setting_user/__init__.py | 0 .../log_setting_user/log_setting_user.js | 7 + .../log_setting_user/log_setting_user.json | 34 + .../log_setting_user/log_setting_user.py | 22 + .../log_setting_user/test_log_setting_user.py | 8 + .../core/doctype/log_settings/__init__.py | 0 .../core/doctype/log_settings/log_settings.js | 14 + .../doctype/log_settings/log_settings.json | 43 + .../core/doctype/log_settings/log_settings.py | 192 + .../doctype/log_settings/test_log_settings.py | 104 + .../core/doctype/logs_to_clear/__init__.py | 0 .../doctype/logs_to_clear/logs_to_clear.json | 43 + .../doctype/logs_to_clear/logs_to_clear.py | 23 + .../core/doctype/module_def/README.md | 1 + .../core/doctype/module_def/__init__.py | 2 + .../core/doctype/module_def/module_def.js | 20 + .../core/doctype/module_def/module_def.json | 166 + .../core/doctype/module_def/module_def.py | 89 + .../doctype/module_def/module_def_list.js | 16 + .../doctype/module_def/test_module_def.py | 9 + .../core/doctype/module_profile/__init__.py | 0 .../doctype/module_profile/module_profile.js | 23 + .../module_profile/module_profile.json | 66 + .../doctype/module_profile/module_profile.py | 24 + .../module_profile/test_module_profile.py | 29 + .../core/doctype/navbar_item/__init__.py | 0 .../core/doctype/navbar_item/navbar_item.js | 7 + .../core/doctype/navbar_item/navbar_item.json | 89 + .../core/doctype/navbar_item/navbar_item.py | 27 + .../doctype/navbar_item/test_navbar_item.py | 8 + .../core/doctype/navbar_settings/__init__.py | 0 .../navbar_settings/navbar_settings.js | 7 + .../navbar_settings/navbar_settings.json | 91 + .../navbar_settings/navbar_settings.py | 55 + .../navbar_settings/test_navbar_settings.py | 8 + .../core/doctype/package/__init__.py | 0 .../GNU Affero General Public License.md | 614 ++ .../licenses/GNU General Public License.md | 617 ++ .../doctype/package/licenses/MIT License.md | 17 + .../core/doctype/package/package.js | 20 + .../core/doctype/package/package.json | 76 + .../core/doctype/package/package.py | 42 + .../core/doctype/package/test_package.py | 112 + .../core/doctype/package_import/__init__.py | 0 .../doctype/package_import/package_import.js | 7 + .../package_import/package_import.json | 65 + .../doctype/package_import/package_import.py | 80 + .../package_import/test_package_import.py | 9 + .../core/doctype/package_release/__init__.py | 0 .../package_release/package_release.js | 7 + .../package_release/package_release.json | 95 + .../package_release/package_release.py | 128 + .../package_release/test_package_release.py | 9 + xhiveframework/core/doctype/page/README.md | 1 + xhiveframework/core/doctype/page/__init__.py | 2 + xhiveframework/core/doctype/page/page.js | 16 + xhiveframework/core/doctype/page/page.json | 133 + xhiveframework/core/doctype/page/page.py | 190 + .../doctype/page/patches/drop_unused_pages.py | 6 + xhiveframework/core/doctype/page/test_page.py | 14 + .../core/doctype/page/test_records.json | 1 + .../core/doctype/patch_log/README.md | 1 + .../core/doctype/patch_log/__init__.py | 2 + .../core/doctype/patch_log/patch_log.js | 8 + .../core/doctype/patch_log/patch_log.json | 69 + .../core/doctype/patch_log/patch_log.py | 27 + .../core/doctype/patch_log/test_patch_log.py | 9 + .../doctype/permission_inspector/__init__.py | 0 .../permission_inspector.js | 24 + .../permission_inspector.json | 90 + .../permission_inspector.py | 75 + .../test_permission_inspector.py | 9 + .../core/doctype/prepared_report/__init__.py | 0 .../prepared_report/prepared_report.js | 55 + .../prepared_report/prepared_report.json | 154 + .../prepared_report/prepared_report.py | 284 + .../prepared_report/prepared_report_list.js | 7 + .../prepared_report/test_prepared_report.py | 92 + .../core/doctype/recorder/__init__.py | 0 .../core/doctype/recorder/recorder.js | 84 + .../core/doctype/recorder/recorder.json | 149 + .../core/doctype/recorder/recorder.py | 104 + .../core/doctype/recorder/recorder_list.js | 212 + .../core/doctype/recorder/test_recorder.py | 77 + .../core/doctype/recorder_query/__init__.py | 0 .../doctype/recorder_query/recorder_query.js | 8 + .../recorder_query/recorder_query.json | 106 + .../doctype/recorder_query/recorder_query.py | 53 + .../recorder_query/test_recorder_query.py | 9 + xhiveframework/core/doctype/report/README.md | 1 + .../core/doctype/report/__init__.py | 2 + .../doctype/report/boilerplate/controller.js | 8 + .../doctype/report/boilerplate/controller.py | 9 + xhiveframework/core/doctype/report/report.js | 68 + .../core/doctype/report/report.json | 242 + xhiveframework/core/doctype/report/report.py | 418 ++ .../core/doctype/report/test_records.json | 10 + .../core/doctype/report/test_report.py | 433 ++ .../doctype/report/user_activity_report.json | 17 + .../user_activity_report_without_sort.json | 17 + .../core/doctype/report_column/__init__.py | 0 .../doctype/report_column/report_column.json | 61 + .../doctype/report_column/report_column.py | 40 + .../core/doctype/report_filter/__init__.py | 0 .../doctype/report_filter/report_filter.json | 90 + .../doctype/report_filter/report_filter.py | 41 + xhiveframework/core/doctype/role/README.md | 1 + xhiveframework/core/doctype/role/__init__.py | 2 + .../v13_set_default_desk_properties.py | 11 + xhiveframework/core/doctype/role/role.js | 29 + xhiveframework/core/doctype/role/role.json | 176 + xhiveframework/core/doctype/role/role.py | 153 + .../core/doctype/role/test_records.json | 22 + xhiveframework/core/doctype/role/test_role.py | 53 + .../__init__.py | 0 .../role_permission_for_page_and_report.js | 127 + .../role_permission_for_page_and_report.json | 95 + .../role_permission_for_page_and_report.py | 107 + ...est_role_permission_for_page_and_report.py | 9 + .../core/doctype/role_profile/__init__.py | 0 .../core/doctype/role_profile/role_profile.js | 20 + .../doctype/role_profile/role_profile.json | 80 + .../core/doctype/role_profile/role_profile.py | 57 + .../doctype/role_profile/test_role_profile.py | 46 + .../core/doctype/rq_job/__init__.py | 0 xhiveframework/core/doctype/rq_job/rq_job.js | 32 + .../core/doctype/rq_job/rq_job.json | 163 + xhiveframework/core/doctype/rq_job/rq_job.py | 230 + .../core/doctype/rq_job/rq_job_list.js | 57 + .../core/doctype/rq_job/test_rq_job.py | 183 + .../core/doctype/rq_worker/__init__.py | 0 .../core/doctype/rq_worker/rq_worker.js | 9 + .../core/doctype/rq_worker/rq_worker.json | 146 + .../core/doctype/rq_worker/rq_worker.py | 109 + .../core/doctype/rq_worker/rq_worker_list.js | 9 + .../core/doctype/rq_worker/test_rq_worker.py | 17 + .../doctype/scheduled_job_log/__init__.py | 0 .../scheduled_job_log/scheduled_job_log.js | 7 + .../scheduled_job_log/scheduled_job_log.json | 65 + .../scheduled_job_log/scheduled_job_log.py | 27 + .../scheduled_job_log_list.js | 7 + .../test_scheduled_job_log.py | 8 + .../doctype/scheduled_job_type/__init__.py | 0 .../scheduled_job_type/scheduled_job_type.js | 7 + .../scheduled_job_type.json | 129 + .../scheduled_job_type/scheduled_job_type.py | 261 + .../test_scheduled_job_type.py | 90 + .../core/doctype/server_script/__init__.py | 0 .../doctype/server_script/server_script.js | 97 + .../doctype/server_script/server_script.json | 178 + .../doctype/server_script/server_script.py | 304 + .../server_script/server_script_list.js | 38 + .../server_script/server_script_utils.py | 80 + .../server_script/test_server_script.py | 342 + .../core/doctype/session_default/__init__.py | 0 .../session_default/session_default.json | 29 + .../session_default/session_default.py | 22 + .../session_default_settings/__init__.py | 0 .../session_default_settings.js | 15 + .../session_default_settings.json | 39 + .../session_default_settings.py | 59 + .../test_session_default_settings.py | 31 + .../core/doctype/sms_parameter/README.md | 1 + .../core/doctype/sms_parameter/__init__.py | 0 .../doctype/sms_parameter/sms_parameter.json | 51 + .../doctype/sms_parameter/sms_parameter.py | 23 + .../core/doctype/sms_settings/README.md | 1 + .../core/doctype/sms_settings/__init__.py | 0 .../core/doctype/sms_settings/sms_settings.js | 0 .../doctype/sms_settings/sms_settings.json | 80 + .../core/doctype/sms_settings/sms_settings.py | 157 + .../doctype/sms_settings/test_sms_settings.py | 7 + .../core/doctype/submission_queue/__init__.py | 0 .../submission_queue/submission_queue.js | 20 + .../submission_queue/submission_queue.json | 130 + .../submission_queue/submission_queue.py | 208 + .../submission_queue/test_submission_queue.py | 51 + .../core/doctype/success_action/__init__.py | 0 .../doctype/success_action/success_action.js | 60 + .../success_action/success_action.json | 84 + .../doctype/success_action/success_action.py | 22 + .../core/doctype/system_settings/__init__.py | 0 .../system_settings/system_settings.js | 89 + .../system_settings/system_settings.json | 683 ++ .../system_settings/system_settings.py | 217 + .../system_settings/test_system_settings.py | 7 + .../core/doctype/transaction_log/__init__.py | 0 .../core/doctype/transaction_log/readme.md | 16 + .../transaction_log/test_transaction_log.py | 46 + .../transaction_log/transaction_log.js | 4 + .../transaction_log/transaction_log.json | 124 + .../transaction_log/transaction_log.py | 86 + .../core/doctype/translation/__init__.py | 0 .../doctype/translation/test_translation.py | 110 + .../core/doctype/translation/translation.js | 8 + .../core/doctype/translation/translation.json | 111 + .../core/doctype/translation/translation.py | 46 + xhiveframework/core/doctype/user/__init__.py | 0 .../core/doctype/user/test_records.json | 95 + xhiveframework/core/doctype/user/test_user.py | 493 ++ xhiveframework/core/doctype/user/user.js | 413 ++ xhiveframework/core/doctype/user/user.json | 806 +++ xhiveframework/core/doctype/user/user.py | 1352 ++++ xhiveframework/core/doctype/user/user_list.js | 19 + .../doctype/user_document_type/__init__.py | 0 .../user_document_type.json | 109 + .../user_document_type/user_document_type.py | 30 + .../core/doctype/user_email/__init__.py | 0 .../core/doctype/user_email/user_email.json | 73 + .../core/doctype/user_email/user_email.py | 25 + .../core/doctype/user_group/__init__.py | 0 .../doctype/user_group/test_user_group.py | 8 + .../core/doctype/user_group/user_group.js | 7 + .../core/doctype/user_group/user_group.json | 50 + .../core/doctype/user_group/user_group.py | 27 + .../doctype/user_group_member/__init__.py | 0 .../test_user_group_member.py | 8 + .../user_group_member/user_group_member.js | 7 + .../user_group_member/user_group_member.json | 32 + .../user_group_member/user_group_member.py | 22 + .../core/doctype/user_permission/__init__.py | 0 .../user_permission/test_user_permission.py | 332 + .../user_permission/user_permission.js | 58 + .../user_permission/user_permission.json | 117 + .../user_permission/user_permission.py | 339 + .../user_permission/user_permission_help.html | 8 + .../user_permission/user_permission_list.js | 293 + .../user_select_document_type/__init__.py | 0 .../user_select_document_type.json | 33 + .../user_select_document_type.py | 22 + .../doctype/user_social_login/__init__.py | 0 .../user_social_login/user_social_login.json | 58 + .../user_social_login/user_social_login.py | 23 + .../core/doctype/user_type/__init__.py | 0 .../core/doctype/user_type/test_user_type.py | 64 + .../core/doctype/user_type/user_type.js | 71 + .../core/doctype/user_type/user_type.json | 145 + .../core/doctype/user_type/user_type.py | 346 + .../doctype/user_type/user_type_dashboard.py | 5 + .../core/doctype/user_type/user_type_list.js | 10 + .../core/doctype/user_type_module/__init__.py | 0 .../user_type_module/user_type_module.json | 33 + .../user_type_module/user_type_module.py | 22 + .../core/doctype/version/__init__.py | 0 .../core/doctype/version/test_records.json | 1 + .../core/doctype/version/test_version.py | 58 + .../core/doctype/version/version.js | 12 + .../core/doctype/version/version.json | 81 + .../core/doctype/version/version.py | 169 + .../core/doctype/version/version_view.html | 95 + .../core/doctype/view_log/__init__.py | 0 .../core/doctype/view_log/test_view_log.py | 34 + .../core/doctype/view_log/view_log.js | 6 + .../core/doctype/view_log/view_log.json | 56 + .../core/doctype/view_log/view_log.py | 28 + .../core/form_tour/doctype/doctype.json | 56 + xhiveframework/core/notifications.py | 44 + xhiveframework/core/page/__init__.py | 2 + .../core/page/dashboard_view/__init__.py | 0 .../page/dashboard_view/dashboard_view.js | 196 + .../page/dashboard_view/dashboard_view.json | 19 + .../core/page/permission_manager/README.md | 1 + .../core/page/permission_manager/__init__.py | 2 + .../permission_manager/permission_manager.css | 51 + .../permission_manager/permission_manager.js | 522 ++ .../permission_manager.json | 20 + .../permission_manager/permission_manager.py | 183 + .../permission_manager_help.html | 41 + xhiveframework/core/report/__init__.py | 2 + .../__init__.py | 0 .../database_storage_usage_by_tables.js | 6 + .../database_storage_usage_by_tables.json | 27 + .../database_storage_usage_by_tables.py | 40 + .../test_database_storage_usage_by_tables.py | 15 + .../report/document_share_report/__init__.py | 0 .../document_share_report.json | 24 + .../permitted_documents_for_user/__init__.py | 0 .../permitted_documents_for_user.js | 34 + .../permitted_documents_for_user.json | 23 + .../permitted_documents_for_user.py | 60 + .../report/transaction_log_report/__init__.py | 0 .../transaction_log_report.js | 10 + .../transaction_log_report.json | 26 + .../transaction_log_report.py | 117 + xhiveframework/core/utils.py | 91 + xhiveframework/core/web_form/__init__.py | 0 .../core/web_form/edit_profile/__init__.py | 0 .../web_form/edit_profile/edit_profile.js | 3 + .../web_form/edit_profile/edit_profile.json | 156 + .../web_form/edit_profile/edit_profile.py | 3 + .../core/workspace/build/build.json | 388 ++ .../core/workspace/users/users.json | 202 + .../welcome_workspace/welcome_workspace.json | 28 + xhiveframework/coverage.py | 77 + xhiveframework/custom/__init__.py | 0 xhiveframework/custom/doctype/__init__.py | 0 .../custom/doctype/client_script/README.md | 11 + .../custom/doctype/client_script/__init__.py | 2 + .../doctype/client_script/client_script.js | 158 + .../doctype/client_script/client_script.json | 114 + .../doctype/client_script/client_script.py | 27 + .../client_script/test_client_script.py | 9 + .../client_script/ui_test_client_script.js | 98 + .../custom/doctype/custom_field/README.md | 1 + .../custom/doctype/custom_field/__init__.py | 2 + .../doctype/custom_field/custom_field.js | 152 + .../doctype/custom_field/custom_field.json | 494 ++ .../doctype/custom_field/custom_field.py | 391 ++ .../doctype/custom_field/test_custom_field.py | 107 + .../doctype/custom_field/test_records.json | 1 + .../custom/doctype/customize_form/README.md | 1 + .../custom/doctype/customize_form/__init__.py | 2 + .../doctype/customize_form/customize_form.js | 445 ++ .../customize_form/customize_form.json | 426 ++ .../doctype/customize_form/customize_form.py | 794 +++ .../customize_form/test_customize_form.py | 427 ++ .../doctype/customize_form_field/__init__.py | 2 + .../customize_form_field.json | 491 ++ .../customize_form_field.py | 113 + .../custom/doctype/doctype_layout/__init__.py | 0 .../doctype/doctype_layout/doctype_layout.js | 105 + .../doctype_layout/doctype_layout.json | 75 + .../doctype/doctype_layout/doctype_layout.py | 92 + .../convert_web_forms_to_doctype_layout.py | 20 + .../doctype_layout/test_doctype_layout.py | 8 + .../doctype/doctype_layout_field/__init__.py | 0 .../doctype_layout_field.json | 38 + .../doctype_layout_field.py | 23 + .../custom/doctype/property_setter/README.md | 1 + .../doctype/property_setter/__init__.py | 2 + .../property_setter/patches/__init__.py | 0 .../remove_invalid_fetch_from_expressions.py | 28 + .../property_setter/property_setter.js | 10 + .../property_setter/property_setter.json | 154 + .../property_setter/property_setter.py | 98 + .../property_setter/test_property_setter.py | 9 + .../doctype/property_setter/test_records.json | 10 + .../custom/fixtures/temp_doctype.json | 166 + .../custom/fixtures/temp_singles.json | 166 + .../form_tour/custom_field/custom_field.json | 79 + .../customization/customization.json | 44 + .../custom_doctype/custom_doctype.json | 21 + .../custom_field/custom_field.json | 21 + .../naming_series/naming_series.json | 20 + .../print_format/print_format.json | 21 + .../report_builder/report_builder.json | 22 + .../role_permissions/role_permissions.json | 20 + .../onboarding_step/workflows/workflows.json | 20 + xhiveframework/custom/report/__init__.py | 0 .../report/audit_system_hooks/__init__.py | 0 .../audit_system_hooks/audit_system_hooks.js | 6 + .../audit_system_hooks.json | 27 + .../audit_system_hooks/audit_system_hooks.py | 68 + .../test_audit_system_hooks.py | 17 + xhiveframework/data/google_fonts.json | 56 + xhiveframework/database/__init__.py | 108 + xhiveframework/database/database.py | 1431 ++++ xhiveframework/database/db_manager.py | 88 + xhiveframework/database/mariadb/__init__.py | 0 xhiveframework/database/mariadb/database.py | 529 ++ .../database/mariadb/framework_mariadb.sql | 338 + xhiveframework/database/mariadb/schema.py | 119 + xhiveframework/database/mariadb/setup_db.py | 173 + xhiveframework/database/operator_map.py | 138 + xhiveframework/database/postgres/__init__.py | 0 xhiveframework/database/postgres/database.py | 471 ++ .../database/postgres/framework_postgres.sql | 345 + xhiveframework/database/postgres/schema.py | 159 + xhiveframework/database/postgres/setup_db.py | 91 + xhiveframework/database/query.py | 557 ++ xhiveframework/database/schema.py | 366 + xhiveframework/database/sequence.py | 99 + xhiveframework/database/utils.py | 78 + xhiveframework/defaults.py | 264 + xhiveframework/deferred_insert.py | 59 + xhiveframework/desk/__init__.py | 2 + xhiveframework/desk/calendar.py | 57 + xhiveframework/desk/desk_page.py | 33 + xhiveframework/desk/desktop.py | 668 ++ xhiveframework/desk/doctype/__init__.py | 0 .../desk/doctype/bulk_update/__init__.py | 0 .../desk/doctype/bulk_update/bulk_update.js | 58 + .../desk/doctype/bulk_update/bulk_update.json | 77 + .../desk/doctype/bulk_update/bulk_update.py | 118 + .../doctype/bulk_update/test_bulk_update.py | 48 + .../desk/doctype/calendar_view/__init__.py | 0 .../doctype/calendar_view/calendar_view.js | 36 + .../doctype/calendar_view/calendar_view.json | 83 + .../doctype/calendar_view/calendar_view.py | 22 + .../calendar_view/calendar_view_list.js | 16 + .../desk/doctype/console_log/__init__.py | 0 .../desk/doctype/console_log/console_log.js | 12 + .../desk/doctype/console_log/console_log.json | 62 + .../desk/doctype/console_log/console_log.py | 24 + .../doctype/console_log/test_console_log.py | 8 + .../doctype/custom_html_block/__init__.py | 0 .../custom_html_block/custom_html_block.js | 23 + .../custom_html_block/custom_html_block.json | 154 + .../custom_html_block/custom_html_block.py | 40 + .../test_custom_html_block.py | 9 + .../desk/doctype/dashboard/__init__.py | 0 .../desk/doctype/dashboard/dashboard.js | 30 + .../desk/doctype/dashboard/dashboard.json | 116 + .../desk/doctype/dashboard/dashboard.py | 137 + .../desk/doctype/dashboard/dashboard_list.js | 16 + .../desk/doctype/dashboard/test_dashboard.py | 7 + .../desk/doctype/dashboard_chart/__init__.py | 0 .../dashboard_chart/dashboard_chart.js | 557 ++ .../dashboard_chart/dashboard_chart.json | 336 + .../dashboard_chart/dashboard_chart.py | 436 ++ .../dashboard_chart/test_dashboard_chart.py | 282 + .../doctype/dashboard_chart_field/__init__.py | 0 .../dashboard_chart_field.json | 37 + .../dashboard_chart_field.py | 23 + .../doctype/dashboard_chart_link/__init__.py | 0 .../dashboard_chart_link.json | 41 + .../dashboard_chart_link.py | 23 + .../dashboard_chart_source/__init__.py | 0 .../dashboard_chart_source.js | 4 + .../dashboard_chart_source.json | 69 + .../dashboard_chart_source.py | 38 + .../test_dashboard_chart_source.py | 7 + .../doctype/dashboard_settings/__init__.py | 0 .../dashboard_settings/dashboard_settings.js | 7 + .../dashboard_settings.json | 53 + .../dashboard_settings/dashboard_settings.py | 58 + .../desk/doctype/desktop_icon/__init__.py | 0 .../desk/doctype/desktop_icon/desktop_icon.js | 6 + .../doctype/desktop_icon/desktop_icon.json | 175 + .../desk/doctype/desktop_icon/desktop_icon.py | 572 ++ xhiveframework/desk/doctype/event/README.md | 1 + xhiveframework/desk/doctype/event/__init__.py | 2 + xhiveframework/desk/doctype/event/event.js | 117 + xhiveframework/desk/doctype/event/event.json | 339 + xhiveframework/desk/doctype/event/event.py | 482 ++ .../desk/doctype/event/event_calendar.js | 16 + .../desk/doctype/event/event_list.js | 8 + .../desk/doctype/event/test_event.py | 138 + .../desk/doctype/event/test_records.json | 23 + .../doctype/event_participants/__init__.py | 0 .../event_participants.json | 49 + .../event_participants/event_participants.py | 22 + .../desk/doctype/form_tour/__init__.py | 0 .../desk/doctype/form_tour/form_tour.js | 255 + .../desk/doctype/form_tour/form_tour.json | 209 + .../desk/doctype/form_tour/form_tour.py | 113 + .../doctype/form_tour/patches/__init__.py | 0 .../form_tour/patches/introduce_ui_tours.py | 13 + .../desk/doctype/form_tour/test_form_tour.py | 9 + .../desk/doctype/form_tour_step/__init__.py | 0 .../form_tour_step/form_tour_step.json | 230 + .../doctype/form_tour_step/form_tour_step.py | 57 + .../doctype/global_search_doctype/__init__.py | 0 .../global_search_doctype.json | 29 + .../global_search_doctype.py | 22 + .../global_search_settings/__init__.py | 0 .../global_search_settings.js | 32 + .../global_search_settings.json | 39 + .../global_search_settings.py | 103 + .../desk/doctype/kanban_board/__init__.py | 0 .../desk/doctype/kanban_board/kanban_board.js | 45 + .../doctype/kanban_board/kanban_board.json | 124 + .../desk/doctype/kanban_board/kanban_board.py | 290 + .../doctype/kanban_board/test_kanban_board.py | 9 + .../doctype/kanban_board_column/__init__.py | 0 .../kanban_board_column.json | 55 + .../kanban_board_column.py | 36 + .../desk/doctype/list_filter/__init__.py | 0 .../desk/doctype/list_filter/list_filter.json | 62 + .../desk/doctype/list_filter/list_filter.py | 21 + .../doctype/list_view_settings/__init__.py | 0 .../list_view_settings/list_view_settings.js | 7 + .../list_view_settings.json | 85 + .../list_view_settings/list_view_settings.py | 99 + .../test_list_view_settings.py | 8 + .../doctype/module_onboarding/__init__.py | 0 .../module_onboarding/module_onboarding.js | 31 + .../module_onboarding/module_onboarding.json | 118 + .../module_onboarding/module_onboarding.py | 84 + .../test_module_onboarding.py | 8 + xhiveframework/desk/doctype/note/README.md | 1 + xhiveframework/desk/doctype/note/__init__.py | 0 xhiveframework/desk/doctype/note/note.js | 54 + xhiveframework/desk/doctype/note/note.json | 148 + xhiveframework/desk/doctype/note/note.py | 64 + xhiveframework/desk/doctype/note/note_list.js | 11 + xhiveframework/desk/doctype/note/test_note.py | 75 + .../desk/doctype/note/test_records.json | 7 + .../desk/doctype/note_seen_by/__init__.py | 0 .../doctype/note_seen_by/note_seen_by.json | 32 + .../desk/doctype/note_seen_by/note_seen_by.py | 21 + .../desk/doctype/notification_log/__init__.py | 0 .../notification_log/notification_log.js | 49 + .../notification_log/notification_log.json | 127 + .../notification_log/notification_log.py | 210 + .../notification_log/notification_log_list.js | 7 + .../notification_log/test_notification_log.py | 51 + .../doctype/notification_settings/__init__.py | 0 .../notification_settings.js | 22 + .../notification_settings.json | 146 + .../notification_settings.py | 138 + .../test_notification_settings.py | 9 + .../__init__.py | 0 .../notification_subscribed_document.json | 30 + .../notification_subscribed_document.py | 22 + .../desk/doctype/number_card/__init__.py | 0 .../desk/doctype/number_card/number_card.js | 451 ++ .../desk/doctype/number_card/number_card.json | 252 + .../desk/doctype/number_card/number_card.py | 260 + .../doctype/number_card/test_number_card.py | 8 + .../desk/doctype/number_card_link/__init__.py | 0 .../number_card_link/number_card_link.json | 31 + .../number_card_link/number_card_link.py | 22 + .../doctype/onboarding_permission/__init__.py | 0 .../onboarding_permission.js | 7 + .../onboarding_permission.json | 32 + .../onboarding_permission.py | 22 + .../test_onboarding_permission.py | 8 + .../desk/doctype/onboarding_step/__init__.py | 0 .../onboarding_step/onboarding_step.js | 86 + .../onboarding_step/onboarding_step.json | 255 + .../onboarding_step/onboarding_step.py | 65 + .../onboarding_step/test_onboarding_step.py | 8 + .../doctype/onboarding_step_map/__init__.py | 0 .../onboarding_step_map.json | 32 + .../onboarding_step_map.py | 22 + .../desk/doctype/route_history/__init__.py | 0 .../doctype/route_history/route_history.js | 6 + .../doctype/route_history/route_history.json | 54 + .../doctype/route_history/route_history.py | 54 + .../route_history/route_history_list.js | 7 + .../desk/doctype/system_console/__init__.py | 0 .../doctype/system_console/system_console.js | 115 + .../system_console/system_console.json | 109 + .../doctype/system_console/system_console.py | 73 + .../system_console/test_system_console.py | 36 + xhiveframework/desk/doctype/tag/__init__.py | 0 xhiveframework/desk/doctype/tag/tag.js | 7 + xhiveframework/desk/doctype/tag/tag.json | 50 + xhiveframework/desk/doctype/tag/tag.py | 205 + xhiveframework/desk/doctype/tag/test_tag.py | 34 + .../desk/doctype/tag_link/__init__.py | 0 .../desk/doctype/tag_link/tag_link.js | 7 + .../desk/doctype/tag_link/tag_link.json | 83 + .../desk/doctype/tag_link/tag_link.py | 22 + .../desk/doctype/tag_link/test_tag_link.py | 8 + xhiveframework/desk/doctype/todo/README.md | 1 + xhiveframework/desk/doctype/todo/__init__.py | 2 + xhiveframework/desk/doctype/todo/test_todo.py | 153 + xhiveframework/desk/doctype/todo/todo.js | 55 + xhiveframework/desk/doctype/todo/todo.json | 202 + xhiveframework/desk/doctype/todo/todo.py | 174 + .../desk/doctype/todo/todo_calendar.js | 29 + xhiveframework/desk/doctype/todo/todo_list.js | 29 + .../desk/doctype/workspace/__init__.py | 0 .../desk/doctype/workspace/test_workspace.py | 98 + .../desk/doctype/workspace/workspace.js | 59 + .../desk/doctype/workspace/workspace.json | 246 + .../desk/doctype/workspace/workspace.py | 475 ++ .../desk/doctype/workspace_chart/__init__.py | 0 .../workspace_chart/workspace_chart.json | 39 + .../workspace_chart/workspace_chart.py | 23 + .../workspace_custom_block/__init__.py | 0 .../workspace_custom_block.json | 39 + .../workspace_custom_block.py | 23 + .../desk/doctype/workspace_link/__init__.py | 0 .../workspace_link/workspace_link.json | 135 + .../doctype/workspace_link/workspace_link.py | 34 + .../doctype/workspace_number_card/__init__.py | 0 .../workspace_number_card.json | 40 + .../workspace_number_card.py | 23 + .../doctype/workspace_quick_list/__init__.py | 0 .../workspace_quick_list.json | 60 + .../workspace_quick_list.py | 24 + .../doctype/workspace_shortcut/__init__.py | 0 .../workspace_shortcut.json | 129 + .../workspace_shortcut/workspace_shortcut.py | 32 + xhiveframework/desk/form/__init__.py | 2 + xhiveframework/desk/form/assign_to.py | 286 + xhiveframework/desk/form/document_follow.py | 342 + xhiveframework/desk/form/linked_with.py | 671 ++ xhiveframework/desk/form/load.py | 489 ++ xhiveframework/desk/form/meta.py | 314 + xhiveframework/desk/form/save.py | 85 + xhiveframework/desk/form/test_form.py | 20 + xhiveframework/desk/form/utils.py | 108 + xhiveframework/desk/gantt.py | 17 + xhiveframework/desk/leaderboard.py | 50 + xhiveframework/desk/like.py | 91 + xhiveframework/desk/link_preview.py | 52 + xhiveframework/desk/listview.py | 73 + xhiveframework/desk/notifications.py | 406 ++ xhiveframework/desk/page/__init__.py | 0 xhiveframework/desk/page/backups/__init__.py | 0 xhiveframework/desk/page/backups/backups.css | 14 + xhiveframework/desk/page/backups/backups.html | 27 + xhiveframework/desk/page/backups/backups.js | 45 + xhiveframework/desk/page/backups/backups.json | 21 + xhiveframework/desk/page/backups/backups.py | 120 + .../desk/page/leaderboard/__init__.py | 0 .../desk/page/leaderboard/leaderboard.css | 85 + .../desk/page/leaderboard/leaderboard.js | 409 ++ .../desk/page/leaderboard/leaderboard.json | 19 + .../desk/page/leaderboard/leaderboard.py | 13 + .../desk/page/setup_wizard/__init__.py | 0 .../page/setup_wizard/install_fixtures.py | 64 + .../desk/page/setup_wizard/setup_wizard.js | 674 ++ .../desk/page/setup_wizard/setup_wizard.json | 23 + .../desk/page/setup_wizard/setup_wizard.py | 438 ++ .../desk/page/user_profile/__init__.py | 0 .../desk/page/user_profile/user_profile.css | 30 + .../desk/page/user_profile/user_profile.html | 44 + .../desk/page/user_profile/user_profile.js | 6 + .../desk/page/user_profile/user_profile.json | 23 + .../desk/page/user_profile/user_profile.py | 112 + .../user_profile/user_profile_controller.js | 494 ++ .../user_profile/user_profile_sidebar.html | 60 + xhiveframework/desk/query_report.py | 774 +++ xhiveframework/desk/report/__init__.py | 0 xhiveframework/desk/report/todo/__init__.py | 0 xhiveframework/desk/report/todo/todo.js | 6 + xhiveframework/desk/report/todo/todo.json | 23 + xhiveframework/desk/report/todo/todo.py | 69 + xhiveframework/desk/reportview.py | 717 ++ xhiveframework/desk/search.py | 333 + xhiveframework/desk/treeview.py | 84 + xhiveframework/desk/utils.py | 60 + xhiveframework/email/__init__.py | 121 + .../email/assets/images/email-pull-flow.png | Bin 0 -> 19057 bytes xhiveframework/email/doctype/__init__.py | 0 .../doctype/auto_email_report/__init__.py | 0 .../auto_email_report/auto_email_report.js | 180 + .../auto_email_report/auto_email_report.json | 258 + .../auto_email_report/auto_email_report.py | 381 ++ .../test_auto_email_report.py | 64 + .../email/doctype/document_follow/__init__.py | 0 .../document_follow/document_follow.js | 4 + .../document_follow/document_follow.json | 79 + .../document_follow/document_follow.py | 20 + .../document_follow/test_document_follow.py | 241 + .../email/doctype/email_account/__init__.py | 0 .../doctype/email_account/email_account.js | 232 + .../doctype/email_account/email_account.json | 642 ++ .../doctype/email_account/email_account.py | 952 +++ .../email_account/email_account_list.js | 24 + .../email_account/test_email_account.py | 648 ++ .../email_account/test_mails/incoming-1.raw | 91 + .../email_account/test_mails/incoming-2.raw | 511 ++ .../email_account/test_mails/incoming-3.raw | 183 + .../email_account/test_mails/incoming-4.raw | 138 + .../test_mails/incoming-self-sent.raw | 91 + .../incoming-subject-placeholder.raw | 183 + .../email_account/test_mails/reply-1.raw | 47 + .../email_account/test_mails/reply-2.raw | 45 + .../email_account/test_mails/reply-3.raw | 45 + .../email_account/test_mails/reply-4.raw | 75 + .../doctype/email_account/test_records.json | 29 + .../email/doctype/email_domain/__init__.py | 0 .../doctype/email_domain/email_domain.js | 22 + .../doctype/email_domain/email_domain.json | 157 + .../doctype/email_domain/email_domain.py | 124 + .../doctype/email_domain/test_email_domain.py | 39 + .../doctype/email_domain/test_records.json | 32 + .../doctype/email_flag_queue/__init__.py | 0 .../email_flag_queue/email_flag_queue.js | 6 + .../email_flag_queue/email_flag_queue.json | 67 + .../email_flag_queue/email_flag_queue.py | 22 + .../email_flag_queue/test_email_flag_queue.py | 9 + .../email/doctype/email_group/__init__.py | 0 .../email/doctype/email_group/email_group.js | 97 + .../doctype/email_group/email_group.json | 108 + .../email/doctype/email_group/email_group.py | 161 + .../doctype/email_group/test_email_group.py | 32 + .../doctype/email_group/test_records.json | 6 + .../doctype/email_group_member/__init__.py | 0 .../email_group_member/email_group_member.js | 6 + .../email_group_member.json | 71 + .../email_group_member/email_group_member.py | 32 + .../test_email_group_member.py | 9 + .../email/doctype/email_queue/__init__.py | 0 .../email/doctype/email_queue/email_queue.js | 34 + .../doctype/email_queue/email_queue.json | 177 + .../email/doctype/email_queue/email_queue.py | 803 +++ .../doctype/email_queue/email_queue_list.js | 60 + .../doctype/email_queue/patches/__init__.py | 0 .../drop_search_index_on_message_id.py | 11 + .../doctype/email_queue/test_email_queue.py | 95 + .../doctype/email_queue_recipient/__init__.py | 0 .../email_queue_recipient.json | 46 + .../email_queue_recipient.py | 40 + .../email/doctype/email_rule/__init__.py | 0 .../email/doctype/email_rule/email_rule.js | 6 + .../email/doctype/email_rule/email_rule.json | 49 + .../email/doctype/email_rule/email_rule.py | 19 + .../doctype/email_rule/test_email_rule.py | 7 + .../email/doctype/email_template/__init__.py | 0 .../doctype/email_template/email_template.js | 6 + .../email_template/email_template.json | 88 + .../doctype/email_template/email_template.py | 56 + .../email_template/test_email_template.py | 7 + .../doctype/email_unsubscribe/__init__.py | 0 .../email_unsubscribe/email_unsubscribe.js | 6 + .../email_unsubscribe/email_unsubscribe.json | 70 + .../email_unsubscribe/email_unsubscribe.py | 58 + .../test_email_unsubscribe.py | 9 + .../email/doctype/imap_folder/__init__.py | 0 .../doctype/imap_folder/imap_folder.json | 53 + .../email/doctype/imap_folder/imap_folder.py | 25 + .../email/doctype/newsletter/__init__.py | 0 .../email/doctype/newsletter/exceptions.py | 16 + .../email/doctype/newsletter/newsletter.js | 229 + .../email/doctype/newsletter/newsletter.json | 280 + .../email/doctype/newsletter/newsletter.py | 449 ++ .../doctype/newsletter/newsletter_list.js | 12 + .../newsletter/templates/newsletter.html | 65 + .../newsletter/templates/newsletter_row.html | 15 + .../doctype/newsletter/test_newsletter.py | 252 + .../doctype/newsletter_attachment/__init__.py | 0 .../newsletter_attachment.json | 31 + .../newsletter_attachment.py | 22 + .../newsletter_email_group/__init__.py | 0 .../newsletter_email_group.json | 42 + .../newsletter_email_group.py | 22 + .../email/doctype/notification/__init__.py | 0 .../doctype/notification/notification.js | 201 + .../doctype/notification/notification.json | 306 + .../doctype/notification/notification.py | 532 ++ .../doctype/notification/test_notification.py | 380 ++ .../doctype/notification/test_records.json | 76 + .../notification_recipient/__init__.py | 0 .../notification_recipient.json | 61 + .../notification_recipient.py | 25 + .../email/doctype/unhandled_email/__init__.py | 0 .../unhandled_email/test_unhandled_email.py | 9 + .../unhandled_email/unhandled_email.json | 60 + .../unhandled_email/unhandled_email.py | 31 + xhiveframework/email/email.md | 65 + xhiveframework/email/email_body.py | 591 ++ xhiveframework/email/inbox.py | 132 + xhiveframework/email/oauth.py | 74 + xhiveframework/email/page/__init__.py | 0 xhiveframework/email/queue.py | 179 + xhiveframework/email/receive.py | 898 +++ xhiveframework/email/smtp.py | 136 + xhiveframework/email/test_email_body.py | 207 + xhiveframework/email/test_smtp.py | 88 + xhiveframework/email/utils.py | 18 + xhiveframework/exceptions.py | 301 + xhiveframework/geo/__init__.py | 0 xhiveframework/geo/country_info.json | 3007 +++++++++ xhiveframework/geo/country_info.py | 80 + xhiveframework/geo/doctype/__init__.py | 0 xhiveframework/geo/doctype/country/README.md | 1 + .../geo/doctype/country/__init__.py | 0 xhiveframework/geo/doctype/country/country.js | 6 + .../geo/doctype/country/country.json | 89 + xhiveframework/geo/doctype/country/country.py | 77 + .../geo/doctype/country/test_country.py | 53 + .../geo/doctype/country/test_records.json | 6 + xhiveframework/geo/doctype/currency/README.md | 1 + .../geo/doctype/currency/__init__.py | 0 .../geo/doctype/currency/currency.js | 11 + .../geo/doctype/currency/currency.json | 126 + .../geo/doctype/currency/currency.py | 47 + .../geo/doctype/currency/test_currency.py | 13 + .../geo/doctype/currency/test_records.json | 1 + xhiveframework/geo/languages.json | 330 + xhiveframework/geo/report/__init__.py | 0 xhiveframework/geo/utils.py | 99 + xhiveframework/gettext/extractors/navbar.py | 51 + xhiveframework/handler.py | 349 + xhiveframework/hooks.py | 473 ++ xhiveframework/installer.py | 869 +++ xhiveframework/integrations/__init__.py | 0 .../integrations/doctype/__init__.py | 0 .../doctype/connected_app/__init__.py | 0 .../doctype/connected_app/connected_app.js | 38 + .../doctype/connected_app/connected_app.json | 169 + .../doctype/connected_app/connected_app.py | 193 + .../connected_app/test_connected_app.py | 148 + .../doctype/connected_app/test_records.json | 13 + .../doctype/dropbox_settings/__init__.py | 0 .../dropbox_settings/dropbox_settings.js | 47 + .../dropbox_settings/dropbox_settings.json | 126 + .../dropbox_settings/dropbox_settings.py | 378 ++ .../dropbox_settings/test_dropbox_settings.py | 8 + .../doctype/google_calendar/__init__.py | 0 .../google_calendar/google_calendar.js | 67 + .../google_calendar/google_calendar.json | 150 + .../google_calendar/google_calendar.py | 871 +++ .../doctype/google_contacts/__init__.py | 0 .../google_contacts/google_contacts.js | 63 + .../google_contacts/google_contacts.json | 132 + .../google_contacts/google_contacts.py | 309 + .../google_contacts/test_google_contacts.py | 9 + .../doctype/google_drive/__init__.py | 0 .../doctype/google_drive/google_drive.js | 71 + .../doctype/google_drive/google_drive.json | 126 + .../doctype/google_drive/google_drive.py | 231 + .../doctype/google_drive/test_google_drive.py | 8 + .../doctype/google_settings/__init__.py | 0 .../google_settings/google_settings.js | 14 + .../google_settings/google_settings.json | 100 + .../google_settings/google_settings.py | 39 + .../google_settings/test_google_settings.py | 42 + .../doctype/integration_request/__init__.py | 0 .../integration_request.js | 6 + .../integration_request.json | 154 + .../integration_request.py | 66 + .../integration_request_list.js | 7 + .../test_integration_request.py | 10 + .../doctype/ldap_group_mapping/__init__.py | 0 .../ldap_group_mapping.json | 41 + .../ldap_group_mapping/ldap_group_mapping.py | 23 + .../doctype/ldap_settings/__init__.py | 0 .../doctype/ldap_settings/ldap_settings.js | 6 + .../doctype/ldap_settings/ldap_settings.json | 327 + .../doctype/ldap_settings/ldap_settings.py | 426 ++ .../test_data_ldif_activedirectory.json | 338 + .../test_data_ldif_openldap.json | 400 ++ .../ldap_settings/test_ldap_settings.py | 656 ++ .../oauth_authorization_code/__init__.py | 0 .../oauth_authorization_code.js | 6 + .../oauth_authorization_code.json | 112 + .../oauth_authorization_code.py | 28 + .../test_oauth_authorization_code.py | 10 + .../doctype/oauth_bearer_token/__init__.py | 0 .../oauth_bearer_token/oauth_bearer_token.js | 6 + .../oauth_bearer_token.json | 99 + .../oauth_bearer_token/oauth_bearer_token.py | 31 + .../test_oauth_bearer_token.py | 10 + .../doctype/oauth_client/__init__.py | 0 .../doctype/oauth_client/oauth_client.js | 6 + .../doctype/oauth_client/oauth_client.json | 144 + .../doctype/oauth_client/oauth_client.py | 47 + .../doctype/oauth_client/test_oauth_client.py | 10 + .../oauth_provider_settings/__init__.py | 0 .../oauth_provider_settings.js | 6 + .../oauth_provider_settings.json | 43 + .../oauth_provider_settings.py | 27 + .../doctype/oauth_scope/__init__.py | 0 .../doctype/oauth_scope/oauth_scope.json | 30 + .../doctype/oauth_scope/oauth_scope.py | 22 + .../push_notification_settings/__init__.py | 0 .../push_notification_settings.js | 8 + .../push_notification_settings.json | 69 + .../push_notification_settings.py | 31 + .../test_push_notification_settings.py | 9 + .../doctype/query_parameters/__init__.py | 0 .../query_parameters/query_parameters.json | 37 + .../query_parameters/query_parameters.py | 23 + .../doctype/s3_backup_settings/__init__.py | 0 .../s3_backup_settings/s3_backup_settings.js | 26 + .../s3_backup_settings.json | 155 + .../s3_backup_settings/s3_backup_settings.py | 196 + .../test_s3_backup_settings.py | 7 + .../doctype/slack_webhook_url/__init__.py | 0 .../slack_webhook_url/slack_webhook_url.js | 4 + .../slack_webhook_url/slack_webhook_url.json | 61 + .../slack_webhook_url/slack_webhook_url.py | 67 + .../test_slack_webhook_url.py | 7 + .../doctype/social_login_key/__init__.py | 0 .../social_login_key/social_login_key.js | 84 + .../social_login_key/social_login_key.json | 203 + .../social_login_key/social_login_key.py | 226 + .../social_login_key/test_social_login_key.py | 172 + .../doctype/social_login_keys/__init__.py | 0 .../social_login_keys/social_login_keys.py | 6 + .../doctype/token_cache/__init__.py | 0 .../doctype/token_cache/test_records.json | 18 + .../doctype/token_cache/test_token_cache.py | 36 + .../doctype/token_cache/token_cache.js | 7 + .../doctype/token_cache/token_cache.json | 111 + .../doctype/token_cache/token_cache.py | 90 + .../integrations/doctype/webhook/__init__.py | 108 + .../doctype/webhook/test_webhook.py | 311 + .../integrations/doctype/webhook/webhook.js | 129 + .../integrations/doctype/webhook/webhook.json | 253 + .../integrations/doctype/webhook/webhook.py | 264 + .../doctype/webhook_data/__init__.py | 0 .../doctype/webhook_data/webhook_data.json | 43 + .../doctype/webhook_data/webhook_data.py | 23 + .../doctype/webhook_header/__init__.py | 0 .../webhook_header/webhook_header.json | 38 + .../doctype/webhook_header/webhook_header.py | 23 + .../doctype/webhook_request_log/__init__.py | 0 .../test_webhook_request_log.py | 9 + .../webhook_request_log.js | 7 + .../webhook_request_log.json | 105 + .../webhook_request_log.py | 33 + .../webhook_request_log_list.js | 7 + xhiveframework/integrations/google_oauth.py | 195 + xhiveframework/integrations/oauth2.py | 244 + xhiveframework/integrations/oauth2_logins.py | 63 + .../integrations/offsite_backup_utils.py | 119 + xhiveframework/integrations/utils.py | 111 + .../workspace/integrations/integrations.json | 235 + .../xhiveframework_providers/__init__.py | 13 + .../xhiveframeworkcloud.py | 32 + xhiveframework/middlewares.py | 28 + xhiveframework/migrate.py | 190 + xhiveframework/model/__init__.py | 254 + xhiveframework/model/base_document.py | 1308 ++++ xhiveframework/model/create_new.py | 182 + xhiveframework/model/db_query.py | 1380 ++++ xhiveframework/model/delete_doc.py | 463 ++ xhiveframework/model/docfield.py | 10 + xhiveframework/model/docstatus.py | 25 + xhiveframework/model/document.py | 1736 +++++ xhiveframework/model/dynamic_links.py | 63 + xhiveframework/model/mapper.py | 267 + xhiveframework/model/meta.py | 895 +++ xhiveframework/model/naming.py | 544 ++ xhiveframework/model/rename_doc.py | 693 ++ xhiveframework/model/sync.py | 176 + xhiveframework/model/utils/__init__.py | 143 + xhiveframework/model/utils/link_count.py | 78 + xhiveframework/model/utils/rename_doc.py | 63 + xhiveframework/model/utils/rename_field.py | 176 + xhiveframework/model/utils/user_settings.py | 105 + xhiveframework/model/virtual_doctype.py | 93 + xhiveframework/model/workflow.py | 369 + xhiveframework/modules.txt | 12 + xhiveframework/modules/__init__.py | 1 + xhiveframework/modules/export_file.py | 160 + xhiveframework/modules/import_file.py | 287 + xhiveframework/modules/patch_handler.py | 234 + xhiveframework/modules/utils.py | 335 + xhiveframework/monitor.py | 135 + xhiveframework/oauth.py | 592 ++ xhiveframework/parallel_test_runner.py | 313 + xhiveframework/patches.txt | 236 + xhiveframework/patches/__init__.py | 0 xhiveframework/patches/v10_0/__init__.py | 0 ..._chat_by_default_within_system_settings.py | 13 + .../patches/v10_0/enhance_security.py | 32 + .../increase_single_table_column_length.py | 9 + .../v10_0/modify_naming_series_table.py | 10 + .../modify_smallest_currency_fraction.py | 8 + .../v10_0/refactor_social_login_keys.py | 153 + .../v10_0/reload_countries_and_currencies.py | 8 + ...remove_custom_field_for_disabled_domain.py | 14 + .../patches/v10_0/set_default_locking_time.py | 9 + .../v10_0/set_no_copy_to_workflow_state.py | 13 + xhiveframework/patches/v11_0/__init__.py | 0 .../apply_customization_to_custom_doctype.py | 52 + .../v11_0/change_email_signature_fieldtype.py | 14 + .../v11_0/copy_fetch_data_from_options.py | 38 + .../patches/v11_0/create_contact_for_user.py | 28 + .../v11_0/delete_all_prepared_reports.py | 9 + .../delete_duplicate_user_permissions.py | 18 + .../drop_column_apply_user_permissions.py | 14 + .../v11_0/fix_order_by_in_reports_json.py | 35 + ...all_prepared_report_attachments_private.py | 28 + ...igrate_report_settings_for_new_listview.py | 34 + .../v11_0/multiple_references_in_events.py | 24 + .../v11_0/reload_and_rename_view_log.py | 28 + ...pe_user_permissions_for_page_and_report.py | 8 + .../patches/v11_0/remove_skip_for_doctype.py | 89 + .../rename_email_alert_to_notification.py | 14 + ...rename_standard_reply_to_email_template.py | 8 + ...rkflow_action_to_workflow_action_master.py | 8 + .../v11_0/replicate_old_user_permissions.py | 97 + .../set_allow_self_approval_in_workflow.py | 6 + .../v11_0/set_default_letter_head_source.py | 8 + .../patches/v11_0/set_dropbox_file_backup.py | 9 + ...and_modified_value_for_user_permissions.py | 9 + .../v11_0/update_list_user_settings.py | 33 + xhiveframework/patches/v12_0/__init__.py | 0 ...change_existing_dashboard_chart_filters.py | 31 + .../create_notification_settings_for_user.py | 13 + .../patches/v12_0/delete_duplicate_indexes.py | 55 + .../delete_feedback_request_if_exists.py | 5 + .../patches/v12_0/fix_email_id_formatting.py | 56 + .../patches/v12_0/fix_public_private_files.py | 34 + .../move_email_and_phone_to_child_table.py | 105 + ..._form_attachments_to_attachments_folder.py | 12 + .../move_timeline_links_to_dynamic_links.py | 64 + .../remove_deprecated_fields_from_doctype.py | 10 + .../remove_example_email_thread_notify.py | 10 + .../patches/v12_0/remove_feedback_rating.py | 10 + .../patches/v12_0/rename_events_repeat_on.py | 37 + .../rename_uploaded_files_with_proper_name.py | 33 + .../v12_0/replace_null_values_in_tables.py | 30 + .../patches/v12_0/reset_home_settings.py | 12 + .../v12_0/set_correct_assign_value_in_docs.py | 28 + .../patches/v12_0/set_correct_url_in_files.py | 41 + .../v12_0/set_default_incoming_email_port.py | 43 + .../v12_0/set_default_password_reset_limit.py | 9 + .../v12_0/set_primary_key_in_series.py | 26 + .../setup_comments_from_communications.py | 31 + .../patches/v12_0/setup_email_linking.py | 5 + xhiveframework/patches/v12_0/setup_tags.py | 47 + ..._auto_repeat_status_and_not_submittable.py | 34 + .../patches/v12_0/update_global_search.py | 8 + .../patches/v12_0/update_print_format_type.py | 18 + xhiveframework/patches/v13_0/__init__.py | 0 .../v13_0/add_standard_navbar_items.py | 9 + .../add_switch_theme_to_navbar_settings.py | 24 + .../add_toggle_width_in_navbar_settings.py | 24 + ...eate_custom_dashboards_cards_and_charts.py | 46 + .../v13_0/delete_package_publish_tool.py | 10 + .../patches/v13_0/email_unsubscribe.py | 14 + .../patches/v13_0/enable_custom_script.py | 14 + .../patches/v13_0/encrypt_2fa_secrets.py | 45 + .../generate_theme_files_in_public_folder.py | 16 + .../patches/v13_0/increase_password_length.py | 5 + xhiveframework/patches/v13_0/jinja_hook.py | 17 + .../patches/v13_0/make_user_type.py | 12 + .../v13_0/migrate_translation_column_data.py | 8 + .../patches/v13_0/queryreport_columns.py | 18 + xhiveframework/patches/v13_0/remove_chat.py | 19 + .../patches/v13_0/remove_custom_link.py | 18 + .../v13_0/remove_duplicate_navbar_items.py | 14 + .../remove_invalid_options_for_data_fields.py | 15 + .../v13_0/remove_share_for_std_users.py | 7 + .../remove_tailwind_from_page_builder.py | 11 + .../patches/v13_0/remove_twilio_settings.py | 20 + .../patches/v13_0/remove_web_view.py | 7 + .../v13_0/rename_custom_client_script.py | 13 + .../v13_0/rename_desk_page_to_workspace.py | 22 + ...name_is_custom_field_in_dashboard_chart.py | 12 + ...list_view_setting_to_list_view_settings.py | 29 + .../v13_0/rename_notification_fields.py | 16 + .../patches/v13_0/rename_onboarding.py | 9 + ...place_field_target_with_open_in_new_tab.py | 10 + .../patches/v13_0/replace_old_data_import.py | 20 + .../patches/v13_0/reset_corrupt_defaults.py | 33 + ...set_existing_dashboard_charts_as_public.py | 21 + .../v13_0/set_first_day_of_the_week.py | 8 + .../set_path_for_homepage_in_web_page_view.py | 6 + .../patches/v13_0/set_read_times.py | 21 + .../v13_0/set_route_for_blog_category.py | 9 + .../patches/v13_0/set_social_icons.py | 10 + .../patches/v13_0/set_unique_for_page_view.py | 7 + .../patches/v13_0/site_wise_logging.py | 11 + .../update_date_filters_in_user_settings.py | 54 + .../patches/v13_0/update_duration_options.py | 32 + .../update_icons_in_customized_desk_pages.py | 18 + .../v13_0/update_newsletter_content_type.py | 14 + .../update_notification_channel_if_empty.py | 14 + .../patches/v13_0/web_template_set_module.py | 17 + .../v13_0/website_theme_custom_scss.py | 38 + xhiveframework/patches/v14_0/__init__.py | 0 .../v14_0/clear_long_pending_stale_logs.py | 40 + .../patches/v14_0/copy_mail_data.py | 23 + .../v14_0/delete_data_migration_tool.py | 12 + .../patches/v14_0/delete_payment_gateways.py | 16 + .../patches/v14_0/different_encryption_key.py | 16 + .../disable_email_accounts_with_oauth.py | 36 + .../patches/v14_0/drop_data_import_legacy.py | 23 + .../patches/v14_0/drop_unused_indexes.py | 56 + .../patches/v14_0/log_settings_migration.py | 29 + .../modify_value_column_size_for_singles.py | 6 + .../patches/v14_0/remove_db_aggregation.py | 35 + .../patches/v14_0/remove_is_first_startup.py | 8 + ...remove_manage_subscriptions_from_navbar.py | 10 + .../v14_0/remove_post_and_post_comment.py | 6 + .../patches/v14_0/reset_creation_datetime.py | 38 + .../patches/v14_0/save_ratings_in_fraction.py | 37 + .../v14_0/set_document_expiry_default.py | 8 + .../v14_0/set_suspend_email_queue_default.py | 13 + .../v14_0/setup_likes_from_feedback.py | 32 + .../patches/v14_0/transform_todo_schema.py | 12 + .../v14_0/update_attachment_comment.py | 33 + .../update_auto_account_deletion_duration.py | 6 + ...date_color_names_in_kanban_board_column.py | 22 + .../patches/v14_0/update_github_endpoints.py | 10 + .../v14_0/update_integration_request.py | 21 + .../v14_0/update_is_system_generated_flag.py | 20 + .../v14_0/update_multistep_webforms.py | 12 + .../patches/v14_0/update_webforms.py | 14 + .../patches/v14_0/update_workspace2.py | 85 + ...able_prepared_report_to_prepared_report.py | 6 + .../patches/v15_0/drop_modified_index.py | 21 + .../v15_0/move_event_cancelled_to_status.py | 12 + .../remove_background_jobs_from_dropdown.py | 9 + .../patches/v15_0/remove_event_streaming.py | 22 + .../v15_0/remove_implicit_primary_key.py | 50 + ...ed_report_settings_from_system_settings.py | 9 + .../v15_0/sanitize_workspace_titles.py | 25 + .../patches/v15_0/set_contact_full_name.py | 31 + xhiveframework/patches/v15_0/set_file_type.py | 32 + .../v15_0/validate_newsletter_recipients.py | 9 + xhiveframework/permissions.py | 842 +++ xhiveframework/printing/__init__.py | 0 xhiveframework/printing/doctype/__init__.py | 0 .../printing/doctype/letter_head/__init__.py | 0 .../doctype/letter_head/letter_head.js | 61 + .../doctype/letter_head/letter_head.json | 229 + .../doctype/letter_head/letter_head.py | 123 + .../doctype/letter_head/test_letter_head.py | 14 + .../network_printer_settings/__init__.py | 0 .../network_printer_settings.js | 29 + .../network_printer_settings.json | 77 + .../network_printer_settings.py | 54 + .../test_network_printer_settings.py | 9 + .../printing/doctype/print_format/__init__.py | 0 .../doctype/print_format/print_format.js | 88 + .../doctype/print_format/print_format.json | 290 + .../doctype/print_format/print_format.py | 166 + .../doctype/print_format/test_print_format.py | 65 + .../doctype/print_format/test_records.json | 9 + .../print_format_field_template/__init__.py | 0 .../print_format_field_template.js | 7 + .../print_format_field_template.json | 101 + .../print_format_field_template.py | 59 + .../test_print_format_field_template.py | 9 + .../doctype/print_heading/__init__.py | 0 .../doctype/print_heading/print_heading.js | 6 + .../doctype/print_heading/print_heading.json | 143 + .../doctype/print_heading/print_heading.py | 20 + .../print_heading/test_print_heading.py | 8 + .../doctype/print_settings/__init__.py | 0 .../doctype/print_settings/print_settings.js | 23 + .../print_settings/print_settings.json | 198 + .../doctype/print_settings/print_settings.py | 83 + .../print_settings/test_print_settings.py | 7 + .../printing/doctype/print_style/__init__.py | 0 .../doctype/print_style/print_style.js | 10 + .../doctype/print_style/print_style.json | 75 + .../doctype/print_style/print_style.py | 39 + .../doctype/print_style/test_print_style.py | 8 + .../form_tour/letter_head/letter_head.json | 53 + .../form_tour/print_format/print_format.json | 94 + xhiveframework/printing/page/__init__.py | 0 .../printing/page/print/__init__.py | 0 xhiveframework/printing/page/print/print.js | 846 +++ xhiveframework/printing/page/print/print.json | 18 + xhiveframework/printing/page/print/print.py | 22 + .../page/print/print_skeleton_loading.html | 164 + .../page/print_format_builder/__init__.py | 0 .../print_format_builder.css | 161 + .../print_format_builder.js | 849 +++ .../print_format_builder.json | 23 + .../print_format_builder.py | 19 + .../print_format_builder_column_selector.html | 36 + .../print_format_builder_field.html | 46 + .../print_format_builder_layout.html | 30 + .../print_format_builder_section.html | 23 + .../print_format_builder_sidebar.html | 21 + .../print_format_builder_start.html | 18 + .../print_format_builder_beta/__init__.py | 0 .../print_format_builder_beta.css | 3 + .../print_format_builder_beta.js | 110 + .../print_format_builder_beta.json | 22 + .../print_format_builder_beta.py | 18 + .../printing/print_style/__init__.py | 0 .../printing/print_style/classic/__init__.py | 0 .../printing/print_style/classic/classic.json | 15 + .../printing/print_style/modern/__init__.py | 0 .../printing/print_style/modern/modern.json | 15 + .../print_style/monochrome/__init__.py | 0 .../print_style/monochrome/monochrome.json | 15 + .../printing/print_style/redesign/__init__.py | 0 .../print_style/redesign/redesign.json | 14 + xhiveframework/public/css/bootstrap.css | 5819 ++++++++++++++++ .../css/fonts/fontawesome/FontAwesome.otf | Bin 0 -> 134808 bytes .../public/css/fonts/fontawesome/LICENSE | 4 + .../fonts/fontawesome/font-awesome.min.css | 4 + .../fonts/fontawesome/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../fonts/fontawesome/fontawesome-webfont.svg | 2671 ++++++++ .../fonts/fontawesome/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fontawesome/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fontawesome/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../public/css/fonts/inter/Inter-Black.woff2 | Bin 0 -> 108748 bytes .../css/fonts/inter/Inter-BlackItalic.woff2 | Bin 0 -> 115364 bytes .../public/css/fonts/inter/Inter-Bold.woff2 | Bin 0 -> 111040 bytes .../css/fonts/inter/Inter-BoldItalic.woff2 | Bin 0 -> 118392 bytes .../css/fonts/inter/Inter-ExtraBold.woff2 | Bin 0 -> 111360 bytes .../fonts/inter/Inter-ExtraBoldItalic.woff2 | Bin 0 -> 118604 bytes .../css/fonts/inter/Inter-ExtraLight.woff2 | Bin 0 -> 110176 bytes .../fonts/inter/Inter-ExtraLightItalic.woff2 | Bin 0 -> 116296 bytes .../public/css/fonts/inter/Inter-Italic.woff2 | Bin 0 -> 114576 bytes .../public/css/fonts/inter/Inter-Light.woff2 | Bin 0 -> 109992 bytes .../css/fonts/inter/Inter-LightItalic.woff2 | Bin 0 -> 116516 bytes .../public/css/fonts/inter/Inter-Medium.woff2 | Bin 0 -> 111380 bytes .../css/fonts/inter/Inter-MediumItalic.woff2 | Bin 0 -> 118392 bytes .../css/fonts/inter/Inter-Regular.woff2 | Bin 0 -> 108488 bytes .../css/fonts/inter/Inter-SemiBold.woff2 | Bin 0 -> 111588 bytes .../fonts/inter/Inter-SemiBoldItalic.woff2 | Bin 0 -> 118216 bytes .../public/css/fonts/inter/Inter-Thin.woff2 | Bin 0 -> 106620 bytes .../css/fonts/inter/Inter-ThinItalic.woff2 | Bin 0 -> 113384 bytes .../fonts/inter/InterVariable-Italic.woff2 | Bin 0 -> 380904 bytes .../css/fonts/inter/InterVariable.woff2 | Bin 0 -> 345588 bytes .../public/css/fonts/inter/inter.css | 33 + .../public/css/fonts/inter/inter.scss | 1 + xhiveframework/public/css/hljs-night-owl.css | 183 + .../public/css/octicons/LICENSE.txt | 9 + xhiveframework/public/css/octicons/README.md | 1 + .../public/css/octicons/octicons-local.ttf | Bin 0 -> 50856 bytes .../public/css/octicons/octicons.css | 221 + .../public/css/octicons/octicons.eot | Bin 0 -> 29160 bytes .../public/css/octicons/octicons.less | 220 + .../public/css/octicons/octicons.scss | 548 ++ .../public/css/octicons/octicons.svg | 183 + .../public/css/octicons/octicons.ttf | Bin 0 -> 28992 bytes .../public/css/octicons/octicons.woff | Bin 0 -> 16060 bytes .../css/octicons/sprockets-octicons.scss | 543 ++ xhiveframework/public/css/tree.css | 95 + xhiveframework/public/css/tree_grid.css | 15 + .../public/html/print_template.html | 46 + .../public/icons/espresso/icons.svg | 1693 +++++ .../public/icons/social/facebook.svg | 3 + xhiveframework/public/icons/social/fair.svg | 14 + xhiveframework/public/icons/social/github.svg | 3 + xhiveframework/public/icons/social/google.svg | 6 + .../public/icons/social/google_drive.svg | 1 + .../public/icons/social/office_365.svg | 53 + .../public/icons/social/salesforce.svg | 3 + .../public/icons/social/xhiveframework.svg | 23 + .../public/icons/timeless/icon-check.svg | 3 + .../public/icons/timeless/icons.svg | 1009 +++ .../public/icons/timeless/message.svg | 3 + .../public/icons/timeless/search.svg | 3 + xhiveframework/public/icons/timeless/test.svg | 5 + xhiveframework/public/images/background.png | Bin 0 -> 12460 bytes xhiveframework/public/images/color-circle.png | Bin 0 -> 3783 bytes .../public/images/default-avatar.png | Bin 0 -> 45267 bytes xhiveframework/public/images/default-skin.svg | 1 + .../public/images/fallback-thumbnail.jpg | Bin 0 -> 2598 bytes xhiveframework/public/images/form-builder.gif | Bin 0 -> 489380 bytes .../images/help/print-style-classic.png | Bin 0 -> 97352 bytes .../public/images/help/print-style-modern.png | Bin 0 -> 101181 bytes .../images/help/print-style-monochrome.png | Bin 0 -> 100658 bytes .../images/help/print-style-standard.png | Bin 0 -> 100605 bytes .../public/images/leaflet/layers-2x.png | Bin 0 -> 1259 bytes .../public/images/leaflet/layers.png | Bin 0 -> 696 bytes .../images/leaflet/leafletmarker-icon.png | Bin 0 -> 1466 bytes .../images/leaflet/leafletmarker-shadow.png | Bin 0 -> 618 bytes xhiveframework/public/images/leaflet/lego.png | Bin 0 -> 226340 bytes .../public/images/leaflet/marker-icon-2x.png | Bin 0 -> 2586 bytes .../public/images/leaflet/marker-icon.png | Bin 0 -> 1466 bytes .../public/images/leaflet/marker-shadow.png | Bin 0 -> 618 bytes .../public/images/leaflet/spritesheet-2x.png | Bin 0 -> 3581 bytes .../public/images/leaflet/spritesheet.png | Bin 0 -> 1906 bytes .../public/images/leaflet/spritesheet.svg | 156 + .../public/images/signature-placeholder.png | Bin 0 -> 3124 bytes xhiveframework/public/images/smiley.png | Bin 0 -> 390 bytes .../public/images/ui-states/404.png | Bin 0 -> 96089 bytes .../images/ui-states/empty-app-state.svg | 6 + .../public/images/ui-states/empty.png | Bin 0 -> 50732 bytes .../images/ui-states/event-empty-state.svg | 9 + .../images/ui-states/grid-empty-state.svg | 9 + .../images/ui-states/list-empty-state.svg | 10 + .../ui-states/notification-empty-state.svg | 9 + .../images/ui-states/search-empty-state.svg | 9 + .../public/images/ui-states/success-color.png | Bin 0 -> 64316 bytes .../public/images/ui/ajax-loader.gif | Bin 0 -> 1737 bytes xhiveframework/public/images/ui/bot.png | Bin 0 -> 14886 bytes .../public/images/ui/bubble-tea-happy.svg | 11 + .../public/images/ui/bubble-tea-smile.svg | 8 + .../public/images/ui/bubble-tea-sorry.svg | 12 + xhiveframework/public/images/up.png | Bin 0 -> 235 bytes .../public/images/workflow-builder.gif | Bin 0 -> 785377 bytes .../public/images/xhiveframework-favicon.svg | 23 + .../images/xhiveframework-framework-logo.png | Bin 0 -> 8589 bytes .../images/xhiveframework-framework-logo.svg | 23 + .../public/images/xhiveframework-logo.png | Bin 0 -> 8589 bytes .../public/js/bootstrap-4-web.bundle.js | 68 + xhiveframework/public/js/controls.bundle.js | 4 + .../public/js/data_import_tools.bundle.js | 1 + xhiveframework/public/js/desk.bundle.js | 110 + xhiveframework/public/js/dialog.bundle.js | 7 + xhiveframework/public/js/form.bundle.js | 16 + .../public/js/form_builder/FormBuilder.vue | 282 + .../components/AddFieldButton.vue | 139 + .../form_builder/components/Autocomplete.vue | 160 + .../js/form_builder/components/Column.vue | 185 + .../js/form_builder/components/Dropdown.vue | 129 + .../form_builder/components/EditableInput.vue | 89 + .../js/form_builder/components/Field.vue | 214 + .../components/FieldProperties.vue | 126 + .../js/form_builder/components/SearchBox.vue | 34 + .../js/form_builder/components/Section.vue | 390 ++ .../js/form_builder/components/Sidebar.vue | 142 + .../js/form_builder/components/Tabs.vue | 337 + .../components/controls/AttachControl.vue | 20 + .../components/controls/ButtonControl.vue | 28 + .../components/controls/CheckControl.vue | 51 + .../components/controls/CodeControl.vue | 70 + .../components/controls/DataControl.vue | 96 + .../components/controls/FetchFromControl.vue | 100 + .../controls/GeolocationControl.vue | 40 + .../components/controls/ImageControl.vue | 14 + .../components/controls/LinkControl.vue | 90 + .../components/controls/RatingControl.vue | 50 + .../components/controls/SelectControl.vue | 131 + .../components/controls/SignatureControl.vue | 31 + .../components/controls/TableControl.vue | 88 + .../components/controls/TextControl.vue | 47 + .../components/controls/TextEditorControl.vue | 49 + .../js/form_builder/form_builder.bundle.js | 92 + .../public/js/form_builder/globals.js | 58 + .../public/js/form_builder/store.js | 371 ++ .../public/js/form_builder/utils.js | 353 + .../js/integrations/google_drive_picker.js | 85 + xhiveframework/public/js/jquery-bootstrap.js | 31 + .../public/js/lib/fullcalendar/LICENCE.txt | 20 + .../js/lib/fullcalendar/fullcalendar.min.css | 5 + .../js/lib/fullcalendar/fullcalendar.min.js | 12 + .../public/js/lib/fullcalendar/locale-all.js | 6 + .../public/js/lib/jSignature.min.js | 1462 ++++ .../public/js/lib/jquery/LICENCE.txt | 36 + .../public/js/lib/jquery/jquery.min.js | 4 + xhiveframework/public/js/lib/leaflet/LICENSE | 23 + .../public/js/lib/leaflet/leaflet.css | 634 ++ .../public/js/lib/leaflet/leaflet.js | 5 + .../L.Control.Locate.css | 12 + .../L.Control.Locate.js | 591 ++ .../js/lib/leaflet_control_locate/LICENSE | 21 + .../public/js/lib/leaflet_draw/MIT-LICENSE.md | 20 + .../js/lib/leaflet_draw/leaflet.draw.css | 10 + .../js/lib/leaflet_draw/leaflet.draw.js | 1702 +++++ .../public/js/lib/leaflet_easy_button/LICENSE | 7 + .../lib/leaflet_easy_button/easy-button.css | 56 + .../js/lib/leaflet_easy_button/easy-button.js | 370 + xhiveframework/public/js/lib/moment.js | 5 + .../public/js/lib/photoswipe/LICENCE | 21 + .../public/js/lib/photoswipe/default-skin.css | 483 ++ .../lib/photoswipe/photoswipe-ui-default.js | 861 +++ .../public/js/lib/photoswipe/photoswipe.css | 178 + .../public/js/lib/photoswipe/photoswipe.js | 3718 +++++++++++ xhiveframework/public/js/lib/posthog.js | 1 + xhiveframework/public/js/libs.bundle.js | 13 + xhiveframework/public/js/list.bundle.js | 42 + xhiveframework/public/js/logtypes.bundle.js | 1 + .../public/js/onboarding_tours.bundle.js | 1 + .../js/onboarding_tours/onboarding_tours.js | 356 + .../print_format_builder/ConfigureColumns.vue | 101 + .../public/js/print_format_builder/Field.vue | 331 + .../js/print_format_builder/HTMLEditor.vue | 70 + .../print_format_builder/LetterHeadEditor.vue | 295 + .../js/print_format_builder/Preview.vue | 134 + .../js/print_format_builder/PrintFormat.vue | 132 + .../PrintFormatBuilder.vue | 67 + .../PrintFormatControls.vue | 314 + .../PrintFormatSection.vue | 224 + .../print_format_builder.bundle.js | 62 + .../public/js/print_format_builder/store.js | 159 + .../public/js/print_format_builder/utils.js | 152 + xhiveframework/public/js/report.bundle.js | 8 + xhiveframework/public/js/sentry.bundle.js | 23 + xhiveframework/public/js/telemetry.bundle.js | 1 + xhiveframework/public/js/telemetry/index.js | 86 + .../js/user_profile_controller.bundle.js | 1 + .../public/js/video_player.bundle.js | 3 + xhiveframework/public/js/web_form.bundle.js | 7 + .../js/workflow_builder/WorkflowBuilder.vue | 369 + .../components/ActionNode.vue | 83 + .../components/ConnectionLine.vue | 34 + .../components/Properties.vue | 97 + .../workflow_builder/components/Sidebar.vue | 59 + .../workflow_builder/components/StateNode.vue | 90 + .../components/TransitionEdge.vue | 110 + .../public/js/workflow_builder/globals.js | 56 + .../public/js/workflow_builder/store.js | 228 + .../public/js/workflow_builder/utils.js | 189 + .../workflow_builder.bundle.js | 69 + .../public/js/xhiveframework-web.bundle.js | 26 + .../public/js/xhiveframework/assets.js | 213 + .../build_events/BuildError.vue | 107 + .../build_events/BuildSuccess.vue | 56 + .../build_events/build_events.bundle.js | 66 + .../public/js/xhiveframework/change_log.html | 18 + .../public/js/xhiveframework/class.js | 92 + .../color_picker/color_picker.js | 203 + .../js/xhiveframework/color_picker/utils.js | 115 + .../data_import/data_exporter.js | 353 + .../data_import/import_preview.js | 352 + .../js/xhiveframework/data_import/index.js | 2 + xhiveframework/public/js/xhiveframework/db.js | 134 + .../public/js/xhiveframework/defaults.js | 140 + .../public/js/xhiveframework/desk.js | 554 ++ .../public/js/xhiveframework/doctype/index.js | 224 + .../public/js/xhiveframework/dom.js | 421 ++ .../public/js/xhiveframework/event_emitter.js | 36 + .../file_uploader/FileBrowser.vue | 186 + .../file_uploader/FilePreview.vue | 238 + .../file_uploader/FileUploader.vue | 749 +++ .../file_uploader/ImageCropper.vue | 144 + .../file_uploader/ProgressRing.vue | 71 + .../xhiveframework/file_uploader/TreeNode.vue | 137 + .../xhiveframework/file_uploader/WebLink.vue | 32 + .../file_uploader/file_uploader.bundle.js | 144 + .../public/js/xhiveframework/form/column.js | 60 + .../js/xhiveframework/form/controls/attach.js | 133 + .../form/controls/attach_image.js | 24 + .../form/controls/autocomplete.js | 266 + .../xhiveframework/form/controls/barcode.js | 79 + .../form/controls/base_control.js | 292 + .../form/controls/base_input.js | 238 + .../js/xhiveframework/form/controls/button.js | 69 + .../js/xhiveframework/form/controls/check.js | 44 + .../js/xhiveframework/form/controls/code.js | 222 + .../js/xhiveframework/form/controls/color.js | 117 + .../xhiveframework/form/controls/comment.js | 107 + .../xhiveframework/form/controls/control.js | 52 + .../xhiveframework/form/controls/currency.js | 20 + .../js/xhiveframework/form/controls/data.js | 302 + .../js/xhiveframework/form/controls/date.js | 185 + .../form/controls/date_range.js | 66 + .../form/controls/datepicker_i18n.js | 179 + .../xhiveframework/form/controls/datetime.js | 93 + .../xhiveframework/form/controls/duration.js | 155 + .../form/controls/dynamic_link.js | 32 + .../js/xhiveframework/form/controls/float.js | 27 + .../form/controls/geolocation.js | 258 + .../xhiveframework/form/controls/heading.js | 5 + .../js/xhiveframework/form/controls/html.js | 33 + .../form/controls/html_editor.js | 15 + .../js/xhiveframework/form/controls/icon.js | 101 + .../js/xhiveframework/form/controls/image.js | 23 + .../js/xhiveframework/form/controls/int.js | 44 + .../js/xhiveframework/form/controls/json.js | 6 + .../js/xhiveframework/form/controls/link.js | 652 ++ .../form/controls/markdown_editor.js | 104 + .../form/controls/multicheck.js | 177 + .../form/controls/multiselect.js | 112 + .../form/controls/multiselect_list.js | 264 + .../form/controls/multiselect_pills.js | 162 + .../xhiveframework/form/controls/password.js | 110 + .../js/xhiveframework/form/controls/phone.js | 220 + .../controls/quill-mention/blots/mention.js | 39 + .../controls/quill-mention/constants/keys.js | 9 + .../controls/quill-mention/quill.mention.js | 393 ++ .../js/xhiveframework/form/controls/rating.js | 102 + .../js/xhiveframework/form/controls/select.js | 180 + .../xhiveframework/form/controls/signature.js | 139 + .../js/xhiveframework/form/controls/table.js | 141 + .../form/controls/table_multiselect.js | 179 + .../js/xhiveframework/form/controls/text.js | 29 + .../form/controls/text_editor.js | 323 + .../js/xhiveframework/form/controls/time.js | 112 + .../js/xhiveframework/form/dashboard.js | 664 ++ .../form/footer/base_timeline.js | 151 + .../js/xhiveframework/form/footer/footer.js | 72 + .../form/footer/form_timeline.js | 740 ++ .../version_timeline_content_builder.js | 300 + .../public/js/xhiveframework/form/form.js | 2140 ++++++ .../js/xhiveframework/form/form_tour.js | 333 + .../js/xhiveframework/form/form_viewers.js | 85 + .../js/xhiveframework/form/formatters.js | 436 ++ .../public/js/xhiveframework/form/grid.js | 1242 ++++ .../js/xhiveframework/form/grid_pagination.js | 184 + .../public/js/xhiveframework/form/grid_row.js | 1500 +++++ .../js/xhiveframework/form/grid_row_form.js | 148 + .../public/js/xhiveframework/form/layout.js | 793 +++ .../js/xhiveframework/form/link_selector.js | 253 + .../js/xhiveframework/form/linked_with.js | 80 + .../form/multi_select_dialog.js | 649 ++ .../js/xhiveframework/form/print_utils.js | 209 + .../js/xhiveframework/form/quick_entry.js | 301 + .../js/xhiveframework/form/reminders.js | 100 + .../public/js/xhiveframework/form/save.js | 348 + .../js/xhiveframework/form/script_helpers.js | 65 + .../js/xhiveframework/form/script_manager.js | 268 + .../public/js/xhiveframework/form/section.js | 164 + .../xhiveframework/form/sidebar/assign_to.js | 347 + .../form/sidebar/attachments.js | 275 + .../form/sidebar/document_follow.js | 153 + .../form/sidebar/form_sidebar.js | 286 + .../form/sidebar/form_sidebar_users.js | 77 + .../js/xhiveframework/form/sidebar/review.js | 198 + .../js/xhiveframework/form/sidebar/share.js | 204 + .../xhiveframework/form/sidebar/user_image.js | 89 + .../js/xhiveframework/form/success_action.js | 106 + .../public/js/xhiveframework/form/tab.js | 102 + .../form/templates/address_list.html | 44 + .../form/templates/contact_list.html | 92 + .../form/templates/form_dashboard.html | 20 + .../form/templates/form_footer.html | 9 + .../form/templates/form_links.html | 33 + .../form/templates/form_sidebar.html | 181 + .../form/templates/print_layout.html | 56 + .../form/templates/report_links.html | 23 + .../form/templates/set_sharing.html | 71 + .../form/templates/timeline_message_box.html | 113 + .../form/templates/users_in_sidebar.html | 13 + .../public/js/xhiveframework/form/toolbar.js | 764 +++ .../js/xhiveframework/form/undo_manager.js | 74 + .../public/js/xhiveframework/form/workflow.js | 157 + .../public/js/xhiveframework/format.js | 21 + .../xhiveframework/icon_picker/icon_picker.js | 88 + .../js/xhiveframework/list/base_list.js | 911 +++ .../js/xhiveframework/list/bulk_operations.js | 422 ++ .../js/xhiveframework/list/list_factory.js | 97 + .../js/xhiveframework/list/list_filter.js | 200 + .../js/xhiveframework/list/list_settings.js | 404 ++ .../js/xhiveframework/list/list_sidebar.html | 76 + .../js/xhiveframework/list/list_sidebar.js | 292 + .../list/list_sidebar_group_by.js | 271 + .../list/list_sidebar_stat.html | 16 + .../js/xhiveframework/list/list_view.js | 2111 ++++++ .../list_view_permission_restrictions.html | 16 + .../xhiveframework/list/list_view_select.js | 346 + .../public/js/xhiveframework/logtypes.js | 35 + .../public/js/xhiveframework/meta_tag.js | 19 + .../public/js/xhiveframework/microtemplate.js | 220 + .../js/xhiveframework/model/create_new.js | 389 ++ .../js/xhiveframework/model/indicator.js | 120 + .../public/js/xhiveframework/model/meta.js | 333 + .../public/js/xhiveframework/model/model.js | 873 +++ .../public/js/xhiveframework/model/perm.js | 303 + .../public/js/xhiveframework/model/sync.js | 180 + .../js/xhiveframework/model/user_settings.js | 57 + .../js/xhiveframework/model/workflow.js | 98 + .../public/js/xhiveframework/module_editor.js | 59 + .../phone_picker/phone_picker.js | 105 + .../public/js/xhiveframework/polyfill.js | 85 + .../public/js/xhiveframework/provide.js | 50 + .../public/js/xhiveframework/query_string.js | 79 + .../public/js/xhiveframework/request.js | 669 ++ .../public/js/xhiveframework/roles_editor.js | 143 + .../public/js/xhiveframework/router.js | 582 ++ .../js/xhiveframework/router_history.js | 40 + .../public/js/xhiveframework/scanner/index.js | 99 + .../js/xhiveframework/socketio_client.js | 218 + .../public/js/xhiveframework/translate.js | 39 + .../ui/alt_keyboard_shortcuts.js | 179 + .../public/js/xhiveframework/ui/app_icon.js | 62 + .../public/js/xhiveframework/ui/capture.js | 340 + .../public/js/xhiveframework/ui/chart.js | 39 + .../public/js/xhiveframework/ui/colors.js | 142 + .../public/js/xhiveframework/ui/datatable.js | 3 + .../public/js/xhiveframework/ui/dialog.js | 315 + .../public/js/xhiveframework/ui/driver.js | 3 + .../js/xhiveframework/ui/field_group.js | 200 + .../ui/filters/edit_filter.html | 26 + .../xhiveframework/ui/filters/field_select.js | 190 + .../js/xhiveframework/ui/filters/filter.js | 592 ++ .../xhiveframework/ui/filters/filter_list.js | 368 + .../public/js/xhiveframework/ui/find.js | 18 + .../xhiveframework/ui/group_by/group_by.html | 43 + .../js/xhiveframework/ui/group_by/group_by.js | 436 ++ .../public/js/xhiveframework/ui/iconbar.js | 73 + .../public/js/xhiveframework/ui/keyboard.js | 341 + .../public/js/xhiveframework/ui/like.js | 148 + .../js/xhiveframework/ui/link_preview.js | 237 + .../public/js/xhiveframework/ui/listing.html | 34 + .../public/js/xhiveframework/ui/messages.js | 470 ++ .../ui/notifications/notifications.js | 441 ++ .../public/js/xhiveframework/ui/page.html | 83 + .../public/js/xhiveframework/ui/page.js | 949 +++ .../public/js/xhiveframework/ui/sidebar.js | 70 + .../public/js/xhiveframework/ui/slides.js | 455 ++ .../js/xhiveframework/ui/sort_selector.html | 25 + .../js/xhiveframework/ui/sort_selector.js | 202 + .../public/js/xhiveframework/ui/tag_editor.js | 130 + .../public/js/xhiveframework/ui/tags.js | 116 + .../js/xhiveframework/ui/theme_switcher.js | 163 + .../js/xhiveframework/ui/toolbar/about.js | 71 + .../xhiveframework/ui/toolbar/awesome_bar.js | 365 + .../xhiveframework/ui/toolbar/fuzzy_match.js | 180 + .../js/xhiveframework/ui/toolbar/navbar.html | 139 + .../js/xhiveframework/ui/toolbar/search.html | 8 + .../js/xhiveframework/ui/toolbar/search.js | 401 ++ .../xhiveframework/ui/toolbar/search_utils.js | 706 ++ .../js/xhiveframework/ui/toolbar/tag_utils.js | 113 + .../js/xhiveframework/ui/toolbar/toolbar.js | 301 + .../public/js/xhiveframework/ui/tree.js | 314 + .../ui/workspace_loading_skeleton.html | 24 + .../workspace_sidebar_loading_skeleton.html | 22 + .../public/js/xhiveframework/upload.js | 10 + .../utils/address_and_contact.js | 70 + .../public/js/xhiveframework/utils/common.js | 451 ++ .../xhiveframework/utils/dashboard_utils.js | 285 + .../js/xhiveframework/utils/datatable.js | 22 + .../js/xhiveframework/utils/datatype.js | 81 + .../js/xhiveframework/utils/datetime.js | 276 + .../js/xhiveframework/utils/diffview.js | 104 + .../js/xhiveframework/utils/display_image.js | 0 .../utils/energy_point_utils.js | 63 + .../js/xhiveframework/utils/file_manager.js | 58 + .../public/js/xhiveframework/utils/help.js | 55 + .../js/xhiveframework/utils/help_links.js | 85 + .../js/xhiveframework/utils/number_format.js | 328 + .../js/xhiveframework/utils/number_systems.js | 56 + .../js/xhiveframework/utils/pretty_date.js | 106 + .../js/xhiveframework/utils/preview_email.js | 35 + .../public/js/xhiveframework/utils/tools.js | 79 + .../public/js/xhiveframework/utils/urllib.js | 77 + .../public/js/xhiveframework/utils/user.js | 140 + .../public/js/xhiveframework/utils/utils.js | 1765 +++++ .../js/xhiveframework/utils/web_template.js | 70 + .../js/xhiveframework/views/breadcrumbs.js | 246 + .../xhiveframework/views/calendar/calendar.js | 488 ++ .../js/xhiveframework/views/communication.js | 946 +++ .../js/xhiveframework/views/container.js | 105 + .../views/dashboard/dashboard_view.js | 514 ++ .../public/js/xhiveframework/views/factory.js | 51 + .../js/xhiveframework/views/file/file_view.js | 478 ++ .../js/xhiveframework/views/formview.js | 109 + .../xhiveframework/views/gantt/gantt_view.js | 232 + .../xhiveframework/views/image/image_view.js | 365 + .../views/image/image_view_item_row.html | 35 + .../views/image/photoswipe_dom.html | 73 + .../xhiveframework/views/inbox/inbox_view.js | 215 + .../js/xhiveframework/views/interaction.js | 363 + .../views/kanban/kanban_board.bundle.js | 979 +++ .../views/kanban/kanban_board.html | 13 + .../views/kanban/kanban_card.html | 24 + .../views/kanban/kanban_column.html | 28 + .../views/kanban/kanban_settings.js | 247 + .../views/kanban/kanban_view.js | 412 ++ .../js/xhiveframework/views/map/map_view.js | 91 + .../js/xhiveframework/views/pageview.js | 159 + .../views/reports/print_grid.html | 64 + .../views/reports/print_tree.html | 106 + .../views/reports/query_report.js | 2062 ++++++ .../views/reports/report_factory.js | 15 + .../views/reports/report_utils.js | 308 + .../views/reports/report_view.js | 1704 +++++ .../views/translation_manager.js | 107 + .../js/xhiveframework/views/treeview.js | 496 ++ .../views/workspace/blocks/block.js | 340 + .../views/workspace/blocks/card.js | 62 + .../views/workspace/blocks/chart.js | 63 + .../views/workspace/blocks/custom_block.js | 62 + .../views/workspace/blocks/header.js | 142 + .../views/workspace/blocks/header_size.js | 124 + .../views/workspace/blocks/index.js | 33 + .../views/workspace/blocks/number_card.js | 63 + .../views/workspace/blocks/onboarding.js | 143 + .../views/workspace/blocks/paragraph.js | 224 + .../views/workspace/blocks/quick_list.js | 63 + .../views/workspace/blocks/shortcut.js | 81 + .../views/workspace/blocks/spacer.js | 57 + .../views/workspace/workspace.js | 1564 +++++ .../js/xhiveframework/web_form/web_form.js | 457 ++ .../xhiveframework/web_form/web_form_list.js | 409 ++ .../xhiveframework/web_form/webform_script.js | 98 + .../js/xhiveframework/widgets/base_widget.js | 198 + .../js/xhiveframework/widgets/chart_widget.js | 793 +++ .../widgets/custom_block_widget.js | 46 + .../js/xhiveframework/widgets/links_widget.js | 158 + .../js/xhiveframework/widgets/new_widget.js | 64 + .../widgets/number_card_widget.js | 367 + .../widgets/onboarding_widget.js | 634 ++ .../widgets/quick_list_widget.js | 259 + .../xhiveframework/widgets/shortcut_widget.js | 111 + .../xhiveframework/widgets/widget_dialog.js | 792 +++ .../js/xhiveframework/widgets/widget_group.js | 238 + xhiveframework/public/scss/common/alert.scss | 12 + .../public/scss/common/awesomeplete.scss | 72 + .../public/scss/common/buttons.scss | 133 + .../public/scss/common/color_picker.scss | 134 + .../public/scss/common/controls.scss | 537 ++ .../public/scss/common/css_variables.scss | 156 + .../public/scss/common/datepicker.scss | 117 + xhiveframework/public/scss/common/flex.scss | 89 + xhiveframework/public/scss/common/form.scss | 30 + xhiveframework/public/scss/common/global.scss | 162 + xhiveframework/public/scss/common/grid.scss | 580 ++ .../public/scss/common/icon_picker.scss | 96 + xhiveframework/public/scss/common/icons.scss | 79 + .../public/scss/common/indicator.scss | 84 + xhiveframework/public/scss/common/mixins.scss | 44 + xhiveframework/public/scss/common/modal.scss | 289 + .../public/scss/common/phone_picker.scss | 141 + xhiveframework/public/scss/common/quill.scss | 339 + xhiveframework/public/scss/desk.bundle.scss | 11 + xhiveframework/public/scss/desk/avatar.scss | 208 + .../public/scss/desk/breadcrumb.scss | 26 + xhiveframework/public/scss/desk/calendar.scss | 207 + .../public/scss/desk/css_variables.scss | 54 + xhiveframework/public/scss/desk/dark.scss | 264 + .../public/scss/desk/dashboard_view.scss | 63 + .../public/scss/desk/data_import.scss | 29 + xhiveframework/public/scss/desk/desktop.scss | 1539 +++++ xhiveframework/public/scss/desk/driver.scss | 80 + .../public/scss/desk/file_view.scss | 122 + xhiveframework/public/scss/desk/filters.scss | 105 + xhiveframework/public/scss/desk/form.scss | 521 ++ xhiveframework/public/scss/desk/global.scss | 740 ++ .../public/scss/desk/global_search.scss | 121 + .../public/scss/desk/image_view.scss | 236 + xhiveframework/public/scss/desk/index.scss | 51 + xhiveframework/public/scss/desk/kanban.scss | 358 + .../public/scss/desk/link_preview.scss | 62 + xhiveframework/public/scss/desk/list.scss | 488 ++ xhiveframework/public/scss/desk/mobile.scss | 394 ++ xhiveframework/public/scss/desk/module.scss | 143 + xhiveframework/public/scss/desk/navbar.scss | 105 + .../public/scss/desk/notification.scss | 318 + xhiveframework/public/scss/desk/page.scss | 207 + xhiveframework/public/scss/desk/plyr.scss | 11 + .../public/scss/desk/print_preview.scss | 149 + xhiveframework/public/scss/desk/report.scss | 284 + .../public/scss/desk/role_editor.scss | 50 + .../public/scss/desk/scrollbar.scss | 30 + xhiveframework/public/scss/desk/sidebar.scss | 571 ++ xhiveframework/public/scss/desk/slides.scss | 136 + xhiveframework/public/scss/desk/tags.scss | 16 + .../public/scss/desk/theme_switcher.scss | 115 + xhiveframework/public/scss/desk/timeline.scss | 181 + xhiveframework/public/scss/desk/toast.scss | 150 + xhiveframework/public/scss/desk/tree.scss | 162 + .../public/scss/desk/typography.scss | 28 + .../public/scss/desk/user_profile.scss | 100 + .../public/scss/desk/variables.scss | 135 + xhiveframework/public/scss/desk/version.scss | 34 + .../scss/desk/xhiveframework_datatable.scss | 196 + .../scss/desk/xhiveframework_gantt.scss | 82 + .../public/scss/element/checkbox.scss | 124 + xhiveframework/public/scss/element/radio.scss | 37 + xhiveframework/public/scss/email.bundle.scss | 430 ++ .../public/scss/espresso/_borders.scss | 10 + .../public/scss/espresso/_colors.scss | 205 + .../public/scss/espresso/_shadows.scss | 29 + .../public/scss/espresso/_spacing.scss | 8 + .../public/scss/espresso/_typography.scss | 100 + xhiveframework/public/scss/login.bundle.scss | 220 + xhiveframework/public/scss/print.bundle.scss | 48 + .../public/scss/print_format.bundle.scss | 5 + xhiveframework/public/scss/report.bundle.scss | 2 + .../public/scss/web_form.bundle.scss | 3 + .../public/scss/website.bundle.scss | 1 + xhiveframework/public/scss/website/base.scss | 111 + xhiveframework/public/scss/website/blog.scss | 138 + .../public/scss/website/css_variables.scss | 19 + xhiveframework/public/scss/website/doc.scss | 204 + .../public/scss/website/error-state.scss | 17 + .../public/scss/website/footer.scss | 105 + xhiveframework/public/scss/website/index.scss | 350 + .../public/scss/website/markdown.scss | 137 + .../scss/website/multilevel_dropdown.scss | 17 + .../public/scss/website/my_account.scss | 100 + .../public/scss/website/navbar.scss | 157 + .../public/scss/website/page_builder.scss | 740 ++ .../public/scss/website/portal.scss | 7 + .../public/scss/website/search.scss | 40 + .../public/scss/website/sidebar.scss | 52 + .../public/scss/website/variables.scss | 127 + .../public/scss/website/web_form.scss | 554 ++ .../public/scss/website/website_avatar.scss | 8 + .../public/scss/website/website_image.scss | 64 + xhiveframework/public/sounds/alert.mp3 | Bin 0 -> 10448 bytes xhiveframework/public/sounds/cancel.mp3 | Bin 0 -> 8776 bytes xhiveframework/public/sounds/chime.mp3 | Bin 0 -> 12955 bytes xhiveframework/public/sounds/click.mp3 | Bin 0 -> 5850 bytes xhiveframework/public/sounds/delete.mp3 | Bin 0 -> 9612 bytes xhiveframework/public/sounds/email.mp3 | Bin 0 -> 9743 bytes xhiveframework/public/sounds/error.mp3 | Bin 0 -> 38451 bytes xhiveframework/public/sounds/submit.mp3 | Bin 0 -> 15463 bytes xhiveframework/push_notification.py | 294 + xhiveframework/query_builder/__init__.py | 22 + xhiveframework/query_builder/builder.py | 99 + xhiveframework/query_builder/custom.py | 104 + xhiveframework/query_builder/docs.md | 117 + xhiveframework/query_builder/functions.py | 166 + xhiveframework/query_builder/terms.py | 120 + xhiveframework/query_builder/utils.py | 153 + xhiveframework/rate_limiter.py | 156 + xhiveframework/realtime.py | 165 + xhiveframework/recorder.py | 410 ++ xhiveframework/search/__init__.py | 14 + xhiveframework/search/full_text_search.py | 161 + .../search/test_full_text_search.py | 132 + xhiveframework/search/website_search.py | 148 + xhiveframework/sessions.py | 449 ++ xhiveframework/share.py | 245 + xhiveframework/social/__init__.py | 0 xhiveframework/social/doctype/__init__.py | 0 .../doctype/energy_point_log/__init__.py | 0 .../energy_point_log/energy_point_log.js | 50 + .../energy_point_log/energy_point_log.json | 134 + .../energy_point_log/energy_point_log.py | 391 ++ .../energy_point_log/energy_point_log_list.js | 29 + .../energy_point_log/test_energy_point_log.py | 373 ++ .../doctype/energy_point_rule/__init__.py | 0 .../energy_point_rule/energy_point_rule.js | 58 + .../energy_point_rule/energy_point_rule.json | 140 + .../energy_point_rule/energy_point_rule.py | 158 + .../doctype/energy_point_settings/__init__.py | 0 .../energy_point_settings.js | 50 + .../energy_point_settings.json | 70 + .../energy_point_settings.py | 77 + .../test_energy_point_settings.py | 9 + .../social/doctype/review_level/__init__.py | 0 .../doctype/review_level/review_level.js | 7 + .../doctype/review_level/review_level.json | 51 + .../doctype/review_level/review_level.py | 24 + xhiveframework/templates/__init__.py | 0 xhiveframework/templates/base.html | 105 + .../templates/discussions/button.html | 6 + .../templates/discussions/comment_box.html | 32 + .../templates/discussions/discussions.js | 362 + .../discussions/discussions_section.html | 80 + .../templates/discussions/reply_card.html | 61 + .../templates/discussions/reply_section.html | 54 + .../templates/discussions/search.html | 2 + .../templates/discussions/sidebar.html | 24 + .../templates/discussions/topic_modal.html | 15 + xhiveframework/templates/doc.html | 116 + xhiveframework/templates/emails/.txt | 0 xhiveframework/templates/emails/__init__.py | 0 .../emails/account_deletion_notification.html | 4 + .../emails/administrator_logged_in.html | 3 + .../templates/emails/auto_email_report.html | 67 + .../templates/emails/auto_repeat_fail.html | 8 + .../templates/emails/auto_reply.html | 7 + .../emails/data_deletion_approval.html | 8 + .../emails/delete_data_confirmation.html | 12 + .../templates/emails/document_follow.html | 88 + .../templates/emails/download_data.html | 10 + .../templates/emails/email_footer.html | 33 + .../templates/emails/email_header.html | 12 + .../emails/energy_points_summary.html | 54 + .../emails/file_backup_notification.html | 6 + .../emails/login_with_email_link.html | 41 + .../templates/emails/new_message.html | 5 + .../templates/emails/new_notification.html | 11 + xhiveframework/templates/emails/new_user.html | 24 + .../templates/emails/newsletter.html | 13 + .../templates/emails/password_reset.html | 7 + .../templates/emails/print_link.html | 3 + xhiveframework/templates/emails/standard.html | 67 + .../templates/emails/upcoming_events.html | 9 + .../templates/emails/verification_code.html | 1 + .../templates/emails/workflow_action.html | 8 + .../templates/form_grid/fields.html | 46 + xhiveframework/templates/includes/__init__.py | 0 .../app_analytics/google_analytics.html | 25 + .../app_analytics/heap_analytics.html | 6 + .../templates/includes/avatar_macro.html | 17 + .../templates/includes/blog/blogger.html | 14 + .../templates/includes/blog/hero.html | 12 + .../templates/includes/breadcrumbs.html | 24 + .../templates/includes/comments/__init__.py | 0 .../templates/includes/comments/comment.html | 18 + .../templates/includes/comments/comments.html | 298 + .../templates/includes/comments/comments.py | 74 + xhiveframework/templates/includes/contact.js | 46 + .../templates/includes/footer/footer.html | 12 + .../includes/footer/footer_extension.html | 0 .../includes/footer/footer_grouped_links.html | 33 + .../includes/footer/footer_info.html | 23 + .../includes/footer/footer_links.html | 24 + .../footer/footer_logo_extension.html | 16 + .../includes/footer/footer_powered.html | 1 + .../templates/includes/form_macros.html | 13 + .../templates/includes/full_index.html | 11 + xhiveframework/templates/includes/head.html | 14 + .../templates/includes/image_with_blur.html | 29 + .../includes/integrations/third_party_apps.js | 9 + .../templates/includes/likes/__init__.py | 0 .../templates/includes/likes/likes.html | 41 + .../templates/includes/likes/likes.py | 78 + .../templates/includes/list/__init__.py | 0 .../templates/includes/list/filters.html | 22 + .../templates/includes/list/filters.js | 47 + .../templates/includes/list/list.html | 26 + .../templates/includes/list/list.js | 45 + .../templates/includes/list/row_template.html | 15 + .../templates/includes/login/login.js | 395 ++ xhiveframework/templates/includes/macros.html | 13 + .../templates/includes/meta_block.html | 8 + .../includes/navbar/dropdown_items.html | 18 + .../includes/navbar/dropdown_login.html | 17 + .../templates/includes/navbar/navbar.html | 32 + .../includes/navbar/navbar_items.html | 108 + .../includes/navbar/navbar_login.html | 27 + .../includes/navbar/navbar_search.html | 9 + .../includes/oauth_confirmation.html | 44 + .../templates/includes/print_table.html | 26 + .../templates/includes/search_box.html | 28 + .../templates/includes/search_result.html | 6 + .../templates/includes/search_template.html | 52 + .../templates/includes/slideshow.html | 50 + .../templates/includes/splash_screen.html | 4 + .../templates/includes/static_index.html | 12 + .../templates/includes/web_block.html | 32 + .../templates/includes/web_sidebar.html | 82 + .../includes/website_theme/footer.css | 19 + .../includes/website_theme/navbar.css | 103 + xhiveframework/templates/pages/__init__.py | 0 .../templates/pages/integrations/__init__.py | 0 .../pages/integrations/gcalendar-success.html | 21 + .../templates/print_format/macros.html | 13 + .../templates/print_format/macros/Attach.html | 7 + .../print_format/macros/AttachImage.html | 7 + .../templates/print_format/macros/Check.html | 9 + .../templates/print_format/macros/Code.html | 7 + .../templates/print_format/macros/Color.html | 8 + .../templates/print_format/macros/Data.html | 10 + .../print_format/macros/Divider.html | 2 + .../print_format/macros/FieldTemplate.html | 4 + .../templates/print_format/macros/HTML.html | 3 + .../print_format/macros/Markdown.html | 9 + .../templates/print_format/macros/Rating.html | 22 + .../print_format/macros/Signature.html | 7 + .../templates/print_format/macros/Spacer.html | 2 + .../templates/print_format/macros/Table.html | 30 + .../templates/print_format/print_footer.html | 24 + .../templates/print_format/print_format.css | 135 + .../templates/print_format/print_format.html | 40 + .../print_format/print_format_font.css | 9 + .../templates/print_format/print_header.html | 24 + .../print_formats/pdf_header_footer.html | 75 + .../templates/print_formats/standard.html | 39 + .../print_formats/standard_macros.html | 215 + xhiveframework/templates/signup.html | 22 + .../templates/styles/card_style.css | 92 + .../templates/styles/discussion_style.css | 293 + xhiveframework/templates/styles/standard.css | 187 + xhiveframework/templates/test/_test_base.html | 9 + .../test/_test_base_breadcrumbs.html | 20 + xhiveframework/templates/web.html | 76 + xhiveframework/test_runner.py | 512 ++ xhiveframework/tests/__init__.py | 17 + .../tests/data/email_with_image.txt | 5923 +++++++++++++++++ .../tests/data/exif_sample_image.jpg | Bin 0 -> 161713 bytes xhiveframework/tests/data/load_sleep.py | 4 + .../data/sample_image_for_optimization.jpg | Bin 0 -> 249403 bytes xhiveframework/tests/data/sample_svg.svg | 12 + xhiveframework/tests/test_api.py | 478 ++ xhiveframework/tests/test_api_v2.py | 294 + xhiveframework/tests/test_assign.py | 112 + xhiveframework/tests/test_auth.py | 230 + xhiveframework/tests/test_background_jobs.py | 103 + xhiveframework/tests/test_base_document.py | 17 + xhiveframework/tests/test_boilerplate.py | 225 + xhiveframework/tests/test_boot.py | 79 + xhiveframework/tests/test_caching.py | 247 + xhiveframework/tests/test_child_table.py | 63 + xhiveframework/tests/test_client.py | 247 + xhiveframework/tests/test_commands.py | 924 +++ xhiveframework/tests/test_config.py | 14 + xhiveframework/tests/test_cors.py | 67 + .../tests/test_dashboard_connections.py | 350 + xhiveframework/tests/test_db.py | 1040 +++ xhiveframework/tests/test_db_query.py | 1472 ++++ xhiveframework/tests/test_db_update.py | 187 + xhiveframework/tests/test_defaults.py | 112 + xhiveframework/tests/test_deferred_insert.py | 12 + xhiveframework/tests/test_docstatus.py | 25 + xhiveframework/tests/test_document.py | 574 ++ xhiveframework/tests/test_document_locks.py | 48 + xhiveframework/tests/test_domainification.py | 165 + xhiveframework/tests/test_dynamic_links.py | 83 + xhiveframework/tests/test_email.py | 422 ++ .../tests/test_exporter_fixtures.py | 286 + xhiveframework/tests/test_fixture_import.py | 80 + xhiveframework/tests/test_fmt_datetime.py | 129 + xhiveframework/tests/test_fmt_money.py | 109 + xhiveframework/tests/test_form_load.py | 200 + xhiveframework/tests/test_formatter.py | 19 + xhiveframework/tests/test_geo_ip.py | 13 + xhiveframework/tests/test_global_search.py | 207 + xhiveframework/tests/test_goal.py | 48 + xhiveframework/tests/test_hooks.py | 123 + xhiveframework/tests/test_linked_with.py | 150 + xhiveframework/tests/test_listview.py | 96 + xhiveframework/tests/test_model_utils.py | 82 + xhiveframework/tests/test_modules.py | 194 + xhiveframework/tests/test_monitor.py | 90 + xhiveframework/tests/test_naming.py | 400 ++ xhiveframework/tests/test_nestedset.py | 297 + xhiveframework/tests/test_oauth20.py | 408 ++ xhiveframework/tests/test_password.py | 133 + .../tests/test_password_strength.py | 26 + xhiveframework/tests/test_patches.py | 176 + xhiveframework/tests/test_pdf.py | 66 + xhiveframework/tests/test_perf.py | 198 + xhiveframework/tests/test_permissions.py | 745 +++ xhiveframework/tests/test_printview.py | 32 + xhiveframework/tests/test_query.py | 457 ++ xhiveframework/tests/test_query_builder.py | 489 ++ xhiveframework/tests/test_query_report.py | 263 + xhiveframework/tests/test_rate_limiter.py | 112 + xhiveframework/tests/test_rating.py | 29 + xhiveframework/tests/test_recorder.py | 170 + xhiveframework/tests/test_redis.py | 77 + xhiveframework/tests/test_rename_doc.py | 281 + xhiveframework/tests/test_reportview.py | 34 + xhiveframework/tests/test_safe_exec.py | 124 + xhiveframework/tests/test_scheduler.py | 112 + xhiveframework/tests/test_search.py | 264 + xhiveframework/tests/test_seen.py | 51 + xhiveframework/tests/test_sequence.py | 49 + xhiveframework/tests/test_sitemap.py | 15 + xhiveframework/tests/test_test_utils.py | 43 + xhiveframework/tests/test_translate.py | 268 + xhiveframework/tests/test_twofactor.py | 246 + xhiveframework/tests/test_utils.py | 1245 ++++ xhiveframework/tests/test_virtual_doctype.py | 177 + xhiveframework/tests/test_webform.py | 83 + xhiveframework/tests/test_website.py | 413 ++ .../tests/test_xhiveframework_client.py | 204 + xhiveframework/tests/tests_geo_utils.py | 50 + .../tests/translation_test_file.txt | 35 + xhiveframework/tests/ui_test_helpers.py | 664 ++ xhiveframework/tests/utils.py | 364 + xhiveframework/translate.py | 1356 ++++ xhiveframework/translations/af.csv | 4798 +++++++++++++ xhiveframework/translations/am.csv | 4798 +++++++++++++ xhiveframework/translations/ar.csv | 4798 +++++++++++++ xhiveframework/translations/bg.csv | 4798 +++++++++++++ xhiveframework/translations/bn.csv | 4798 +++++++++++++ xhiveframework/translations/bs.csv | 4798 +++++++++++++ xhiveframework/translations/ca.csv | 4798 +++++++++++++ xhiveframework/translations/cs.csv | 4798 +++++++++++++ xhiveframework/translations/cz.csv | 1471 ++++ xhiveframework/translations/da-DK.csv | 9 + xhiveframework/translations/da.csv | 4798 +++++++++++++ xhiveframework/translations/de.csv | 5000 ++++++++++++++ xhiveframework/translations/el.csv | 4798 +++++++++++++ xhiveframework/translations/en-GB.csv | 0 xhiveframework/translations/en-US.csv | 18 + xhiveframework/translations/es-AR.csv | 3 + xhiveframework/translations/es-BO.csv | 0 xhiveframework/translations/es-CL.csv | 37 + xhiveframework/translations/es-DO.csv | 0 xhiveframework/translations/es-EC.csv | 1 + xhiveframework/translations/es-MX.csv | 29 + xhiveframework/translations/es-NI.csv | 5 + xhiveframework/translations/es-PE.csv | 494 ++ xhiveframework/translations/es.csv | 4816 ++++++++++++++ xhiveframework/translations/et.csv | 4798 +++++++++++++ xhiveframework/translations/fa.csv | 4798 +++++++++++++ xhiveframework/translations/fi.csv | 4798 +++++++++++++ xhiveframework/translations/fil.csv | 0 xhiveframework/translations/fr-CA.csv | 26 + xhiveframework/translations/fr.csv | 4851 ++++++++++++++ xhiveframework/translations/gu.csv | 4798 +++++++++++++ xhiveframework/translations/he.csv | 4798 +++++++++++++ xhiveframework/translations/hi.csv | 4798 +++++++++++++ xhiveframework/translations/hr.csv | 4798 +++++++++++++ xhiveframework/translations/hu.csv | 4798 +++++++++++++ xhiveframework/translations/id.csv | 4798 +++++++++++++ xhiveframework/translations/is.csv | 4798 +++++++++++++ xhiveframework/translations/it.csv | 4801 +++++++++++++ xhiveframework/translations/ja.csv | 4798 +++++++++++++ xhiveframework/translations/km.csv | 4798 +++++++++++++ xhiveframework/translations/kn.csv | 4798 +++++++++++++ xhiveframework/translations/ko.csv | 4798 +++++++++++++ xhiveframework/translations/ku.csv | 4798 +++++++++++++ xhiveframework/translations/lo.csv | 4798 +++++++++++++ xhiveframework/translations/lt.csv | 4798 +++++++++++++ xhiveframework/translations/lv.csv | 4798 +++++++++++++ xhiveframework/translations/mk.csv | 4798 +++++++++++++ xhiveframework/translations/ml.csv | 4798 +++++++++++++ xhiveframework/translations/mr.csv | 4798 +++++++++++++ xhiveframework/translations/ms.csv | 4798 +++++++++++++ xhiveframework/translations/my.csv | 4798 +++++++++++++ xhiveframework/translations/nl.csv | 4798 +++++++++++++ xhiveframework/translations/no.csv | 4798 +++++++++++++ xhiveframework/translations/pl.csv | 4797 +++++++++++++ xhiveframework/translations/ps.csv | 4798 +++++++++++++ xhiveframework/translations/pt-BR.csv | 4798 +++++++++++++ xhiveframework/translations/pt.csv | 4798 +++++++++++++ xhiveframework/translations/quc.csv | 0 xhiveframework/translations/ro.csv | 4798 +++++++++++++ xhiveframework/translations/ru.csv | 4790 +++++++++++++ xhiveframework/translations/rw.csv | 4798 +++++++++++++ xhiveframework/translations/se.csv | 0 xhiveframework/translations/si.csv | 4798 +++++++++++++ xhiveframework/translations/sk.csv | 4798 +++++++++++++ xhiveframework/translations/sl.csv | 4798 +++++++++++++ xhiveframework/translations/sq.csv | 4798 +++++++++++++ xhiveframework/translations/sr-BA.csv | 469 ++ xhiveframework/translations/sr-SP.csv | 474 ++ xhiveframework/translations/sr.csv | 4798 +++++++++++++ xhiveframework/translations/sv.csv | 4798 +++++++++++++ xhiveframework/translations/sw.csv | 4798 +++++++++++++ xhiveframework/translations/ta.csv | 4798 +++++++++++++ xhiveframework/translations/te.csv | 4798 +++++++++++++ xhiveframework/translations/th.csv | 4798 +++++++++++++ xhiveframework/translations/tr.csv | 4870 ++++++++++++++ xhiveframework/translations/uk.csv | 4798 +++++++++++++ xhiveframework/translations/ur.csv | 4798 +++++++++++++ xhiveframework/translations/uz.csv | 4798 +++++++++++++ xhiveframework/translations/vi.csv | 4798 +++++++++++++ xhiveframework/translations/zh-TW.csv | 3056 +++++++++ xhiveframework/translations/zh.csv | 4797 +++++++++++++ xhiveframework/twofactor.py | 481 ++ xhiveframework/types/DF.py | 38 + xhiveframework/types/__init__.py | 0 xhiveframework/types/exporter.py | 210 + xhiveframework/utils/__init__.py | 1168 ++++ xhiveframework/utils/background_jobs.py | 642 ++ xhiveframework/utils/backups.py | 694 ++ xhiveframework/utils/bench_helper.py | 114 + xhiveframework/utils/boilerplate.py | 674 ++ xhiveframework/utils/caching.py | 165 + xhiveframework/utils/change_log.py | 307 + xhiveframework/utils/commands.py | 63 + xhiveframework/utils/connections.py | 50 + xhiveframework/utils/csvutils.py | 223 + xhiveframework/utils/dashboard.py | 113 + xhiveframework/utils/data.py | 2250 +++++++ xhiveframework/utils/dateutils.py | 168 + xhiveframework/utils/deprecations.py | 27 + xhiveframework/utils/diff.py | 65 + xhiveframework/utils/doctor.py | 152 + xhiveframework/utils/error.py | 165 + xhiveframework/utils/file_lock.py | 74 + xhiveframework/utils/file_manager.py | 452 ++ xhiveframework/utils/fixtures.py | 87 + xhiveframework/utils/formatters.py | 145 + xhiveframework/utils/global_search.py | 588 ++ xhiveframework/utils/goal.py | 142 + xhiveframework/utils/html_utils.py | 755 +++ xhiveframework/utils/identicon.py | 83 + xhiveframework/utils/image.py | 75 + xhiveframework/utils/install.py | 287 + xhiveframework/utils/jinja.py | 191 + xhiveframework/utils/jinja_globals.py | 146 + xhiveframework/utils/lazy_loader.py | 35 + xhiveframework/utils/logger.py | 126 + xhiveframework/utils/make_random.py | 63 + xhiveframework/utils/momentjs.py | 4795 +++++++++++++ xhiveframework/utils/nestedset.py | 403 ++ xhiveframework/utils/oauth.py | 317 + xhiveframework/utils/password.py | 222 + xhiveframework/utils/password_strength.py | 185 + xhiveframework/utils/pdf.py | 334 + xhiveframework/utils/print_format.py | 282 + xhiveframework/utils/redis_queue.py | 80 + xhiveframework/utils/redis_wrapper.py | 331 + xhiveframework/utils/response.py | 323 + xhiveframework/utils/safe_exec.py | 644 ++ xhiveframework/utils/scheduler.py | 198 + xhiveframework/utils/sentry.py | 151 + xhiveframework/utils/synchronization.py | 49 + xhiveframework/utils/telemetry.py | 72 + xhiveframework/utils/testutils.py | 20 + xhiveframework/utils/typing_validations.py | 154 + xhiveframework/utils/user.py | 428 ++ xhiveframework/utils/verified_command.py | 85 + xhiveframework/utils/weasyprint.py | 254 + xhiveframework/utils/xlsxutils.py | 105 + xhiveframework/website/__init__.py | 2 + xhiveframework/website/css/web_form.css | 104 + xhiveframework/website/dashboard_fixtures.py | 43 + xhiveframework/website/doctype/__init__.py | 0 .../doctype/about_us_settings/README.md | 1 + .../doctype/about_us_settings/__init__.py | 0 .../about_us_settings/about_us_settings.js | 8 + .../about_us_settings/about_us_settings.json | 104 + .../about_us_settings/about_us_settings.py | 39 + .../test_about_us_settings.py | 8 + .../doctype/about_us_team_member/README.md | 1 + .../doctype/about_us_team_member/__init__.py | 0 .../about_us_team_member.json | 49 + .../about_us_team_member.py | 26 + .../website/doctype/blog_category/README.md | 1 + .../website/doctype/blog_category/__init__.py | 0 .../doctype/blog_category/blog_category.js | 6 + .../doctype/blog_category/blog_category.json | 100 + .../doctype/blog_category/blog_category.py | 33 + .../templates/blog_category.html | 9 + .../templates/blog_category_row.html | 4 + .../blog_category/test_blog_category.py | 8 + .../doctype/blog_category/test_records.json | 17 + .../website/doctype/blog_post/README.md | 1 + .../website/doctype/blog_post/__init__.py | 0 .../website/doctype/blog_post/blog_post.js | 67 + .../website/doctype/blog_post/blog_post.json | 253 + .../website/doctype/blog_post/blog_post.py | 390 ++ .../doctype/blog_post/blog_post_list.js | 10 + .../blog_post/templates/blog_post.html | 91 + .../blog_post/templates/blog_post_list.html | 91 + .../blog_post/templates/blog_post_row.html | 43 + .../doctype/blog_post/test_blog_post.py | 197 + .../doctype/blog_post/test_records.json | 38 + .../doctype/blog_post/ui_test_blog_post.js | 36 + .../website/doctype/blog_settings/README.md | 1 + .../website/doctype/blog_settings/__init__.py | 0 .../doctype/blog_settings/blog_settings.js | 6 + .../doctype/blog_settings/blog_settings.json | 161 + .../doctype/blog_settings/blog_settings.py | 46 + .../blog_settings/test_blog_settings.py | 8 + .../website/doctype/blogger/README.md | 1 + .../website/doctype/blogger/__init__.py | 0 .../website/doctype/blogger/blogger.js | 6 + .../website/doctype/blogger/blogger.json | 102 + .../website/doctype/blogger/blogger.py | 52 + .../website/doctype/blogger/test_blogger.py | 6 + .../website/doctype/blogger/test_records.json | 17 + .../website/doctype/color/__init__.py | 0 xhiveframework/website/doctype/color/color.js | 7 + .../website/doctype/color/color.json | 44 + xhiveframework/website/doctype/color/color.py | 19 + .../website/doctype/color/test_color.py | 8 + .../website/doctype/company_history/README.md | 1 + .../doctype/company_history/__init__.py | 0 .../company_history/company_history.json | 39 + .../company_history/company_history.py | 25 + .../doctype/contact_us_settings/README.md | 1 + .../doctype/contact_us_settings/__init__.py | 0 .../contact_us_settings.js | 8 + .../contact_us_settings.json | 143 + .../contact_us_settings.py | 39 + .../doctype/discussion_reply/__init__.py | 0 .../discussion_reply/discussion_reply.js | 7 + .../discussion_reply/discussion_reply.json | 54 + .../discussion_reply/discussion_reply.py | 82 + .../discussion_reply/test_discussion_reply.py | 9 + .../doctype/discussion_topic/__init__.py | 0 .../discussion_topic/discussion_topic.js | 7 + .../discussion_topic/discussion_topic.json | 63 + .../discussion_topic/discussion_topic.py | 52 + .../discussion_topic/test_discussion_topic.py | 9 + .../website/doctype/help_article/__init__.py | 0 .../doctype/help_article/help_article.js | 23 + .../doctype/help_article/help_article.json | 146 + .../doctype/help_article/help_article.py | 139 + .../help_article/templates/help_article.html | 64 + .../templates/help_article_row.html | 15 + .../doctype/help_article/test_help_article.py | 57 + .../website/doctype/help_category/__init__.py | 0 .../doctype/help_category/help_category.js | 6 + .../doctype/help_category/help_category.json | 72 + .../doctype/help_category/help_category.py | 45 + .../help_category/test_help_category.py | 10 + .../doctype/marketing_campaign/__init__.py | 0 .../marketing_campaign.json | 64 + .../marketing_campaign/marketing_campaign.py | 19 + .../__init__.py | 0 .../personal_data_deletion_request.js | 34 + .../personal_data_deletion_request.json | 70 + .../personal_data_deletion_request.py | 409 ++ .../test_personal_data_deletion_request.py | 71 + .../personal_data_deletion_step/__init__.py | 0 .../personal_data_deletion_step.json | 66 + .../personal_data_deletion_step.py | 26 + .../__init__.py | 0 .../personal_data_download_request.js | 10 + .../personal_data_download_request.json | 65 + .../personal_data_download_request.py | 82 + .../test_personal_data_download_request.py | 66 + .../doctype/portal_menu_item/__init__.py | 0 .../portal_menu_item/portal_menu_item.json | 75 + .../portal_menu_item/portal_menu_item.py | 27 + .../doctype/portal_settings/__init__.py | 0 .../portal_settings/portal_settings.js | 25 + .../portal_settings/portal_settings.json | 79 + .../portal_settings/portal_settings.py | 80 + .../portal_settings/test_portal_settings.py | 8 + .../doctype/social_link_settings/__init__.py | 0 .../social_link_settings.json | 43 + .../social_link_settings.py | 24 + .../website/doctype/top_bar_item/README.md | 1 + .../website/doctype/top_bar_item/__init__.py | 0 .../doctype/top_bar_item/top_bar_item.json | 73 + .../doctype/top_bar_item/top_bar_item.py | 27 + .../website/doctype/web_form/__init__.py | 0 .../doctype/web_form/templates/web_form.html | 197 + .../web_form/templates/web_form_row.html | 4 + .../web_form/templates/web_form_skeleton.html | 38 + .../doctype/web_form/templates/web_list.html | 44 + .../doctype/web_form/test_records.json | 52 + .../website/doctype/web_form/test_web_form.py | 75 + .../website/doctype/web_form/web_form.js | 368 + .../website/doctype/web_form/web_form.json | 417 ++ .../website/doctype/web_form/web_form.py | 686 ++ .../website/doctype/web_form/web_form_list.js | 10 + .../doctype/web_form_field/__init__.py | 0 .../web_form_field/web_form_field.json | 168 + .../doctype/web_form_field/web_form_field.py | 65 + .../doctype/web_form_list_column/__init__.py | 0 .../web_form_list_column.json | 48 + .../web_form_list_column.py | 25 + .../website/doctype/web_page/README.md | 1 + .../website/doctype/web_page/__init__.py | 0 .../doctype/web_page/templates/web_page.html | 62 + .../web_page/templates/web_page_row.html | 4 + .../doctype/web_page/test_records.json | 35 + .../website/doctype/web_page/test_web_page.py | 65 + .../website/doctype/web_page/web_page.js | 136 + .../website/doctype/web_page/web_page.json | 364 + .../website/doctype/web_page/web_page.py | 299 + .../website/doctype/web_page/web_page_list.js | 10 + .../doctype/web_page_block/__init__.py | 0 .../web_page_block/web_page_block.json | 128 + .../doctype/web_page_block/web_page_block.py | 34 + .../website/doctype/web_page_view/__init__.py | 0 .../web_page_view/test_web_page_view.py | 8 + .../doctype/web_page_view/web_page_view.js | 7 + .../doctype/web_page_view/web_page_view.json | 114 + .../doctype/web_page_view/web_page_view.py | 106 + .../website/doctype/web_template/__init__.py | 0 .../doctype/web_template/test_web_template.py | 115 + .../doctype/web_template/web_template.js | 25 + .../doctype/web_template/web_template.json | 81 + .../doctype/web_template/web_template.py | 130 + .../doctype/web_template_field/__init__.py | 0 .../test_web_template_field.py | 8 + .../web_template_field/web_template_field.js | 7 + .../web_template_field.json | 67 + .../web_template_field/web_template_field.py | 40 + .../doctype/website_meta_tag/__init__.py | 0 .../website_meta_tag/website_meta_tag.json | 40 + .../website_meta_tag/website_meta_tag.py | 34 + .../doctype/website_route_meta/__init__.py | 0 .../test_website_route_meta.py | 38 + .../website_route_meta/website_route_meta.js | 15 + .../website_route_meta.json | 43 + .../website_route_meta/website_route_meta.py | 22 + .../website_route_redirect/__init__.py | 0 .../website_route_redirect.json | 35 + .../website_route_redirect.py | 23 + .../website/doctype/website_script/README.md | 1 + .../doctype/website_script/__init__.py | 0 .../doctype/website_script/website_script.js | 6 + .../website_script/website_script.json | 43 + .../doctype/website_script/website_script.py | 28 + .../doctype/website_settings/README.md | 1 + .../doctype/website_settings/__init__.py | 0 .../website_settings/google_indexing.py | 57 + .../website_settings/test_website_settings.py | 35 + .../website_settings/website_settings.js | 151 + .../website_settings/website_settings.json | 511 ++ .../website_settings/website_settings.py | 300 + .../doctype/website_sidebar/__init__.py | 0 .../website_sidebar/test_website_sidebar.py | 10 + .../website_sidebar/website_sidebar.js | 6 + .../website_sidebar/website_sidebar.json | 56 + .../website_sidebar/website_sidebar.py | 40 + .../doctype/website_sidebar_item/__init__.py | 0 .../website_sidebar_item.json | 46 + .../website_sidebar_item.py | 24 + .../doctype/website_slideshow/README.md | 1 + .../doctype/website_slideshow/__init__.py | 0 .../test_website_slideshow.py | 10 + .../website_slideshow/website_slideshow.js | 59 + .../website_slideshow/website_slideshow.json | 71 + .../website_slideshow/website_slideshow.py | 55 + .../doctype/website_slideshow_item/README.md | 1 + .../website_slideshow_item/__init__.py | 0 .../website_slideshow_item.json | 56 + .../website_slideshow_item.py | 27 + .../website/doctype/website_theme/__init__.py | 0 .../doctype/website_theme/custom_theme.css | 17 + .../website_theme/test_website_theme.py | 53 + .../doctype/website_theme/website_theme.js | 103 + .../doctype/website_theme/website_theme.json | 212 + .../doctype/website_theme/website_theme.py | 202 + .../website_theme/website_theme_template.scss | 62 + .../website_theme_ignore_app/__init__.py | 0 .../website_theme_ignore_app.json | 32 + .../website_theme_ignore_app.py | 22 + xhiveframework/website/js/bootstrap-4.js | 65 + xhiveframework/website/js/syntax_highlight.js | 18 + xhiveframework/website/js/website.js | 650 ++ .../module_onboarding/website/website.json | 38 + .../add_blog_category/add_blog_category.json | 19 + .../create_blogger/create_blogger.json | 19 + .../enable_website_tracking.json | 21 + .../introduction_to_website.json | 19 + .../web_page_tour/web_page_tour.json | 19 + .../website/page_renderers/base_renderer.py | 27 + .../page_renderers/base_template_page.py | 75 + .../website/page_renderers/document_page.py | 108 + .../website/page_renderers/error_page.py | 17 + .../website/page_renderers/list_page.py | 12 + .../website/page_renderers/not_found_page.py | 35 + .../page_renderers/not_permitted_page.py | 25 + .../website/page_renderers/print_page.py | 24 + .../website/page_renderers/redirect_page.py | 22 + .../website/page_renderers/static_page.py | 55 + .../website/page_renderers/template_page.py | 313 + .../website/page_renderers/web_form.py | 13 + xhiveframework/website/path_resolver.py | 189 + xhiveframework/website/report/__init__.py | 0 .../report/website_analytics/__init__.py | 0 .../website_analytics/website_analytics.js | 64 + .../website_analytics/website_analytics.json | 27 + .../website_analytics/website_analytics.py | 196 + xhiveframework/website/router.py | 329 + xhiveframework/website/serve.py | 47 + xhiveframework/website/utils.py | 593 ++ xhiveframework/website/web_form/__init__.py | 0 .../website/web_form/request_data/__init__.py | 0 .../web_form/request_data/request_data.js | 3 + .../web_form/request_data/request_data.json | 52 + .../web_form/request_data/request_data.py | 2 + .../request_to_delete_data/__init__.py | 0 .../request_to_delete_data.js | 18 + .../request_to_delete_data.json | 53 + .../request_to_delete_data.py | 3 + .../website/web_template/__init__.py | 0 .../web_template/cover_image/__init__.py | 0 .../web_template/cover_image/cover_image.html | 5 + .../web_template/cover_image/cover_image.json | 34 + .../web_template/discussions/__init__.py | 0 .../web_template/discussions/discussions.html | 6 + .../web_template/discussions/discussions.json | 43 + .../web_template/full_width_image/__init__.py | 0 .../full_width_image/full_width_image.html | 5 + .../full_width_image/full_width_image.json | 29 + .../website/web_template/hero/__init__.py | 0 .../website/web_template/hero/hero.html | 24 + .../website/web_template/hero/hero.json | 61 + .../hero_with_right_image/__init__.py | 0 .../hero_with_right_image.html | 33 + .../hero_with_right_image.json | 63 + .../website/web_template/markdown/__init__.py | 0 .../web_template/markdown/markdown.html | 5 + .../web_template/markdown/markdown.json | 30 + .../web_template/primary_navbar/__init__.py | 0 .../primary_navbar/primary_navbar.html | 29 + .../primary_navbar/primary_navbar.json | 17 + .../section_with_cards/__init__.py | 0 .../section_with_cards.html | 50 + .../section_with_cards.json | 305 + .../__init__.py | 0 .../section_with_collapsible_content.html | 42 + .../section_with_collapsible_content.json | 54 + .../web_template/section_with_cta/__init__.py | 0 .../section_with_cta/section_with_cta.html | 26 + .../section_with_cta/section_with_cta.json | 50 + .../section_with_embed/__init__.py | 0 .../section_with_embed.html | 10 + .../section_with_embed.json | 32 + .../section_with_features/__init__.py | 0 .../section_with_features.html | 32 + .../section_with_features.json | 65 + .../section_with_image/__init__.py | 0 .../section_with_image.html | 14 + .../section_with_image.json | 47 + .../section_with_image_grid/__init__.py | 0 .../section_with_image_grid.html | 18 + .../section_with_image_grid.json | 51 + .../section_with_small_cta/__init__.py | 0 .../section_with_small_cta.html | 20 + .../section_with_small_cta.json | 38 + .../section_with_tabs/__init__.py | 0 .../section_with_tabs/section_with_tabs.html | 52 + .../section_with_tabs/section_with_tabs.json | 98 + .../section_with_testimonials/__init__.py | 0 .../section_with_testimonials.html | 31 + .../section_with_testimonials.json | 73 + .../section_with_videos/__init__.py | 0 .../section_with_videos.html | 24 + .../section_with_videos.json | 61 + .../web_template/slideshow/__init__.py | 0 .../web_template/slideshow/slideshow.html | 55 + .../web_template/slideshow/slideshow.json | 21 + .../split_section_with_image/__init__.py | 0 .../split_section_with_image.html | 36 + .../split_section_with_image.json | 67 + .../web_template/standard_footer/__init__.py | 0 .../standard_footer/standard_footer.html | 1 + .../standard_footer/standard_footer.json | 13 + .../web_template/standard_navbar/__init__.py | 0 .../standard_navbar/standard_navbar.html | 1 + .../standard_navbar/standard_navbar.json | 13 + .../web_template/testimonial/__init__.py | 0 .../web_template/testimonial/testimonial.html | 13 + .../web_template/testimonial/testimonial.json | 32 + .../website/website_components/metatags.py | 71 + xhiveframework/website/website_generator.py | 177 + .../website/website_theme/__init__.py | 0 .../website_theme/standard/__init__.py | 0 .../website_theme/standard/standard.json | 20 + .../website/workspace/website/website.json | 286 + xhiveframework/workflow/__init__.py | 0 xhiveframework/workflow/doctype/__init__.py | 0 .../workflow/doctype/workflow/README.md | 1 + .../workflow/doctype/workflow/__init__.py | 2 + .../doctype/workflow/test_records.json | 1 + .../doctype/workflow/test_workflow.py | 208 + .../workflow/doctype/workflow/workflow.js | 270 + .../workflow/doctype/workflow/workflow.json | 129 + .../workflow/doctype/workflow/workflow.py | 134 + .../doctype/workflow/workflow_list.js | 26 + .../doctype/workflow_action/README.md | 1 + .../doctype/workflow_action/__init__.py | 2 + .../workflow_action/test_workflow_action.py | 9 + .../workflow_action/workflow_action.js | 6 + .../workflow_action/workflow_action.json | 99 + .../workflow_action/workflow_action.py | 508 ++ .../workflow_action/workflow_action_list.js | 16 + .../workflow_action_master/__init__.py | 0 .../workflow_action_master.js | 6 + .../workflow_action_master.json | 47 + .../workflow_action_master.py | 18 + .../__init__.py | 0 .../workflow_action_permitted_role.json | 33 + .../workflow_action_permitted_role.py | 22 + .../doctype/workflow_document_state/README.md | 1 + .../workflow_document_state/__init__.py | 2 + .../workflow_document_state.json | 123 + .../workflow_document_state.py | 31 + .../workflow/doctype/workflow_state/README.md | 1 + .../doctype/workflow_state/__init__.py | 2 + .../doctype/workflow_state/test_records.json | 1 + .../workflow_state/test_workflow_state.py | 5 + .../doctype/workflow_state/workflow_state.js | 6 + .../workflow_state/workflow_state.json | 72 + .../doctype/workflow_state/workflow_state.py | 163 + .../doctype/workflow_transition/README.md | 1 + .../doctype/workflow_transition/__init__.py | 2 + .../workflow_transition.json | 107 + .../workflow_transition.py | 27 + xhiveframework/workflow/page/__init__.py | 0 .../page/workflow_builder/__init__.py | 0 .../page/workflow_builder/workflow_builder.js | 108 + .../workflow_builder/workflow_builder.json | 23 + xhiveframework/www/404.html | 24 + xhiveframework/www/404.py | 6 + xhiveframework/www/__init__.py | 0 xhiveframework/www/_test/__init__.py | 0 xhiveframework/www/_test/_sidebar.json | 6 + .../www/_test/_test_custom_base.html | 4 + .../www/_test/_test_folder/__init__.py | 0 .../www/_test/_test_folder/_test_page.css | 3 + .../www/_test/_test_folder/_test_page.html | 6 + .../www/_test/_test_folder/_test_page.js | 1 + .../www/_test/_test_folder/_test_page.py | 3 + .../www/_test/_test_folder/_test_toc.md | 19 + .../www/_test/_test_folder/index.md | 9 + .../_test/_test_folder/new.csv/__init__.py | 0 .../www/_test/_test_folder/new.csv/index.html | 12 + xhiveframework/www/_test/_test_home_page.py | 2 + xhiveframework/www/_test/_test_metatags.html | 5 + xhiveframework/www/_test/_test_metatags.py | 6 + .../www/_test/_test_no_context.html | 1 + xhiveframework/www/_test/_test_no_context.py | 8 + .../www/_test/_test_safe_render_off.html | 7 + .../www/_test/_test_safe_render_on.html | 6 + xhiveframework/www/_test/_test_webform.py | 6 + xhiveframework/www/_test/assets/__init__.py | 0 xhiveframework/www/_test/assets/css_asset.css | 1 + xhiveframework/www/_test/assets/file.zip | Bin 0 -> 156164 bytes xhiveframework/www/_test/assets/image | Bin 0 -> 161713 bytes xhiveframework/www/_test/assets/image.jpg | Bin 0 -> 161713 bytes .../www/_test/assets/js_asset.min.js | 2 + xhiveframework/www/_test/index.html | 1 + .../www/_test/problematic_page.html | 1 + xhiveframework/www/_test/static-file-test.png | Bin 0 -> 440 bytes xhiveframework/www/about.html | 62 + xhiveframework/www/about.py | 12 + xhiveframework/www/app.html | 73 + xhiveframework/www/app.py | 74 + xhiveframework/www/complete_signup.html | 24 + xhiveframework/www/complete_signup.py | 2 + .../www/confirm_workflow_action.html | 16 + xhiveframework/www/contact.html | 87 + xhiveframework/www/contact.py | 56 + xhiveframework/www/error.html | 64 + xhiveframework/www/error.py | 20 + xhiveframework/www/list.html | 33 + xhiveframework/www/list.py | 248 + xhiveframework/www/login.html | 225 + xhiveframework/www/login.py | 178 + xhiveframework/www/me.html | 82 + xhiveframework/www/me.py | 16 + xhiveframework/www/message.html | 51 + xhiveframework/www/message.py | 35 + xhiveframework/www/modified_doc_alert.html | 19 + xhiveframework/www/printpreview.html | 10 + xhiveframework/www/printview.html | 52 + xhiveframework/www/printview.py | 672 ++ xhiveframework/www/profile.py | 9 + xhiveframework/www/qrcode.html | 27 + xhiveframework/www/qrcode.py | 40 + xhiveframework/www/robots.py | 13 + xhiveframework/www/robots.txt | 1 + xhiveframework/www/rss.py | 47 + xhiveframework/www/rss.xml | 18 + xhiveframework/www/search.html | 25 + xhiveframework/www/search.py | 63 + xhiveframework/www/sitemap.py | 80 + xhiveframework/www/sitemap.xml | 9 + xhiveframework/www/third_party_apps.html | 95 + xhiveframework/www/third_party_apps.py | 63 + xhiveframework/www/unsubscribe.html | 111 + xhiveframework/www/unsubscribe.py | 47 + xhiveframework/www/update-password.html | 324 + xhiveframework/www/update_password.py | 10 + xhiveframework/www/website_script.js | 43 + xhiveframework/www/website_script.py | 28 + xhiveframework/xhiveframeworkclient.py | 398 ++ yarn.lock | 3629 ++++++++++ 3088 files changed, 655079 insertions(+) create mode 100644 .coveragerc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .git-blame-ignore-revs create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/question-about-using-frappe.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/frappe-framework-logo.svg create mode 100644 .github/helper/db/mariadb.json create mode 100644 .github/helper/db/postgres.json create mode 100644 .github/helper/documentation.py create mode 100644 .github/helper/install.sh create mode 100644 .github/helper/install_dependencies.sh create mode 100644 .github/helper/roulette.py create mode 100644 .github/labeler.yml create mode 100644 .github/stale.yml create mode 100644 .github/try-on-f-cloud-button.svg create mode 100644 .github/workflows/backport.yml create mode 100644 .github/workflows/create-release.yml create mode 100644 .github/workflows/initiate_release.yml create mode 100644 .github/workflows/labeller.yml create mode 100644 .github/workflows/linters.yml create mode 100644 .github/workflows/lock.yml create mode 100644 .github/workflows/on_release.yml create mode 100644 .github/workflows/patch-mariadb-tests.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .github/workflows/publish-assets-develop.yml create mode 100644 .github/workflows/release_notes.yml create mode 100644 .github/workflows/server-tests.yml create mode 100644 .github/workflows/ui-tests.yml create mode 100644 .gitignore create mode 100644 .mergify.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .releaserc create mode 100644 .semgrepignore create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 attributions.md create mode 100644 codecov.yml create mode 100644 commitlint.config.js create mode 100644 cypress.config.js create mode 100644 cypress/fixtures/child_table_doctype.js create mode 100644 cypress/fixtures/child_table_doctype_1.js create mode 100644 cypress/fixtures/custom_submittable_doctype.js create mode 100644 cypress/fixtures/data_field_validation_doctype.js create mode 100644 cypress/fixtures/datetime_doctype.js create mode 100644 cypress/fixtures/doctype_to_link.js create mode 100644 cypress/fixtures/doctype_with_child_table.js create mode 100644 cypress/fixtures/doctype_with_phone.js create mode 100644 cypress/fixtures/doctype_with_tab_break.js create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/fixtures/form_builder_doctype.js create mode 100644 cypress/fixtures/sample_attachments/attachment-1.jpg create mode 100644 cypress/fixtures/sample_attachments/attachment-10.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-11.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-2.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-3.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-4.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-5.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-6.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-7.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-8.txt create mode 100644 cypress/fixtures/sample_attachments/attachment-9.txt create mode 100644 cypress/fixtures/sample_image.jpg create mode 100644 cypress/integration/api.js create mode 100644 cypress/integration/assignment_rule.js create mode 100644 cypress/integration/awesome_bar.js create mode 100644 cypress/integration/control_attach.js create mode 100644 cypress/integration/control_autocomplete.js create mode 100644 cypress/integration/control_barcode.js create mode 100644 cypress/integration/control_color.js create mode 100644 cypress/integration/control_currency.js create mode 100644 cypress/integration/control_data.js create mode 100644 cypress/integration/control_date.js create mode 100644 cypress/integration/control_date_range.js create mode 100644 cypress/integration/control_duration.js create mode 100644 cypress/integration/control_dynamic_link.js create mode 100644 cypress/integration/control_float.js create mode 100644 cypress/integration/control_icon.js create mode 100644 cypress/integration/control_link.js create mode 100644 cypress/integration/control_markdown_editor.js create mode 100644 cypress/integration/control_phone.js create mode 100644 cypress/integration/control_rating.js create mode 100644 cypress/integration/control_select.js create mode 100644 cypress/integration/custom_buttons.js create mode 100644 cypress/integration/customize_form.js create mode 100644 cypress/integration/dashboard.js create mode 100644 cypress/integration/dashboard_chart.js create mode 100644 cypress/integration/dashboard_links.js create mode 100644 cypress/integration/data_field_form_validation.js create mode 100644 cypress/integration/datetime.js create mode 100644 cypress/integration/datetime_field_form_validation.js create mode 100644 cypress/integration/depends_on.js create mode 100644 cypress/integration/discussions.js create mode 100644 cypress/integration/file_uploader.js create mode 100644 cypress/integration/first_day_of_the_week.js create mode 100644 cypress/integration/folder_navigation.js create mode 100644 cypress/integration/form.js create mode 100644 cypress/integration/form_builder.js create mode 100644 cypress/integration/form_tab_break.js create mode 100644 cypress/integration/form_tour.js create mode 100644 cypress/integration/grid.js create mode 100644 cypress/integration/grid_configuration.js create mode 100644 cypress/integration/grid_keyboard_shortcut.js create mode 100644 cypress/integration/grid_pagination.js create mode 100644 cypress/integration/grid_search.js create mode 100644 cypress/integration/kanban.js create mode 100644 cypress/integration/list_paging.js create mode 100644 cypress/integration/list_view.js create mode 100644 cypress/integration/list_view_drag_select.js create mode 100644 cypress/integration/list_view_settings.js create mode 100644 cypress/integration/login.js create mode 100644 cypress/integration/multi_select_dialog.js create mode 100644 cypress/integration/navigation.js create mode 100644 cypress/integration/number_card.js create mode 100644 cypress/integration/permissions.js create mode 100644 cypress/integration/query_report.js create mode 100644 cypress/integration/recorder.js create mode 100644 cypress/integration/relative_time_filters.js create mode 100644 cypress/integration/report_view.js create mode 100644 cypress/integration/rounding.js create mode 100644 cypress/integration/routing.js create mode 100644 cypress/integration/sidebar.js create mode 100644 cypress/integration/socket_updates.js create mode 100644 cypress/integration/table_multiselect.js create mode 100644 cypress/integration/url_data_field.js create mode 100644 cypress/integration/view_routing.js create mode 100644 cypress/integration/web_form.js create mode 100644 cypress/integration/workspace.js create mode 100644 cypress/integration/workspace_blocks.js create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/e2e.js create mode 100644 cypress/tsconfig.json create mode 100644 esbuild/build-cleanup.js create mode 100644 esbuild/esbuild.js create mode 100644 esbuild/ignore-assets.js create mode 100644 esbuild/index.js create mode 100644 esbuild/sass_options.js create mode 100644 esbuild/utils.js create mode 100644 esbuild/xhiveframework-html.js create mode 100644 esbuild/xhiveframework-vue-style.js create mode 100644 generate_bootstrap_theme.js create mode 100644 hooks.md create mode 100644 node_utils.js create mode 100644 package.json create mode 100644 pyproject.toml create mode 100644 realtime/handlers/xhiveframework_handlers.js create mode 100644 realtime/index.js create mode 100644 realtime/middlewares/authenticate.js create mode 100644 realtime/utils.js create mode 100644 sider.yml create mode 100644 socketio.js create mode 100644 xhiveframework/__init__.py create mode 100644 xhiveframework/api/__init__.py create mode 100644 xhiveframework/api/utils.py create mode 100644 xhiveframework/api/v1.py create mode 100644 xhiveframework/api/v2.py create mode 100644 xhiveframework/app.py create mode 100644 xhiveframework/auth.py create mode 100644 xhiveframework/automation/__init__.py create mode 100644 xhiveframework/automation/doctype/__init__.py create mode 100644 xhiveframework/automation/doctype/assignment_rule/__init__.py create mode 100644 xhiveframework/automation/doctype/assignment_rule/assignment_rule.js create mode 100644 xhiveframework/automation/doctype/assignment_rule/assignment_rule.json create mode 100644 xhiveframework/automation/doctype/assignment_rule/assignment_rule.py create mode 100644 xhiveframework/automation/doctype/assignment_rule/test_assignment_rule.py create mode 100644 xhiveframework/automation/doctype/assignment_rule_day/__init__.py create mode 100644 xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.json create mode 100644 xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.py create mode 100644 xhiveframework/automation/doctype/assignment_rule_user/__init__.py create mode 100644 xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.json create mode 100644 xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.py create mode 100644 xhiveframework/automation/doctype/auto_repeat/__init__.py create mode 100644 xhiveframework/automation/doctype/auto_repeat/auto_repeat.js create mode 100644 xhiveframework/automation/doctype/auto_repeat/auto_repeat.json create mode 100644 xhiveframework/automation/doctype/auto_repeat/auto_repeat.py create mode 100644 xhiveframework/automation/doctype/auto_repeat/auto_repeat_list.js create mode 100644 xhiveframework/automation/doctype/auto_repeat/auto_repeat_schedule.html create mode 100644 xhiveframework/automation/doctype/auto_repeat/test_auto_repeat.py create mode 100644 xhiveframework/automation/doctype/auto_repeat_day/__init__.py create mode 100644 xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.json create mode 100644 xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.py create mode 100644 xhiveframework/automation/doctype/milestone/__init__.py create mode 100644 xhiveframework/automation/doctype/milestone/milestone.js create mode 100644 xhiveframework/automation/doctype/milestone/milestone.json create mode 100644 xhiveframework/automation/doctype/milestone/milestone.py create mode 100644 xhiveframework/automation/doctype/milestone/test_milestone.py create mode 100644 xhiveframework/automation/doctype/milestone_tracker/__init__.py create mode 100644 xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.js create mode 100644 xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.json create mode 100644 xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.py create mode 100644 xhiveframework/automation/doctype/milestone_tracker/test_milestone_tracker.py create mode 100644 xhiveframework/automation/doctype/reminder/__init__.py create mode 100644 xhiveframework/automation/doctype/reminder/reminder.js create mode 100644 xhiveframework/automation/doctype/reminder/reminder.json create mode 100644 xhiveframework/automation/doctype/reminder/reminder.py create mode 100644 xhiveframework/automation/doctype/reminder/test_reminder.py create mode 100644 xhiveframework/automation/workspace/tools/tools.json create mode 100644 xhiveframework/boot.py create mode 100644 xhiveframework/build.py create mode 100644 xhiveframework/cache_manager.py create mode 100644 xhiveframework/change_log/__init__.py create mode 100644 xhiveframework/change_log/current/readme.md create mode 100644 xhiveframework/change_log/v10/v10_0_0.md create mode 100644 xhiveframework/change_log/v11/v11_1_0.md create mode 100644 xhiveframework/change_log/v12/v12_0_0.md create mode 100644 xhiveframework/change_log/v13/v13_0_0.md create mode 100644 xhiveframework/change_log/v13/v13_1_0.md create mode 100644 xhiveframework/change_log/v13/v13_2_0.md create mode 100644 xhiveframework/change_log/v13/v13_3_0.md create mode 100644 xhiveframework/change_log/v5/v5_0_18.md create mode 100644 xhiveframework/change_log/v5/v5_0_20.md create mode 100644 xhiveframework/change_log/v5/v5_0_32.md create mode 100644 xhiveframework/change_log/v5/v5_1_0.md create mode 100644 xhiveframework/change_log/v5/v5_1_1.md create mode 100644 xhiveframework/change_log/v5/v5_3_0.md create mode 100644 xhiveframework/change_log/v5/v5_4_0.md create mode 100644 xhiveframework/change_log/v6/v6_0_0.md create mode 100644 xhiveframework/change_log/v6/v6_0_8.md create mode 100644 xhiveframework/change_log/v6/v6_12_0.md create mode 100644 xhiveframework/change_log/v6/v6_13_0.md create mode 100644 xhiveframework/change_log/v6/v6_14_1.md create mode 100644 xhiveframework/change_log/v6/v6_15_0.md create mode 100644 xhiveframework/change_log/v6/v6_16_1.md create mode 100644 xhiveframework/change_log/v6/v6_16_4.md create mode 100644 xhiveframework/change_log/v6/v6_17_0.md create mode 100644 xhiveframework/change_log/v6/v6_1_0.md create mode 100644 xhiveframework/change_log/v6/v6_20_0.md create mode 100644 xhiveframework/change_log/v6/v6_21_0.md create mode 100644 xhiveframework/change_log/v6/v6_22_0.md create mode 100644 xhiveframework/change_log/v6/v6_23_0.md create mode 100644 xhiveframework/change_log/v6/v6_25_0.md create mode 100644 xhiveframework/change_log/v6/v6_26_0.md create mode 100644 xhiveframework/change_log/v6/v6_26_6.md create mode 100644 xhiveframework/change_log/v6/v6_27_1.md create mode 100644 xhiveframework/change_log/v6/v6_27_11.md create mode 100644 xhiveframework/change_log/v6/v6_2_0.md create mode 100644 xhiveframework/change_log/v6/v6_3_0.md create mode 100644 xhiveframework/change_log/v6/v6_4_0.md create mode 100644 xhiveframework/change_log/v6/v6_4_8.md create mode 100644 xhiveframework/change_log/v6/v6_5_0.md create mode 100644 xhiveframework/change_log/v6/v6_6_0.md create mode 100644 xhiveframework/change_log/v6/v6_7_0.md create mode 100644 xhiveframework/change_log/v6/v6_8_0.md create mode 100644 xhiveframework/change_log/v7/v7_0_0.md create mode 100644 xhiveframework/change_log/v7/v7_0_18.md create mode 100644 xhiveframework/change_log/v7/v7_1_0.md create mode 100644 xhiveframework/change_log/v7/v7_2_0.md create mode 100644 xhiveframework/change_log/v8/v8_0_0.md create mode 100644 xhiveframework/change_log/v8/v8_7_0.md create mode 100644 xhiveframework/change_log/v8/v8_8_0.md create mode 100644 xhiveframework/client.py create mode 100644 xhiveframework/commands/__init__.py create mode 100644 xhiveframework/commands/redis_utils.py create mode 100755 xhiveframework/commands/scheduler.py create mode 100644 xhiveframework/commands/site.py create mode 100644 xhiveframework/commands/translate.py create mode 100644 xhiveframework/commands/utils.py create mode 100644 xhiveframework/config/__init__.py create mode 100644 xhiveframework/contacts/__init__.py create mode 100644 xhiveframework/contacts/address_and_contact.py create mode 100644 xhiveframework/contacts/doctype/__init__.py create mode 100644 xhiveframework/contacts/doctype/address/__init__.py create mode 100644 xhiveframework/contacts/doctype/address/address.js create mode 100644 xhiveframework/contacts/doctype/address/address.json create mode 100644 xhiveframework/contacts/doctype/address/address.py create mode 100644 xhiveframework/contacts/doctype/address/test_address.py create mode 100644 xhiveframework/contacts/doctype/address_template/__init__.py create mode 100644 xhiveframework/contacts/doctype/address_template/address_template.jinja create mode 100644 xhiveframework/contacts/doctype/address_template/address_template.js create mode 100644 xhiveframework/contacts/doctype/address_template/address_template.json create mode 100644 xhiveframework/contacts/doctype/address_template/address_template.py create mode 100644 xhiveframework/contacts/doctype/address_template/test_address_template.py create mode 100644 xhiveframework/contacts/doctype/contact/__init__.py create mode 100644 xhiveframework/contacts/doctype/contact/contact.js create mode 100644 xhiveframework/contacts/doctype/contact/contact.json create mode 100644 xhiveframework/contacts/doctype/contact/contact.py create mode 100644 xhiveframework/contacts/doctype/contact/contact_list.js create mode 100644 xhiveframework/contacts/doctype/contact/test_contact.py create mode 100644 xhiveframework/contacts/doctype/contact/test_records.json create mode 100644 xhiveframework/contacts/doctype/contact_email/__init__.py create mode 100644 xhiveframework/contacts/doctype/contact_email/contact_email.json create mode 100644 xhiveframework/contacts/doctype/contact_email/contact_email.py create mode 100644 xhiveframework/contacts/doctype/contact_phone/__init__.py create mode 100644 xhiveframework/contacts/doctype/contact_phone/contact_phone.json create mode 100644 xhiveframework/contacts/doctype/contact_phone/contact_phone.py create mode 100644 xhiveframework/contacts/doctype/gender/__init__.py create mode 100644 xhiveframework/contacts/doctype/gender/gender.js create mode 100644 xhiveframework/contacts/doctype/gender/gender.json create mode 100644 xhiveframework/contacts/doctype/gender/gender.py create mode 100644 xhiveframework/contacts/doctype/gender/test_gender.py create mode 100644 xhiveframework/contacts/doctype/salutation/__init__.py create mode 100644 xhiveframework/contacts/doctype/salutation/salutation.js create mode 100644 xhiveframework/contacts/doctype/salutation/salutation.json create mode 100644 xhiveframework/contacts/doctype/salutation/salutation.py create mode 100644 xhiveframework/contacts/doctype/salutation/test_records.json create mode 100644 xhiveframework/contacts/doctype/salutation/test_salutation.py create mode 100644 xhiveframework/contacts/report/__init__.py create mode 100644 xhiveframework/contacts/report/addresses_and_contacts/__init__.py create mode 100644 xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.js create mode 100644 xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.json create mode 100644 xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.py create mode 100644 xhiveframework/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py create mode 100644 xhiveframework/core/README.md create mode 100644 xhiveframework/core/__init__.py create mode 100644 xhiveframework/core/api/__init__.py create mode 100644 xhiveframework/core/api/file.py create mode 100644 xhiveframework/core/doctype/__init__.py create mode 100644 xhiveframework/core/doctype/access_log/__init__.py create mode 100644 xhiveframework/core/doctype/access_log/access_log.js create mode 100644 xhiveframework/core/doctype/access_log/access_log.json create mode 100644 xhiveframework/core/doctype/access_log/access_log.py create mode 100644 xhiveframework/core/doctype/access_log/access_log_list.js create mode 100644 xhiveframework/core/doctype/access_log/test_access_log.py create mode 100644 xhiveframework/core/doctype/activity_log/__init__.py create mode 100644 xhiveframework/core/doctype/activity_log/activity_log.js create mode 100644 xhiveframework/core/doctype/activity_log/activity_log.json create mode 100644 xhiveframework/core/doctype/activity_log/activity_log.py create mode 100644 xhiveframework/core/doctype/activity_log/activity_log_list.js create mode 100644 xhiveframework/core/doctype/activity_log/feed.py create mode 100644 xhiveframework/core/doctype/activity_log/test_activity_log.py create mode 100644 xhiveframework/core/doctype/amended_document_naming_settings/__init__.py create mode 100644 xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.json create mode 100644 xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.py create mode 100644 xhiveframework/core/doctype/audit_trail/__init__.py create mode 100644 xhiveframework/core/doctype/audit_trail/audit_trail.html create mode 100644 xhiveframework/core/doctype/audit_trail/audit_trail.js create mode 100644 xhiveframework/core/doctype/audit_trail/audit_trail.json create mode 100644 xhiveframework/core/doctype/audit_trail/audit_trail.py create mode 100644 xhiveframework/core/doctype/audit_trail/audit_trail_rows_added_removed.html create mode 100644 xhiveframework/core/doctype/audit_trail/test_audit_trail.py create mode 100644 xhiveframework/core/doctype/block_module/__init__.py create mode 100644 xhiveframework/core/doctype/block_module/block_module.json create mode 100644 xhiveframework/core/doctype/block_module/block_module.py create mode 100644 xhiveframework/core/doctype/comment/__init__.py create mode 100644 xhiveframework/core/doctype/comment/comment.js create mode 100644 xhiveframework/core/doctype/comment/comment.json create mode 100644 xhiveframework/core/doctype/comment/comment.py create mode 100644 xhiveframework/core/doctype/comment/test_comment.py create mode 100644 xhiveframework/core/doctype/communication/README.md create mode 100644 xhiveframework/core/doctype/communication/__init__.py create mode 100644 xhiveframework/core/doctype/communication/communication.js create mode 100644 xhiveframework/core/doctype/communication/communication.json create mode 100644 xhiveframework/core/doctype/communication/communication.py create mode 100644 xhiveframework/core/doctype/communication/communication_list.js create mode 100755 xhiveframework/core/doctype/communication/email.py create mode 100644 xhiveframework/core/doctype/communication/mixins.py create mode 100644 xhiveframework/core/doctype/communication/test_communication.py create mode 100644 xhiveframework/core/doctype/communication/test_records.json create mode 100644 xhiveframework/core/doctype/communication_link/__init__.py create mode 100644 xhiveframework/core/doctype/communication_link/communication_link.json create mode 100644 xhiveframework/core/doctype/communication_link/communication_link.py create mode 100644 xhiveframework/core/doctype/custom_docperm/__init__.py create mode 100644 xhiveframework/core/doctype/custom_docperm/custom_docperm.js create mode 100644 xhiveframework/core/doctype/custom_docperm/custom_docperm.json create mode 100644 xhiveframework/core/doctype/custom_docperm/custom_docperm.py create mode 100644 xhiveframework/core/doctype/custom_docperm/test_custom_docperm.py create mode 100644 xhiveframework/core/doctype/custom_role/__init__.py create mode 100644 xhiveframework/core/doctype/custom_role/custom_role.js create mode 100644 xhiveframework/core/doctype/custom_role/custom_role.json create mode 100644 xhiveframework/core/doctype/custom_role/custom_role.py create mode 100644 xhiveframework/core/doctype/custom_role/test_custom_role.py create mode 100644 xhiveframework/core/doctype/data_export/__init__.py create mode 100644 xhiveframework/core/doctype/data_export/data_export.js create mode 100644 xhiveframework/core/doctype/data_export/data_export.json create mode 100644 xhiveframework/core/doctype/data_export/data_export.py create mode 100644 xhiveframework/core/doctype/data_export/exporter.py create mode 100644 xhiveframework/core/doctype/data_export/test_data_exporter.py create mode 100644 xhiveframework/core/doctype/data_import/__init__.py create mode 100644 xhiveframework/core/doctype/data_import/data_import.css create mode 100644 xhiveframework/core/doctype/data_import/data_import.js create mode 100644 xhiveframework/core/doctype/data_import/data_import.json create mode 100644 xhiveframework/core/doctype/data_import/data_import.py create mode 100644 xhiveframework/core/doctype/data_import/data_import_list.js create mode 100644 xhiveframework/core/doctype/data_import/exporter.py create mode 100644 xhiveframework/core/doctype/data_import/fixtures/sample_import_file.csv create mode 100644 xhiveframework/core/doctype/data_import/fixtures/sample_import_file_for_update.csv create mode 100644 xhiveframework/core/doctype/data_import/fixtures/sample_import_file_without_mandatory.csv create mode 100644 xhiveframework/core/doctype/data_import/importer.py create mode 100644 xhiveframework/core/doctype/data_import/patches/__init__.py create mode 100644 xhiveframework/core/doctype/data_import/patches/remove_stale_docfields_from_legacy_version.py create mode 100644 xhiveframework/core/doctype/data_import/test_data_import.py create mode 100644 xhiveframework/core/doctype/data_import/test_exporter.py create mode 100644 xhiveframework/core/doctype/data_import/test_importer.py create mode 100644 xhiveframework/core/doctype/data_import_log/__init__.py create mode 100644 xhiveframework/core/doctype/data_import_log/data_import_log.js create mode 100644 xhiveframework/core/doctype/data_import_log/data_import_log.json create mode 100644 xhiveframework/core/doctype/data_import_log/data_import_log.py create mode 100644 xhiveframework/core/doctype/data_import_log/test_data_import_log.py create mode 100644 xhiveframework/core/doctype/defaultvalue/README.md create mode 100644 xhiveframework/core/doctype/defaultvalue/__init__.py create mode 100644 xhiveframework/core/doctype/defaultvalue/defaultvalue.json create mode 100644 xhiveframework/core/doctype/defaultvalue/defaultvalue.py create mode 100644 xhiveframework/core/doctype/deleted_document/__init__.py create mode 100644 xhiveframework/core/doctype/deleted_document/deleted_document.js create mode 100644 xhiveframework/core/doctype/deleted_document/deleted_document.json create mode 100644 xhiveframework/core/doctype/deleted_document/deleted_document.py create mode 100644 xhiveframework/core/doctype/deleted_document/deleted_document_list.js create mode 100644 xhiveframework/core/doctype/deleted_document/test_deleted_document.py create mode 100644 xhiveframework/core/doctype/docfield/README.md create mode 100644 xhiveframework/core/doctype/docfield/__init__.py create mode 100644 xhiveframework/core/doctype/docfield/docfield.json create mode 100644 xhiveframework/core/doctype/docfield/docfield.py create mode 100644 xhiveframework/core/doctype/docperm/__init__.py create mode 100644 xhiveframework/core/doctype/docperm/docperm.json create mode 100644 xhiveframework/core/doctype/docperm/docperm.py create mode 100644 xhiveframework/core/doctype/docshare/__init__.py create mode 100644 xhiveframework/core/doctype/docshare/docshare.js create mode 100644 xhiveframework/core/doctype/docshare/docshare.json create mode 100644 xhiveframework/core/doctype/docshare/docshare.py create mode 100644 xhiveframework/core/doctype/docshare/test_docshare.py create mode 100644 xhiveframework/core/doctype/docshare/test_records.json create mode 100644 xhiveframework/core/doctype/doctype/README.md create mode 100644 xhiveframework/core/doctype/doctype/__init__.py create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/__init__.py create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/controller._py create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/controller.js create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/controller_list.html create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/controller_list.js create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/templates/controller.html create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/templates/controller_row.html create mode 100644 xhiveframework/core/doctype/doctype/boilerplate/test_controller._py create mode 100644 xhiveframework/core/doctype/doctype/doctype.js create mode 100644 xhiveframework/core/doctype/doctype/doctype.json create mode 100644 xhiveframework/core/doctype/doctype/doctype.py create mode 100644 xhiveframework/core/doctype/doctype/doctype_list.js create mode 100644 xhiveframework/core/doctype/doctype/patches/set_route.py create mode 100644 xhiveframework/core/doctype/doctype/test_doctype.py create mode 100644 xhiveframework/core/doctype/doctype_action/__init__.py create mode 100644 xhiveframework/core/doctype/doctype_action/doctype_action.json create mode 100644 xhiveframework/core/doctype/doctype_action/doctype_action.py create mode 100644 xhiveframework/core/doctype/doctype_link/__init__.py create mode 100644 xhiveframework/core/doctype/doctype_link/doctype_link.json create mode 100644 xhiveframework/core/doctype/doctype_link/doctype_link.py create mode 100644 xhiveframework/core/doctype/doctype_state/__init__.py create mode 100644 xhiveframework/core/doctype/doctype_state/doctype_state.json create mode 100644 xhiveframework/core/doctype/doctype_state/doctype_state.py create mode 100644 xhiveframework/core/doctype/document_naming_rule/__init__.py create mode 100644 xhiveframework/core/doctype/document_naming_rule/document_naming_rule.js create mode 100644 xhiveframework/core/doctype/document_naming_rule/document_naming_rule.json create mode 100644 xhiveframework/core/doctype/document_naming_rule/document_naming_rule.py create mode 100644 xhiveframework/core/doctype/document_naming_rule/test_document_naming_rule.py create mode 100644 xhiveframework/core/doctype/document_naming_rule_condition/__init__.py create mode 100644 xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.js create mode 100644 xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.json create mode 100644 xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py create mode 100644 xhiveframework/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py create mode 100644 xhiveframework/core/doctype/document_naming_settings/__init__.py create mode 100644 xhiveframework/core/doctype/document_naming_settings/document_naming_settings.js create mode 100644 xhiveframework/core/doctype/document_naming_settings/document_naming_settings.json create mode 100644 xhiveframework/core/doctype/document_naming_settings/document_naming_settings.py create mode 100644 xhiveframework/core/doctype/document_naming_settings/test_document_naming_settings.py create mode 100644 xhiveframework/core/doctype/document_share_key/__init__.py create mode 100644 xhiveframework/core/doctype/document_share_key/document_share_key.js create mode 100644 xhiveframework/core/doctype/document_share_key/document_share_key.json create mode 100644 xhiveframework/core/doctype/document_share_key/document_share_key.py create mode 100644 xhiveframework/core/doctype/document_share_key/test_document_share_key.py create mode 100644 xhiveframework/core/doctype/domain/__init__.py create mode 100644 xhiveframework/core/doctype/domain/domain.js create mode 100644 xhiveframework/core/doctype/domain/domain.json create mode 100644 xhiveframework/core/doctype/domain/domain.py create mode 100644 xhiveframework/core/doctype/domain/test_domain.py create mode 100644 xhiveframework/core/doctype/domain_settings/__init__.py create mode 100644 xhiveframework/core/doctype/domain_settings/domain_settings.js create mode 100644 xhiveframework/core/doctype/domain_settings/domain_settings.json create mode 100644 xhiveframework/core/doctype/domain_settings/domain_settings.py create mode 100644 xhiveframework/core/doctype/dynamic_link/__init__.py create mode 100644 xhiveframework/core/doctype/dynamic_link/dynamic_link.json create mode 100644 xhiveframework/core/doctype/dynamic_link/dynamic_link.py create mode 100644 xhiveframework/core/doctype/error_log/__init__.py create mode 100644 xhiveframework/core/doctype/error_log/error_log.js create mode 100644 xhiveframework/core/doctype/error_log/error_log.json create mode 100644 xhiveframework/core/doctype/error_log/error_log.py create mode 100644 xhiveframework/core/doctype/error_log/error_log_list.js create mode 100644 xhiveframework/core/doctype/error_log/test_error_log.py create mode 100644 xhiveframework/core/doctype/file/__init__.py create mode 100644 xhiveframework/core/doctype/file/exceptions.py create mode 100644 xhiveframework/core/doctype/file/file.js create mode 100644 xhiveframework/core/doctype/file/file.json create mode 100755 xhiveframework/core/doctype/file/file.py create mode 100644 xhiveframework/core/doctype/file/test_file.py create mode 100644 xhiveframework/core/doctype/file/utils.py create mode 100644 xhiveframework/core/doctype/has_domain/__init__.py create mode 100644 xhiveframework/core/doctype/has_domain/has_domain.json create mode 100644 xhiveframework/core/doctype/has_domain/has_domain.py create mode 100644 xhiveframework/core/doctype/has_role/__init__.py create mode 100644 xhiveframework/core/doctype/has_role/has_role.json create mode 100644 xhiveframework/core/doctype/has_role/has_role.py create mode 100644 xhiveframework/core/doctype/installed_application/__init__.py create mode 100644 xhiveframework/core/doctype/installed_application/installed_application.json create mode 100644 xhiveframework/core/doctype/installed_application/installed_application.py create mode 100644 xhiveframework/core/doctype/installed_applications/__init__.py create mode 100644 xhiveframework/core/doctype/installed_applications/installed_applications.js create mode 100644 xhiveframework/core/doctype/installed_applications/installed_applications.json create mode 100644 xhiveframework/core/doctype/installed_applications/installed_applications.py create mode 100644 xhiveframework/core/doctype/installed_applications/test_installed_applications.py create mode 100644 xhiveframework/core/doctype/language/__init__.py create mode 100644 xhiveframework/core/doctype/language/language.js create mode 100644 xhiveframework/core/doctype/language/language.json create mode 100644 xhiveframework/core/doctype/language/language.py create mode 100644 xhiveframework/core/doctype/language/test_language.py create mode 100644 xhiveframework/core/doctype/log_setting_user/__init__.py create mode 100644 xhiveframework/core/doctype/log_setting_user/log_setting_user.js create mode 100644 xhiveframework/core/doctype/log_setting_user/log_setting_user.json create mode 100644 xhiveframework/core/doctype/log_setting_user/log_setting_user.py create mode 100644 xhiveframework/core/doctype/log_setting_user/test_log_setting_user.py create mode 100644 xhiveframework/core/doctype/log_settings/__init__.py create mode 100644 xhiveframework/core/doctype/log_settings/log_settings.js create mode 100644 xhiveframework/core/doctype/log_settings/log_settings.json create mode 100644 xhiveframework/core/doctype/log_settings/log_settings.py create mode 100644 xhiveframework/core/doctype/log_settings/test_log_settings.py create mode 100644 xhiveframework/core/doctype/logs_to_clear/__init__.py create mode 100644 xhiveframework/core/doctype/logs_to_clear/logs_to_clear.json create mode 100644 xhiveframework/core/doctype/logs_to_clear/logs_to_clear.py create mode 100644 xhiveframework/core/doctype/module_def/README.md create mode 100644 xhiveframework/core/doctype/module_def/__init__.py create mode 100644 xhiveframework/core/doctype/module_def/module_def.js create mode 100644 xhiveframework/core/doctype/module_def/module_def.json create mode 100644 xhiveframework/core/doctype/module_def/module_def.py create mode 100644 xhiveframework/core/doctype/module_def/module_def_list.js create mode 100644 xhiveframework/core/doctype/module_def/test_module_def.py create mode 100644 xhiveframework/core/doctype/module_profile/__init__.py create mode 100644 xhiveframework/core/doctype/module_profile/module_profile.js create mode 100644 xhiveframework/core/doctype/module_profile/module_profile.json create mode 100644 xhiveframework/core/doctype/module_profile/module_profile.py create mode 100644 xhiveframework/core/doctype/module_profile/test_module_profile.py create mode 100644 xhiveframework/core/doctype/navbar_item/__init__.py create mode 100644 xhiveframework/core/doctype/navbar_item/navbar_item.js create mode 100644 xhiveframework/core/doctype/navbar_item/navbar_item.json create mode 100644 xhiveframework/core/doctype/navbar_item/navbar_item.py create mode 100644 xhiveframework/core/doctype/navbar_item/test_navbar_item.py create mode 100644 xhiveframework/core/doctype/navbar_settings/__init__.py create mode 100644 xhiveframework/core/doctype/navbar_settings/navbar_settings.js create mode 100644 xhiveframework/core/doctype/navbar_settings/navbar_settings.json create mode 100644 xhiveframework/core/doctype/navbar_settings/navbar_settings.py create mode 100644 xhiveframework/core/doctype/navbar_settings/test_navbar_settings.py create mode 100644 xhiveframework/core/doctype/package/__init__.py create mode 100644 xhiveframework/core/doctype/package/licenses/GNU Affero General Public License.md create mode 100644 xhiveframework/core/doctype/package/licenses/GNU General Public License.md create mode 100644 xhiveframework/core/doctype/package/licenses/MIT License.md create mode 100644 xhiveframework/core/doctype/package/package.js create mode 100644 xhiveframework/core/doctype/package/package.json create mode 100644 xhiveframework/core/doctype/package/package.py create mode 100644 xhiveframework/core/doctype/package/test_package.py create mode 100644 xhiveframework/core/doctype/package_import/__init__.py create mode 100644 xhiveframework/core/doctype/package_import/package_import.js create mode 100644 xhiveframework/core/doctype/package_import/package_import.json create mode 100644 xhiveframework/core/doctype/package_import/package_import.py create mode 100644 xhiveframework/core/doctype/package_import/test_package_import.py create mode 100644 xhiveframework/core/doctype/package_release/__init__.py create mode 100644 xhiveframework/core/doctype/package_release/package_release.js create mode 100644 xhiveframework/core/doctype/package_release/package_release.json create mode 100644 xhiveframework/core/doctype/package_release/package_release.py create mode 100644 xhiveframework/core/doctype/package_release/test_package_release.py create mode 100644 xhiveframework/core/doctype/page/README.md create mode 100644 xhiveframework/core/doctype/page/__init__.py create mode 100644 xhiveframework/core/doctype/page/page.js create mode 100644 xhiveframework/core/doctype/page/page.json create mode 100644 xhiveframework/core/doctype/page/page.py create mode 100644 xhiveframework/core/doctype/page/patches/drop_unused_pages.py create mode 100644 xhiveframework/core/doctype/page/test_page.py create mode 100644 xhiveframework/core/doctype/page/test_records.json create mode 100644 xhiveframework/core/doctype/patch_log/README.md create mode 100644 xhiveframework/core/doctype/patch_log/__init__.py create mode 100644 xhiveframework/core/doctype/patch_log/patch_log.js create mode 100644 xhiveframework/core/doctype/patch_log/patch_log.json create mode 100644 xhiveframework/core/doctype/patch_log/patch_log.py create mode 100644 xhiveframework/core/doctype/patch_log/test_patch_log.py create mode 100644 xhiveframework/core/doctype/permission_inspector/__init__.py create mode 100644 xhiveframework/core/doctype/permission_inspector/permission_inspector.js create mode 100644 xhiveframework/core/doctype/permission_inspector/permission_inspector.json create mode 100644 xhiveframework/core/doctype/permission_inspector/permission_inspector.py create mode 100644 xhiveframework/core/doctype/permission_inspector/test_permission_inspector.py create mode 100644 xhiveframework/core/doctype/prepared_report/__init__.py create mode 100644 xhiveframework/core/doctype/prepared_report/prepared_report.js create mode 100644 xhiveframework/core/doctype/prepared_report/prepared_report.json create mode 100644 xhiveframework/core/doctype/prepared_report/prepared_report.py create mode 100644 xhiveframework/core/doctype/prepared_report/prepared_report_list.js create mode 100644 xhiveframework/core/doctype/prepared_report/test_prepared_report.py create mode 100644 xhiveframework/core/doctype/recorder/__init__.py create mode 100644 xhiveframework/core/doctype/recorder/recorder.js create mode 100644 xhiveframework/core/doctype/recorder/recorder.json create mode 100644 xhiveframework/core/doctype/recorder/recorder.py create mode 100644 xhiveframework/core/doctype/recorder/recorder_list.js create mode 100644 xhiveframework/core/doctype/recorder/test_recorder.py create mode 100644 xhiveframework/core/doctype/recorder_query/__init__.py create mode 100644 xhiveframework/core/doctype/recorder_query/recorder_query.js create mode 100644 xhiveframework/core/doctype/recorder_query/recorder_query.json create mode 100644 xhiveframework/core/doctype/recorder_query/recorder_query.py create mode 100644 xhiveframework/core/doctype/recorder_query/test_recorder_query.py create mode 100644 xhiveframework/core/doctype/report/README.md create mode 100644 xhiveframework/core/doctype/report/__init__.py create mode 100644 xhiveframework/core/doctype/report/boilerplate/controller.js create mode 100644 xhiveframework/core/doctype/report/boilerplate/controller.py create mode 100644 xhiveframework/core/doctype/report/report.js create mode 100644 xhiveframework/core/doctype/report/report.json create mode 100644 xhiveframework/core/doctype/report/report.py create mode 100644 xhiveframework/core/doctype/report/test_records.json create mode 100644 xhiveframework/core/doctype/report/test_report.py create mode 100644 xhiveframework/core/doctype/report/user_activity_report.json create mode 100644 xhiveframework/core/doctype/report/user_activity_report_without_sort.json create mode 100644 xhiveframework/core/doctype/report_column/__init__.py create mode 100644 xhiveframework/core/doctype/report_column/report_column.json create mode 100644 xhiveframework/core/doctype/report_column/report_column.py create mode 100644 xhiveframework/core/doctype/report_filter/__init__.py create mode 100644 xhiveframework/core/doctype/report_filter/report_filter.json create mode 100644 xhiveframework/core/doctype/report_filter/report_filter.py create mode 100644 xhiveframework/core/doctype/role/README.md create mode 100644 xhiveframework/core/doctype/role/__init__.py create mode 100644 xhiveframework/core/doctype/role/patches/v13_set_default_desk_properties.py create mode 100644 xhiveframework/core/doctype/role/role.js create mode 100644 xhiveframework/core/doctype/role/role.json create mode 100644 xhiveframework/core/doctype/role/role.py create mode 100644 xhiveframework/core/doctype/role/test_records.json create mode 100644 xhiveframework/core/doctype/role/test_role.py create mode 100644 xhiveframework/core/doctype/role_permission_for_page_and_report/__init__.py create mode 100644 xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.js create mode 100644 xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.json create mode 100644 xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py create mode 100644 xhiveframework/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py create mode 100644 xhiveframework/core/doctype/role_profile/__init__.py create mode 100644 xhiveframework/core/doctype/role_profile/role_profile.js create mode 100644 xhiveframework/core/doctype/role_profile/role_profile.json create mode 100644 xhiveframework/core/doctype/role_profile/role_profile.py create mode 100644 xhiveframework/core/doctype/role_profile/test_role_profile.py create mode 100644 xhiveframework/core/doctype/rq_job/__init__.py create mode 100644 xhiveframework/core/doctype/rq_job/rq_job.js create mode 100644 xhiveframework/core/doctype/rq_job/rq_job.json create mode 100644 xhiveframework/core/doctype/rq_job/rq_job.py create mode 100644 xhiveframework/core/doctype/rq_job/rq_job_list.js create mode 100644 xhiveframework/core/doctype/rq_job/test_rq_job.py create mode 100644 xhiveframework/core/doctype/rq_worker/__init__.py create mode 100644 xhiveframework/core/doctype/rq_worker/rq_worker.js create mode 100644 xhiveframework/core/doctype/rq_worker/rq_worker.json create mode 100644 xhiveframework/core/doctype/rq_worker/rq_worker.py create mode 100644 xhiveframework/core/doctype/rq_worker/rq_worker_list.js create mode 100644 xhiveframework/core/doctype/rq_worker/test_rq_worker.py create mode 100644 xhiveframework/core/doctype/scheduled_job_log/__init__.py create mode 100644 xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.js create mode 100644 xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.json create mode 100644 xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.py create mode 100644 xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log_list.js create mode 100644 xhiveframework/core/doctype/scheduled_job_log/test_scheduled_job_log.py create mode 100644 xhiveframework/core/doctype/scheduled_job_type/__init__.py create mode 100644 xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.js create mode 100644 xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.json create mode 100644 xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.py create mode 100644 xhiveframework/core/doctype/scheduled_job_type/test_scheduled_job_type.py create mode 100644 xhiveframework/core/doctype/server_script/__init__.py create mode 100644 xhiveframework/core/doctype/server_script/server_script.js create mode 100644 xhiveframework/core/doctype/server_script/server_script.json create mode 100644 xhiveframework/core/doctype/server_script/server_script.py create mode 100644 xhiveframework/core/doctype/server_script/server_script_list.js create mode 100644 xhiveframework/core/doctype/server_script/server_script_utils.py create mode 100644 xhiveframework/core/doctype/server_script/test_server_script.py create mode 100644 xhiveframework/core/doctype/session_default/__init__.py create mode 100644 xhiveframework/core/doctype/session_default/session_default.json create mode 100644 xhiveframework/core/doctype/session_default/session_default.py create mode 100644 xhiveframework/core/doctype/session_default_settings/__init__.py create mode 100644 xhiveframework/core/doctype/session_default_settings/session_default_settings.js create mode 100644 xhiveframework/core/doctype/session_default_settings/session_default_settings.json create mode 100644 xhiveframework/core/doctype/session_default_settings/session_default_settings.py create mode 100644 xhiveframework/core/doctype/session_default_settings/test_session_default_settings.py create mode 100644 xhiveframework/core/doctype/sms_parameter/README.md create mode 100755 xhiveframework/core/doctype/sms_parameter/__init__.py create mode 100755 xhiveframework/core/doctype/sms_parameter/sms_parameter.json create mode 100644 xhiveframework/core/doctype/sms_parameter/sms_parameter.py create mode 100644 xhiveframework/core/doctype/sms_settings/README.md create mode 100755 xhiveframework/core/doctype/sms_settings/__init__.py create mode 100644 xhiveframework/core/doctype/sms_settings/sms_settings.js create mode 100755 xhiveframework/core/doctype/sms_settings/sms_settings.json create mode 100644 xhiveframework/core/doctype/sms_settings/sms_settings.py create mode 100644 xhiveframework/core/doctype/sms_settings/test_sms_settings.py create mode 100644 xhiveframework/core/doctype/submission_queue/__init__.py create mode 100644 xhiveframework/core/doctype/submission_queue/submission_queue.js create mode 100644 xhiveframework/core/doctype/submission_queue/submission_queue.json create mode 100644 xhiveframework/core/doctype/submission_queue/submission_queue.py create mode 100644 xhiveframework/core/doctype/submission_queue/test_submission_queue.py create mode 100644 xhiveframework/core/doctype/success_action/__init__.py create mode 100644 xhiveframework/core/doctype/success_action/success_action.js create mode 100644 xhiveframework/core/doctype/success_action/success_action.json create mode 100644 xhiveframework/core/doctype/success_action/success_action.py create mode 100644 xhiveframework/core/doctype/system_settings/__init__.py create mode 100644 xhiveframework/core/doctype/system_settings/system_settings.js create mode 100644 xhiveframework/core/doctype/system_settings/system_settings.json create mode 100644 xhiveframework/core/doctype/system_settings/system_settings.py create mode 100644 xhiveframework/core/doctype/system_settings/test_system_settings.py create mode 100644 xhiveframework/core/doctype/transaction_log/__init__.py create mode 100644 xhiveframework/core/doctype/transaction_log/readme.md create mode 100644 xhiveframework/core/doctype/transaction_log/test_transaction_log.py create mode 100644 xhiveframework/core/doctype/transaction_log/transaction_log.js create mode 100644 xhiveframework/core/doctype/transaction_log/transaction_log.json create mode 100644 xhiveframework/core/doctype/transaction_log/transaction_log.py create mode 100644 xhiveframework/core/doctype/translation/__init__.py create mode 100644 xhiveframework/core/doctype/translation/test_translation.py create mode 100644 xhiveframework/core/doctype/translation/translation.js create mode 100644 xhiveframework/core/doctype/translation/translation.json create mode 100644 xhiveframework/core/doctype/translation/translation.py create mode 100644 xhiveframework/core/doctype/user/__init__.py create mode 100644 xhiveframework/core/doctype/user/test_records.json create mode 100644 xhiveframework/core/doctype/user/test_user.py create mode 100644 xhiveframework/core/doctype/user/user.js create mode 100644 xhiveframework/core/doctype/user/user.json create mode 100644 xhiveframework/core/doctype/user/user.py create mode 100644 xhiveframework/core/doctype/user/user_list.js create mode 100644 xhiveframework/core/doctype/user_document_type/__init__.py create mode 100644 xhiveframework/core/doctype/user_document_type/user_document_type.json create mode 100644 xhiveframework/core/doctype/user_document_type/user_document_type.py create mode 100644 xhiveframework/core/doctype/user_email/__init__.py create mode 100644 xhiveframework/core/doctype/user_email/user_email.json create mode 100644 xhiveframework/core/doctype/user_email/user_email.py create mode 100644 xhiveframework/core/doctype/user_group/__init__.py create mode 100644 xhiveframework/core/doctype/user_group/test_user_group.py create mode 100644 xhiveframework/core/doctype/user_group/user_group.js create mode 100644 xhiveframework/core/doctype/user_group/user_group.json create mode 100644 xhiveframework/core/doctype/user_group/user_group.py create mode 100644 xhiveframework/core/doctype/user_group_member/__init__.py create mode 100644 xhiveframework/core/doctype/user_group_member/test_user_group_member.py create mode 100644 xhiveframework/core/doctype/user_group_member/user_group_member.js create mode 100644 xhiveframework/core/doctype/user_group_member/user_group_member.json create mode 100644 xhiveframework/core/doctype/user_group_member/user_group_member.py create mode 100644 xhiveframework/core/doctype/user_permission/__init__.py create mode 100644 xhiveframework/core/doctype/user_permission/test_user_permission.py create mode 100644 xhiveframework/core/doctype/user_permission/user_permission.js create mode 100644 xhiveframework/core/doctype/user_permission/user_permission.json create mode 100644 xhiveframework/core/doctype/user_permission/user_permission.py create mode 100644 xhiveframework/core/doctype/user_permission/user_permission_help.html create mode 100644 xhiveframework/core/doctype/user_permission/user_permission_list.js create mode 100644 xhiveframework/core/doctype/user_select_document_type/__init__.py create mode 100644 xhiveframework/core/doctype/user_select_document_type/user_select_document_type.json create mode 100644 xhiveframework/core/doctype/user_select_document_type/user_select_document_type.py create mode 100644 xhiveframework/core/doctype/user_social_login/__init__.py create mode 100644 xhiveframework/core/doctype/user_social_login/user_social_login.json create mode 100644 xhiveframework/core/doctype/user_social_login/user_social_login.py create mode 100644 xhiveframework/core/doctype/user_type/__init__.py create mode 100644 xhiveframework/core/doctype/user_type/test_user_type.py create mode 100644 xhiveframework/core/doctype/user_type/user_type.js create mode 100644 xhiveframework/core/doctype/user_type/user_type.json create mode 100644 xhiveframework/core/doctype/user_type/user_type.py create mode 100644 xhiveframework/core/doctype/user_type/user_type_dashboard.py create mode 100644 xhiveframework/core/doctype/user_type/user_type_list.js create mode 100644 xhiveframework/core/doctype/user_type_module/__init__.py create mode 100644 xhiveframework/core/doctype/user_type_module/user_type_module.json create mode 100644 xhiveframework/core/doctype/user_type_module/user_type_module.py create mode 100644 xhiveframework/core/doctype/version/__init__.py create mode 100644 xhiveframework/core/doctype/version/test_records.json create mode 100644 xhiveframework/core/doctype/version/test_version.py create mode 100644 xhiveframework/core/doctype/version/version.js create mode 100644 xhiveframework/core/doctype/version/version.json create mode 100644 xhiveframework/core/doctype/version/version.py create mode 100644 xhiveframework/core/doctype/version/version_view.html create mode 100644 xhiveframework/core/doctype/view_log/__init__.py create mode 100644 xhiveframework/core/doctype/view_log/test_view_log.py create mode 100644 xhiveframework/core/doctype/view_log/view_log.js create mode 100644 xhiveframework/core/doctype/view_log/view_log.json create mode 100644 xhiveframework/core/doctype/view_log/view_log.py create mode 100644 xhiveframework/core/form_tour/doctype/doctype.json create mode 100644 xhiveframework/core/notifications.py create mode 100644 xhiveframework/core/page/__init__.py create mode 100644 xhiveframework/core/page/dashboard_view/__init__.py create mode 100644 xhiveframework/core/page/dashboard_view/dashboard_view.js create mode 100644 xhiveframework/core/page/dashboard_view/dashboard_view.json create mode 100644 xhiveframework/core/page/permission_manager/README.md create mode 100644 xhiveframework/core/page/permission_manager/__init__.py create mode 100644 xhiveframework/core/page/permission_manager/permission_manager.css create mode 100644 xhiveframework/core/page/permission_manager/permission_manager.js create mode 100644 xhiveframework/core/page/permission_manager/permission_manager.json create mode 100644 xhiveframework/core/page/permission_manager/permission_manager.py create mode 100644 xhiveframework/core/page/permission_manager/permission_manager_help.html create mode 100644 xhiveframework/core/report/__init__.py create mode 100644 xhiveframework/core/report/database_storage_usage_by_tables/__init__.py create mode 100644 xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js create mode 100644 xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.json create mode 100644 xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py create mode 100644 xhiveframework/core/report/database_storage_usage_by_tables/test_database_storage_usage_by_tables.py create mode 100644 xhiveframework/core/report/document_share_report/__init__.py create mode 100644 xhiveframework/core/report/document_share_report/document_share_report.json create mode 100644 xhiveframework/core/report/permitted_documents_for_user/__init__.py create mode 100644 xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.js create mode 100644 xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.json create mode 100644 xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.py create mode 100644 xhiveframework/core/report/transaction_log_report/__init__.py create mode 100644 xhiveframework/core/report/transaction_log_report/transaction_log_report.js create mode 100644 xhiveframework/core/report/transaction_log_report/transaction_log_report.json create mode 100644 xhiveframework/core/report/transaction_log_report/transaction_log_report.py create mode 100644 xhiveframework/core/utils.py create mode 100644 xhiveframework/core/web_form/__init__.py create mode 100644 xhiveframework/core/web_form/edit_profile/__init__.py create mode 100644 xhiveframework/core/web_form/edit_profile/edit_profile.js create mode 100644 xhiveframework/core/web_form/edit_profile/edit_profile.json create mode 100644 xhiveframework/core/web_form/edit_profile/edit_profile.py create mode 100644 xhiveframework/core/workspace/build/build.json create mode 100644 xhiveframework/core/workspace/users/users.json create mode 100644 xhiveframework/core/workspace/welcome_workspace/welcome_workspace.json create mode 100644 xhiveframework/coverage.py create mode 100644 xhiveframework/custom/__init__.py create mode 100644 xhiveframework/custom/doctype/__init__.py create mode 100644 xhiveframework/custom/doctype/client_script/README.md create mode 100644 xhiveframework/custom/doctype/client_script/__init__.py create mode 100644 xhiveframework/custom/doctype/client_script/client_script.js create mode 100644 xhiveframework/custom/doctype/client_script/client_script.json create mode 100644 xhiveframework/custom/doctype/client_script/client_script.py create mode 100644 xhiveframework/custom/doctype/client_script/test_client_script.py create mode 100644 xhiveframework/custom/doctype/client_script/ui_test_client_script.js create mode 100644 xhiveframework/custom/doctype/custom_field/README.md create mode 100644 xhiveframework/custom/doctype/custom_field/__init__.py create mode 100644 xhiveframework/custom/doctype/custom_field/custom_field.js create mode 100644 xhiveframework/custom/doctype/custom_field/custom_field.json create mode 100644 xhiveframework/custom/doctype/custom_field/custom_field.py create mode 100644 xhiveframework/custom/doctype/custom_field/test_custom_field.py create mode 100644 xhiveframework/custom/doctype/custom_field/test_records.json create mode 100644 xhiveframework/custom/doctype/customize_form/README.md create mode 100644 xhiveframework/custom/doctype/customize_form/__init__.py create mode 100644 xhiveframework/custom/doctype/customize_form/customize_form.js create mode 100644 xhiveframework/custom/doctype/customize_form/customize_form.json create mode 100644 xhiveframework/custom/doctype/customize_form/customize_form.py create mode 100644 xhiveframework/custom/doctype/customize_form/test_customize_form.py create mode 100644 xhiveframework/custom/doctype/customize_form_field/__init__.py create mode 100644 xhiveframework/custom/doctype/customize_form_field/customize_form_field.json create mode 100644 xhiveframework/custom/doctype/customize_form_field/customize_form_field.py create mode 100644 xhiveframework/custom/doctype/doctype_layout/__init__.py create mode 100644 xhiveframework/custom/doctype/doctype_layout/doctype_layout.js create mode 100644 xhiveframework/custom/doctype/doctype_layout/doctype_layout.json create mode 100644 xhiveframework/custom/doctype/doctype_layout/doctype_layout.py create mode 100644 xhiveframework/custom/doctype/doctype_layout/patches/convert_web_forms_to_doctype_layout.py create mode 100644 xhiveframework/custom/doctype/doctype_layout/test_doctype_layout.py create mode 100644 xhiveframework/custom/doctype/doctype_layout_field/__init__.py create mode 100644 xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.json create mode 100644 xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.py create mode 100644 xhiveframework/custom/doctype/property_setter/README.md create mode 100644 xhiveframework/custom/doctype/property_setter/__init__.py create mode 100644 xhiveframework/custom/doctype/property_setter/patches/__init__.py create mode 100644 xhiveframework/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py create mode 100644 xhiveframework/custom/doctype/property_setter/property_setter.js create mode 100644 xhiveframework/custom/doctype/property_setter/property_setter.json create mode 100644 xhiveframework/custom/doctype/property_setter/property_setter.py create mode 100644 xhiveframework/custom/doctype/property_setter/test_property_setter.py create mode 100644 xhiveframework/custom/doctype/property_setter/test_records.json create mode 100644 xhiveframework/custom/fixtures/temp_doctype.json create mode 100644 xhiveframework/custom/fixtures/temp_singles.json create mode 100644 xhiveframework/custom/form_tour/custom_field/custom_field.json create mode 100644 xhiveframework/custom/module_onboarding/customization/customization.json create mode 100644 xhiveframework/custom/onboarding_step/custom_doctype/custom_doctype.json create mode 100644 xhiveframework/custom/onboarding_step/custom_field/custom_field.json create mode 100644 xhiveframework/custom/onboarding_step/naming_series/naming_series.json create mode 100644 xhiveframework/custom/onboarding_step/print_format/print_format.json create mode 100644 xhiveframework/custom/onboarding_step/report_builder/report_builder.json create mode 100644 xhiveframework/custom/onboarding_step/role_permissions/role_permissions.json create mode 100644 xhiveframework/custom/onboarding_step/workflows/workflows.json create mode 100644 xhiveframework/custom/report/__init__.py create mode 100644 xhiveframework/custom/report/audit_system_hooks/__init__.py create mode 100644 xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.js create mode 100644 xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.json create mode 100644 xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.py create mode 100644 xhiveframework/custom/report/audit_system_hooks/test_audit_system_hooks.py create mode 100644 xhiveframework/data/google_fonts.json create mode 100644 xhiveframework/database/__init__.py create mode 100644 xhiveframework/database/database.py create mode 100644 xhiveframework/database/db_manager.py create mode 100644 xhiveframework/database/mariadb/__init__.py create mode 100644 xhiveframework/database/mariadb/database.py create mode 100644 xhiveframework/database/mariadb/framework_mariadb.sql create mode 100644 xhiveframework/database/mariadb/schema.py create mode 100644 xhiveframework/database/mariadb/setup_db.py create mode 100644 xhiveframework/database/operator_map.py create mode 100644 xhiveframework/database/postgres/__init__.py create mode 100644 xhiveframework/database/postgres/database.py create mode 100644 xhiveframework/database/postgres/framework_postgres.sql create mode 100644 xhiveframework/database/postgres/schema.py create mode 100644 xhiveframework/database/postgres/setup_db.py create mode 100644 xhiveframework/database/query.py create mode 100644 xhiveframework/database/schema.py create mode 100644 xhiveframework/database/sequence.py create mode 100644 xhiveframework/database/utils.py create mode 100644 xhiveframework/defaults.py create mode 100644 xhiveframework/deferred_insert.py create mode 100644 xhiveframework/desk/__init__.py create mode 100644 xhiveframework/desk/calendar.py create mode 100644 xhiveframework/desk/desk_page.py create mode 100644 xhiveframework/desk/desktop.py create mode 100644 xhiveframework/desk/doctype/__init__.py create mode 100644 xhiveframework/desk/doctype/bulk_update/__init__.py create mode 100644 xhiveframework/desk/doctype/bulk_update/bulk_update.js create mode 100644 xhiveframework/desk/doctype/bulk_update/bulk_update.json create mode 100644 xhiveframework/desk/doctype/bulk_update/bulk_update.py create mode 100644 xhiveframework/desk/doctype/bulk_update/test_bulk_update.py create mode 100644 xhiveframework/desk/doctype/calendar_view/__init__.py create mode 100644 xhiveframework/desk/doctype/calendar_view/calendar_view.js create mode 100644 xhiveframework/desk/doctype/calendar_view/calendar_view.json create mode 100644 xhiveframework/desk/doctype/calendar_view/calendar_view.py create mode 100644 xhiveframework/desk/doctype/calendar_view/calendar_view_list.js create mode 100644 xhiveframework/desk/doctype/console_log/__init__.py create mode 100644 xhiveframework/desk/doctype/console_log/console_log.js create mode 100644 xhiveframework/desk/doctype/console_log/console_log.json create mode 100644 xhiveframework/desk/doctype/console_log/console_log.py create mode 100644 xhiveframework/desk/doctype/console_log/test_console_log.py create mode 100644 xhiveframework/desk/doctype/custom_html_block/__init__.py create mode 100644 xhiveframework/desk/doctype/custom_html_block/custom_html_block.js create mode 100644 xhiveframework/desk/doctype/custom_html_block/custom_html_block.json create mode 100644 xhiveframework/desk/doctype/custom_html_block/custom_html_block.py create mode 100644 xhiveframework/desk/doctype/custom_html_block/test_custom_html_block.py create mode 100644 xhiveframework/desk/doctype/dashboard/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard/dashboard.js create mode 100644 xhiveframework/desk/doctype/dashboard/dashboard.json create mode 100644 xhiveframework/desk/doctype/dashboard/dashboard.py create mode 100644 xhiveframework/desk/doctype/dashboard/dashboard_list.js create mode 100644 xhiveframework/desk/doctype/dashboard/test_dashboard.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.js create mode 100644 xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.json create mode 100644 xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart/test_dashboard_chart.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_field/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.json create mode 100644 xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_link/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.json create mode 100644 xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_source/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.js create mode 100644 xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.json create mode 100644 xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.py create mode 100644 xhiveframework/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py create mode 100644 xhiveframework/desk/doctype/dashboard_settings/__init__.py create mode 100644 xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.js create mode 100644 xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.json create mode 100644 xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.py create mode 100644 xhiveframework/desk/doctype/desktop_icon/__init__.py create mode 100644 xhiveframework/desk/doctype/desktop_icon/desktop_icon.js create mode 100644 xhiveframework/desk/doctype/desktop_icon/desktop_icon.json create mode 100644 xhiveframework/desk/doctype/desktop_icon/desktop_icon.py create mode 100644 xhiveframework/desk/doctype/event/README.md create mode 100644 xhiveframework/desk/doctype/event/__init__.py create mode 100644 xhiveframework/desk/doctype/event/event.js create mode 100644 xhiveframework/desk/doctype/event/event.json create mode 100644 xhiveframework/desk/doctype/event/event.py create mode 100644 xhiveframework/desk/doctype/event/event_calendar.js create mode 100644 xhiveframework/desk/doctype/event/event_list.js create mode 100644 xhiveframework/desk/doctype/event/test_event.py create mode 100644 xhiveframework/desk/doctype/event/test_records.json create mode 100644 xhiveframework/desk/doctype/event_participants/__init__.py create mode 100644 xhiveframework/desk/doctype/event_participants/event_participants.json create mode 100644 xhiveframework/desk/doctype/event_participants/event_participants.py create mode 100644 xhiveframework/desk/doctype/form_tour/__init__.py create mode 100644 xhiveframework/desk/doctype/form_tour/form_tour.js create mode 100644 xhiveframework/desk/doctype/form_tour/form_tour.json create mode 100644 xhiveframework/desk/doctype/form_tour/form_tour.py create mode 100644 xhiveframework/desk/doctype/form_tour/patches/__init__.py create mode 100644 xhiveframework/desk/doctype/form_tour/patches/introduce_ui_tours.py create mode 100644 xhiveframework/desk/doctype/form_tour/test_form_tour.py create mode 100644 xhiveframework/desk/doctype/form_tour_step/__init__.py create mode 100644 xhiveframework/desk/doctype/form_tour_step/form_tour_step.json create mode 100644 xhiveframework/desk/doctype/form_tour_step/form_tour_step.py create mode 100644 xhiveframework/desk/doctype/global_search_doctype/__init__.py create mode 100644 xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.json create mode 100644 xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.py create mode 100644 xhiveframework/desk/doctype/global_search_settings/__init__.py create mode 100644 xhiveframework/desk/doctype/global_search_settings/global_search_settings.js create mode 100644 xhiveframework/desk/doctype/global_search_settings/global_search_settings.json create mode 100644 xhiveframework/desk/doctype/global_search_settings/global_search_settings.py create mode 100644 xhiveframework/desk/doctype/kanban_board/__init__.py create mode 100644 xhiveframework/desk/doctype/kanban_board/kanban_board.js create mode 100644 xhiveframework/desk/doctype/kanban_board/kanban_board.json create mode 100644 xhiveframework/desk/doctype/kanban_board/kanban_board.py create mode 100644 xhiveframework/desk/doctype/kanban_board/test_kanban_board.py create mode 100644 xhiveframework/desk/doctype/kanban_board_column/__init__.py create mode 100644 xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.json create mode 100644 xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.py create mode 100644 xhiveframework/desk/doctype/list_filter/__init__.py create mode 100644 xhiveframework/desk/doctype/list_filter/list_filter.json create mode 100644 xhiveframework/desk/doctype/list_filter/list_filter.py create mode 100644 xhiveframework/desk/doctype/list_view_settings/__init__.py create mode 100644 xhiveframework/desk/doctype/list_view_settings/list_view_settings.js create mode 100644 xhiveframework/desk/doctype/list_view_settings/list_view_settings.json create mode 100644 xhiveframework/desk/doctype/list_view_settings/list_view_settings.py create mode 100644 xhiveframework/desk/doctype/list_view_settings/test_list_view_settings.py create mode 100644 xhiveframework/desk/doctype/module_onboarding/__init__.py create mode 100644 xhiveframework/desk/doctype/module_onboarding/module_onboarding.js create mode 100644 xhiveframework/desk/doctype/module_onboarding/module_onboarding.json create mode 100644 xhiveframework/desk/doctype/module_onboarding/module_onboarding.py create mode 100644 xhiveframework/desk/doctype/module_onboarding/test_module_onboarding.py create mode 100644 xhiveframework/desk/doctype/note/README.md create mode 100644 xhiveframework/desk/doctype/note/__init__.py create mode 100644 xhiveframework/desk/doctype/note/note.js create mode 100644 xhiveframework/desk/doctype/note/note.json create mode 100644 xhiveframework/desk/doctype/note/note.py create mode 100644 xhiveframework/desk/doctype/note/note_list.js create mode 100644 xhiveframework/desk/doctype/note/test_note.py create mode 100644 xhiveframework/desk/doctype/note/test_records.json create mode 100644 xhiveframework/desk/doctype/note_seen_by/__init__.py create mode 100644 xhiveframework/desk/doctype/note_seen_by/note_seen_by.json create mode 100644 xhiveframework/desk/doctype/note_seen_by/note_seen_by.py create mode 100644 xhiveframework/desk/doctype/notification_log/__init__.py create mode 100644 xhiveframework/desk/doctype/notification_log/notification_log.js create mode 100644 xhiveframework/desk/doctype/notification_log/notification_log.json create mode 100644 xhiveframework/desk/doctype/notification_log/notification_log.py create mode 100644 xhiveframework/desk/doctype/notification_log/notification_log_list.js create mode 100644 xhiveframework/desk/doctype/notification_log/test_notification_log.py create mode 100644 xhiveframework/desk/doctype/notification_settings/__init__.py create mode 100644 xhiveframework/desk/doctype/notification_settings/notification_settings.js create mode 100644 xhiveframework/desk/doctype/notification_settings/notification_settings.json create mode 100644 xhiveframework/desk/doctype/notification_settings/notification_settings.py create mode 100644 xhiveframework/desk/doctype/notification_settings/test_notification_settings.py create mode 100644 xhiveframework/desk/doctype/notification_subscribed_document/__init__.py create mode 100644 xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.json create mode 100644 xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.py create mode 100644 xhiveframework/desk/doctype/number_card/__init__.py create mode 100644 xhiveframework/desk/doctype/number_card/number_card.js create mode 100644 xhiveframework/desk/doctype/number_card/number_card.json create mode 100644 xhiveframework/desk/doctype/number_card/number_card.py create mode 100644 xhiveframework/desk/doctype/number_card/test_number_card.py create mode 100644 xhiveframework/desk/doctype/number_card_link/__init__.py create mode 100644 xhiveframework/desk/doctype/number_card_link/number_card_link.json create mode 100644 xhiveframework/desk/doctype/number_card_link/number_card_link.py create mode 100644 xhiveframework/desk/doctype/onboarding_permission/__init__.py create mode 100644 xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.js create mode 100644 xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.json create mode 100644 xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.py create mode 100644 xhiveframework/desk/doctype/onboarding_permission/test_onboarding_permission.py create mode 100644 xhiveframework/desk/doctype/onboarding_step/__init__.py create mode 100644 xhiveframework/desk/doctype/onboarding_step/onboarding_step.js create mode 100644 xhiveframework/desk/doctype/onboarding_step/onboarding_step.json create mode 100644 xhiveframework/desk/doctype/onboarding_step/onboarding_step.py create mode 100644 xhiveframework/desk/doctype/onboarding_step/test_onboarding_step.py create mode 100644 xhiveframework/desk/doctype/onboarding_step_map/__init__.py create mode 100644 xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.json create mode 100644 xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.py create mode 100644 xhiveframework/desk/doctype/route_history/__init__.py create mode 100644 xhiveframework/desk/doctype/route_history/route_history.js create mode 100644 xhiveframework/desk/doctype/route_history/route_history.json create mode 100644 xhiveframework/desk/doctype/route_history/route_history.py create mode 100644 xhiveframework/desk/doctype/route_history/route_history_list.js create mode 100644 xhiveframework/desk/doctype/system_console/__init__.py create mode 100644 xhiveframework/desk/doctype/system_console/system_console.js create mode 100644 xhiveframework/desk/doctype/system_console/system_console.json create mode 100644 xhiveframework/desk/doctype/system_console/system_console.py create mode 100644 xhiveframework/desk/doctype/system_console/test_system_console.py create mode 100644 xhiveframework/desk/doctype/tag/__init__.py create mode 100644 xhiveframework/desk/doctype/tag/tag.js create mode 100644 xhiveframework/desk/doctype/tag/tag.json create mode 100644 xhiveframework/desk/doctype/tag/tag.py create mode 100644 xhiveframework/desk/doctype/tag/test_tag.py create mode 100644 xhiveframework/desk/doctype/tag_link/__init__.py create mode 100644 xhiveframework/desk/doctype/tag_link/tag_link.js create mode 100644 xhiveframework/desk/doctype/tag_link/tag_link.json create mode 100644 xhiveframework/desk/doctype/tag_link/tag_link.py create mode 100644 xhiveframework/desk/doctype/tag_link/test_tag_link.py create mode 100644 xhiveframework/desk/doctype/todo/README.md create mode 100644 xhiveframework/desk/doctype/todo/__init__.py create mode 100644 xhiveframework/desk/doctype/todo/test_todo.py create mode 100644 xhiveframework/desk/doctype/todo/todo.js create mode 100644 xhiveframework/desk/doctype/todo/todo.json create mode 100644 xhiveframework/desk/doctype/todo/todo.py create mode 100644 xhiveframework/desk/doctype/todo/todo_calendar.js create mode 100644 xhiveframework/desk/doctype/todo/todo_list.js create mode 100644 xhiveframework/desk/doctype/workspace/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace/test_workspace.py create mode 100644 xhiveframework/desk/doctype/workspace/workspace.js create mode 100644 xhiveframework/desk/doctype/workspace/workspace.json create mode 100644 xhiveframework/desk/doctype/workspace/workspace.py create mode 100644 xhiveframework/desk/doctype/workspace_chart/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_chart/workspace_chart.json create mode 100644 xhiveframework/desk/doctype/workspace_chart/workspace_chart.py create mode 100644 xhiveframework/desk/doctype/workspace_custom_block/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.json create mode 100644 xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.py create mode 100644 xhiveframework/desk/doctype/workspace_link/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_link/workspace_link.json create mode 100644 xhiveframework/desk/doctype/workspace_link/workspace_link.py create mode 100644 xhiveframework/desk/doctype/workspace_number_card/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.json create mode 100644 xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.py create mode 100644 xhiveframework/desk/doctype/workspace_quick_list/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.json create mode 100644 xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.py create mode 100644 xhiveframework/desk/doctype/workspace_shortcut/__init__.py create mode 100644 xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.json create mode 100644 xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.py create mode 100644 xhiveframework/desk/form/__init__.py create mode 100644 xhiveframework/desk/form/assign_to.py create mode 100644 xhiveframework/desk/form/document_follow.py create mode 100644 xhiveframework/desk/form/linked_with.py create mode 100644 xhiveframework/desk/form/load.py create mode 100644 xhiveframework/desk/form/meta.py create mode 100644 xhiveframework/desk/form/save.py create mode 100644 xhiveframework/desk/form/test_form.py create mode 100644 xhiveframework/desk/form/utils.py create mode 100644 xhiveframework/desk/gantt.py create mode 100644 xhiveframework/desk/leaderboard.py create mode 100644 xhiveframework/desk/like.py create mode 100644 xhiveframework/desk/link_preview.py create mode 100644 xhiveframework/desk/listview.py create mode 100644 xhiveframework/desk/notifications.py create mode 100644 xhiveframework/desk/page/__init__.py create mode 100644 xhiveframework/desk/page/backups/__init__.py create mode 100644 xhiveframework/desk/page/backups/backups.css create mode 100644 xhiveframework/desk/page/backups/backups.html create mode 100644 xhiveframework/desk/page/backups/backups.js create mode 100644 xhiveframework/desk/page/backups/backups.json create mode 100644 xhiveframework/desk/page/backups/backups.py create mode 100644 xhiveframework/desk/page/leaderboard/__init__.py create mode 100644 xhiveframework/desk/page/leaderboard/leaderboard.css create mode 100644 xhiveframework/desk/page/leaderboard/leaderboard.js create mode 100644 xhiveframework/desk/page/leaderboard/leaderboard.json create mode 100644 xhiveframework/desk/page/leaderboard/leaderboard.py create mode 100644 xhiveframework/desk/page/setup_wizard/__init__.py create mode 100644 xhiveframework/desk/page/setup_wizard/install_fixtures.py create mode 100644 xhiveframework/desk/page/setup_wizard/setup_wizard.js create mode 100644 xhiveframework/desk/page/setup_wizard/setup_wizard.json create mode 100755 xhiveframework/desk/page/setup_wizard/setup_wizard.py create mode 100644 xhiveframework/desk/page/user_profile/__init__.py create mode 100644 xhiveframework/desk/page/user_profile/user_profile.css create mode 100644 xhiveframework/desk/page/user_profile/user_profile.html create mode 100644 xhiveframework/desk/page/user_profile/user_profile.js create mode 100644 xhiveframework/desk/page/user_profile/user_profile.json create mode 100644 xhiveframework/desk/page/user_profile/user_profile.py create mode 100644 xhiveframework/desk/page/user_profile/user_profile_controller.js create mode 100644 xhiveframework/desk/page/user_profile/user_profile_sidebar.html create mode 100644 xhiveframework/desk/query_report.py create mode 100644 xhiveframework/desk/report/__init__.py create mode 100644 xhiveframework/desk/report/todo/__init__.py create mode 100644 xhiveframework/desk/report/todo/todo.js create mode 100644 xhiveframework/desk/report/todo/todo.json create mode 100644 xhiveframework/desk/report/todo/todo.py create mode 100644 xhiveframework/desk/reportview.py create mode 100644 xhiveframework/desk/search.py create mode 100644 xhiveframework/desk/treeview.py create mode 100644 xhiveframework/desk/utils.py create mode 100644 xhiveframework/email/__init__.py create mode 100644 xhiveframework/email/assets/images/email-pull-flow.png create mode 100644 xhiveframework/email/doctype/__init__.py create mode 100644 xhiveframework/email/doctype/auto_email_report/__init__.py create mode 100644 xhiveframework/email/doctype/auto_email_report/auto_email_report.js create mode 100644 xhiveframework/email/doctype/auto_email_report/auto_email_report.json create mode 100644 xhiveframework/email/doctype/auto_email_report/auto_email_report.py create mode 100644 xhiveframework/email/doctype/auto_email_report/test_auto_email_report.py create mode 100644 xhiveframework/email/doctype/document_follow/__init__.py create mode 100644 xhiveframework/email/doctype/document_follow/document_follow.js create mode 100644 xhiveframework/email/doctype/document_follow/document_follow.json create mode 100644 xhiveframework/email/doctype/document_follow/document_follow.py create mode 100644 xhiveframework/email/doctype/document_follow/test_document_follow.py create mode 100644 xhiveframework/email/doctype/email_account/__init__.py create mode 100644 xhiveframework/email/doctype/email_account/email_account.js create mode 100644 xhiveframework/email/doctype/email_account/email_account.json create mode 100755 xhiveframework/email/doctype/email_account/email_account.py create mode 100644 xhiveframework/email/doctype/email_account/email_account_list.js create mode 100644 xhiveframework/email/doctype/email_account/test_email_account.py create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-1.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-2.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-3.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-4.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-self-sent.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/reply-1.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/reply-2.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/reply-3.raw create mode 100644 xhiveframework/email/doctype/email_account/test_mails/reply-4.raw create mode 100644 xhiveframework/email/doctype/email_account/test_records.json create mode 100644 xhiveframework/email/doctype/email_domain/__init__.py create mode 100644 xhiveframework/email/doctype/email_domain/email_domain.js create mode 100644 xhiveframework/email/doctype/email_domain/email_domain.json create mode 100644 xhiveframework/email/doctype/email_domain/email_domain.py create mode 100644 xhiveframework/email/doctype/email_domain/test_email_domain.py create mode 100644 xhiveframework/email/doctype/email_domain/test_records.json create mode 100644 xhiveframework/email/doctype/email_flag_queue/__init__.py create mode 100644 xhiveframework/email/doctype/email_flag_queue/email_flag_queue.js create mode 100644 xhiveframework/email/doctype/email_flag_queue/email_flag_queue.json create mode 100644 xhiveframework/email/doctype/email_flag_queue/email_flag_queue.py create mode 100644 xhiveframework/email/doctype/email_flag_queue/test_email_flag_queue.py create mode 100644 xhiveframework/email/doctype/email_group/__init__.py create mode 100644 xhiveframework/email/doctype/email_group/email_group.js create mode 100644 xhiveframework/email/doctype/email_group/email_group.json create mode 100755 xhiveframework/email/doctype/email_group/email_group.py create mode 100644 xhiveframework/email/doctype/email_group/test_email_group.py create mode 100644 xhiveframework/email/doctype/email_group/test_records.json create mode 100644 xhiveframework/email/doctype/email_group_member/__init__.py create mode 100644 xhiveframework/email/doctype/email_group_member/email_group_member.js create mode 100644 xhiveframework/email/doctype/email_group_member/email_group_member.json create mode 100644 xhiveframework/email/doctype/email_group_member/email_group_member.py create mode 100644 xhiveframework/email/doctype/email_group_member/test_email_group_member.py create mode 100644 xhiveframework/email/doctype/email_queue/__init__.py create mode 100644 xhiveframework/email/doctype/email_queue/email_queue.js create mode 100644 xhiveframework/email/doctype/email_queue/email_queue.json create mode 100644 xhiveframework/email/doctype/email_queue/email_queue.py create mode 100644 xhiveframework/email/doctype/email_queue/email_queue_list.js create mode 100644 xhiveframework/email/doctype/email_queue/patches/__init__.py create mode 100644 xhiveframework/email/doctype/email_queue/patches/drop_search_index_on_message_id.py create mode 100644 xhiveframework/email/doctype/email_queue/test_email_queue.py create mode 100644 xhiveframework/email/doctype/email_queue_recipient/__init__.py create mode 100644 xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.json create mode 100644 xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.py create mode 100644 xhiveframework/email/doctype/email_rule/__init__.py create mode 100644 xhiveframework/email/doctype/email_rule/email_rule.js create mode 100644 xhiveframework/email/doctype/email_rule/email_rule.json create mode 100644 xhiveframework/email/doctype/email_rule/email_rule.py create mode 100644 xhiveframework/email/doctype/email_rule/test_email_rule.py create mode 100644 xhiveframework/email/doctype/email_template/__init__.py create mode 100644 xhiveframework/email/doctype/email_template/email_template.js create mode 100644 xhiveframework/email/doctype/email_template/email_template.json create mode 100644 xhiveframework/email/doctype/email_template/email_template.py create mode 100644 xhiveframework/email/doctype/email_template/test_email_template.py create mode 100644 xhiveframework/email/doctype/email_unsubscribe/__init__.py create mode 100644 xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.js create mode 100644 xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.json create mode 100644 xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.py create mode 100644 xhiveframework/email/doctype/email_unsubscribe/test_email_unsubscribe.py create mode 100644 xhiveframework/email/doctype/imap_folder/__init__.py create mode 100644 xhiveframework/email/doctype/imap_folder/imap_folder.json create mode 100644 xhiveframework/email/doctype/imap_folder/imap_folder.py create mode 100644 xhiveframework/email/doctype/newsletter/__init__.py create mode 100644 xhiveframework/email/doctype/newsletter/exceptions.py create mode 100644 xhiveframework/email/doctype/newsletter/newsletter.js create mode 100644 xhiveframework/email/doctype/newsletter/newsletter.json create mode 100644 xhiveframework/email/doctype/newsletter/newsletter.py create mode 100644 xhiveframework/email/doctype/newsletter/newsletter_list.js create mode 100644 xhiveframework/email/doctype/newsletter/templates/newsletter.html create mode 100644 xhiveframework/email/doctype/newsletter/templates/newsletter_row.html create mode 100644 xhiveframework/email/doctype/newsletter/test_newsletter.py create mode 100644 xhiveframework/email/doctype/newsletter_attachment/__init__.py create mode 100644 xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.json create mode 100644 xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.py create mode 100644 xhiveframework/email/doctype/newsletter_email_group/__init__.py create mode 100644 xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.json create mode 100644 xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.py create mode 100644 xhiveframework/email/doctype/notification/__init__.py create mode 100644 xhiveframework/email/doctype/notification/notification.js create mode 100644 xhiveframework/email/doctype/notification/notification.json create mode 100644 xhiveframework/email/doctype/notification/notification.py create mode 100644 xhiveframework/email/doctype/notification/test_notification.py create mode 100644 xhiveframework/email/doctype/notification/test_records.json create mode 100644 xhiveframework/email/doctype/notification_recipient/__init__.py create mode 100644 xhiveframework/email/doctype/notification_recipient/notification_recipient.json create mode 100644 xhiveframework/email/doctype/notification_recipient/notification_recipient.py create mode 100644 xhiveframework/email/doctype/unhandled_email/__init__.py create mode 100644 xhiveframework/email/doctype/unhandled_email/test_unhandled_email.py create mode 100644 xhiveframework/email/doctype/unhandled_email/unhandled_email.json create mode 100644 xhiveframework/email/doctype/unhandled_email/unhandled_email.py create mode 100644 xhiveframework/email/email.md create mode 100755 xhiveframework/email/email_body.py create mode 100644 xhiveframework/email/inbox.py create mode 100644 xhiveframework/email/oauth.py create mode 100644 xhiveframework/email/page/__init__.py create mode 100755 xhiveframework/email/queue.py create mode 100644 xhiveframework/email/receive.py create mode 100644 xhiveframework/email/smtp.py create mode 100644 xhiveframework/email/test_email_body.py create mode 100644 xhiveframework/email/test_smtp.py create mode 100644 xhiveframework/email/utils.py create mode 100644 xhiveframework/exceptions.py create mode 100644 xhiveframework/geo/__init__.py create mode 100644 xhiveframework/geo/country_info.json create mode 100644 xhiveframework/geo/country_info.py create mode 100644 xhiveframework/geo/doctype/__init__.py create mode 100644 xhiveframework/geo/doctype/country/README.md create mode 100644 xhiveframework/geo/doctype/country/__init__.py create mode 100644 xhiveframework/geo/doctype/country/country.js create mode 100644 xhiveframework/geo/doctype/country/country.json create mode 100644 xhiveframework/geo/doctype/country/country.py create mode 100644 xhiveframework/geo/doctype/country/test_country.py create mode 100644 xhiveframework/geo/doctype/country/test_records.json create mode 100644 xhiveframework/geo/doctype/currency/README.md create mode 100644 xhiveframework/geo/doctype/currency/__init__.py create mode 100644 xhiveframework/geo/doctype/currency/currency.js create mode 100644 xhiveframework/geo/doctype/currency/currency.json create mode 100644 xhiveframework/geo/doctype/currency/currency.py create mode 100644 xhiveframework/geo/doctype/currency/test_currency.py create mode 100644 xhiveframework/geo/doctype/currency/test_records.json create mode 100644 xhiveframework/geo/languages.json create mode 100644 xhiveframework/geo/report/__init__.py create mode 100644 xhiveframework/geo/utils.py create mode 100644 xhiveframework/gettext/extractors/navbar.py create mode 100644 xhiveframework/handler.py create mode 100644 xhiveframework/hooks.py create mode 100644 xhiveframework/installer.py create mode 100644 xhiveframework/integrations/__init__.py create mode 100644 xhiveframework/integrations/doctype/__init__.py create mode 100644 xhiveframework/integrations/doctype/connected_app/__init__.py create mode 100644 xhiveframework/integrations/doctype/connected_app/connected_app.js create mode 100644 xhiveframework/integrations/doctype/connected_app/connected_app.json create mode 100644 xhiveframework/integrations/doctype/connected_app/connected_app.py create mode 100644 xhiveframework/integrations/doctype/connected_app/test_connected_app.py create mode 100644 xhiveframework/integrations/doctype/connected_app/test_records.json create mode 100644 xhiveframework/integrations/doctype/dropbox_settings/__init__.py create mode 100644 xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.js create mode 100644 xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.json create mode 100644 xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.py create mode 100644 xhiveframework/integrations/doctype/dropbox_settings/test_dropbox_settings.py create mode 100644 xhiveframework/integrations/doctype/google_calendar/__init__.py create mode 100644 xhiveframework/integrations/doctype/google_calendar/google_calendar.js create mode 100644 xhiveframework/integrations/doctype/google_calendar/google_calendar.json create mode 100644 xhiveframework/integrations/doctype/google_calendar/google_calendar.py create mode 100644 xhiveframework/integrations/doctype/google_contacts/__init__.py create mode 100644 xhiveframework/integrations/doctype/google_contacts/google_contacts.js create mode 100644 xhiveframework/integrations/doctype/google_contacts/google_contacts.json create mode 100644 xhiveframework/integrations/doctype/google_contacts/google_contacts.py create mode 100644 xhiveframework/integrations/doctype/google_contacts/test_google_contacts.py create mode 100644 xhiveframework/integrations/doctype/google_drive/__init__.py create mode 100644 xhiveframework/integrations/doctype/google_drive/google_drive.js create mode 100644 xhiveframework/integrations/doctype/google_drive/google_drive.json create mode 100644 xhiveframework/integrations/doctype/google_drive/google_drive.py create mode 100644 xhiveframework/integrations/doctype/google_drive/test_google_drive.py create mode 100644 xhiveframework/integrations/doctype/google_settings/__init__.py create mode 100644 xhiveframework/integrations/doctype/google_settings/google_settings.js create mode 100644 xhiveframework/integrations/doctype/google_settings/google_settings.json create mode 100644 xhiveframework/integrations/doctype/google_settings/google_settings.py create mode 100644 xhiveframework/integrations/doctype/google_settings/test_google_settings.py create mode 100644 xhiveframework/integrations/doctype/integration_request/__init__.py create mode 100644 xhiveframework/integrations/doctype/integration_request/integration_request.js create mode 100644 xhiveframework/integrations/doctype/integration_request/integration_request.json create mode 100644 xhiveframework/integrations/doctype/integration_request/integration_request.py create mode 100644 xhiveframework/integrations/doctype/integration_request/integration_request_list.js create mode 100644 xhiveframework/integrations/doctype/integration_request/test_integration_request.py create mode 100644 xhiveframework/integrations/doctype/ldap_group_mapping/__init__.py create mode 100644 xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json create mode 100644 xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py create mode 100644 xhiveframework/integrations/doctype/ldap_settings/__init__.py create mode 100644 xhiveframework/integrations/doctype/ldap_settings/ldap_settings.js create mode 100644 xhiveframework/integrations/doctype/ldap_settings/ldap_settings.json create mode 100644 xhiveframework/integrations/doctype/ldap_settings/ldap_settings.py create mode 100644 xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_activedirectory.json create mode 100644 xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_openldap.json create mode 100644 xhiveframework/integrations/doctype/ldap_settings/test_ldap_settings.py create mode 100644 xhiveframework/integrations/doctype/oauth_authorization_code/__init__.py create mode 100644 xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.js create mode 100644 xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json create mode 100644 xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py create mode 100644 xhiveframework/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py create mode 100644 xhiveframework/integrations/doctype/oauth_bearer_token/__init__.py create mode 100644 xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.js create mode 100644 xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json create mode 100644 xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py create mode 100644 xhiveframework/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py create mode 100644 xhiveframework/integrations/doctype/oauth_client/__init__.py create mode 100644 xhiveframework/integrations/doctype/oauth_client/oauth_client.js create mode 100644 xhiveframework/integrations/doctype/oauth_client/oauth_client.json create mode 100644 xhiveframework/integrations/doctype/oauth_client/oauth_client.py create mode 100644 xhiveframework/integrations/doctype/oauth_client/test_oauth_client.py create mode 100644 xhiveframework/integrations/doctype/oauth_provider_settings/__init__.py create mode 100644 xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.js create mode 100644 xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.json create mode 100644 xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py create mode 100644 xhiveframework/integrations/doctype/oauth_scope/__init__.py create mode 100644 xhiveframework/integrations/doctype/oauth_scope/oauth_scope.json create mode 100644 xhiveframework/integrations/doctype/oauth_scope/oauth_scope.py create mode 100644 xhiveframework/integrations/doctype/push_notification_settings/__init__.py create mode 100644 xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.js create mode 100644 xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.json create mode 100644 xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.py create mode 100644 xhiveframework/integrations/doctype/push_notification_settings/test_push_notification_settings.py create mode 100644 xhiveframework/integrations/doctype/query_parameters/__init__.py create mode 100644 xhiveframework/integrations/doctype/query_parameters/query_parameters.json create mode 100644 xhiveframework/integrations/doctype/query_parameters/query_parameters.py create mode 100755 xhiveframework/integrations/doctype/s3_backup_settings/__init__.py create mode 100755 xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.js create mode 100755 xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.json create mode 100755 xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.py create mode 100755 xhiveframework/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py create mode 100644 xhiveframework/integrations/doctype/slack_webhook_url/__init__.py create mode 100644 xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.js create mode 100644 xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.json create mode 100644 xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.py create mode 100644 xhiveframework/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py create mode 100644 xhiveframework/integrations/doctype/social_login_key/__init__.py create mode 100644 xhiveframework/integrations/doctype/social_login_key/social_login_key.js create mode 100644 xhiveframework/integrations/doctype/social_login_key/social_login_key.json create mode 100644 xhiveframework/integrations/doctype/social_login_key/social_login_key.py create mode 100644 xhiveframework/integrations/doctype/social_login_key/test_social_login_key.py create mode 100644 xhiveframework/integrations/doctype/social_login_keys/__init__.py create mode 100644 xhiveframework/integrations/doctype/social_login_keys/social_login_keys.py create mode 100644 xhiveframework/integrations/doctype/token_cache/__init__.py create mode 100644 xhiveframework/integrations/doctype/token_cache/test_records.json create mode 100644 xhiveframework/integrations/doctype/token_cache/test_token_cache.py create mode 100644 xhiveframework/integrations/doctype/token_cache/token_cache.js create mode 100644 xhiveframework/integrations/doctype/token_cache/token_cache.json create mode 100644 xhiveframework/integrations/doctype/token_cache/token_cache.py create mode 100644 xhiveframework/integrations/doctype/webhook/__init__.py create mode 100644 xhiveframework/integrations/doctype/webhook/test_webhook.py create mode 100644 xhiveframework/integrations/doctype/webhook/webhook.js create mode 100644 xhiveframework/integrations/doctype/webhook/webhook.json create mode 100644 xhiveframework/integrations/doctype/webhook/webhook.py create mode 100644 xhiveframework/integrations/doctype/webhook_data/__init__.py create mode 100644 xhiveframework/integrations/doctype/webhook_data/webhook_data.json create mode 100644 xhiveframework/integrations/doctype/webhook_data/webhook_data.py create mode 100644 xhiveframework/integrations/doctype/webhook_header/__init__.py create mode 100644 xhiveframework/integrations/doctype/webhook_header/webhook_header.json create mode 100644 xhiveframework/integrations/doctype/webhook_header/webhook_header.py create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/__init__.py create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/test_webhook_request_log.py create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.js create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.json create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.py create mode 100644 xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log_list.js create mode 100644 xhiveframework/integrations/google_oauth.py create mode 100644 xhiveframework/integrations/oauth2.py create mode 100644 xhiveframework/integrations/oauth2_logins.py create mode 100644 xhiveframework/integrations/offsite_backup_utils.py create mode 100644 xhiveframework/integrations/utils.py create mode 100644 xhiveframework/integrations/workspace/integrations/integrations.json create mode 100644 xhiveframework/integrations/xhiveframework_providers/__init__.py create mode 100644 xhiveframework/integrations/xhiveframework_providers/xhiveframeworkcloud.py create mode 100644 xhiveframework/middlewares.py create mode 100644 xhiveframework/migrate.py create mode 100644 xhiveframework/model/__init__.py create mode 100644 xhiveframework/model/base_document.py create mode 100644 xhiveframework/model/create_new.py create mode 100644 xhiveframework/model/db_query.py create mode 100644 xhiveframework/model/delete_doc.py create mode 100644 xhiveframework/model/docfield.py create mode 100644 xhiveframework/model/docstatus.py create mode 100644 xhiveframework/model/document.py create mode 100644 xhiveframework/model/dynamic_links.py create mode 100644 xhiveframework/model/mapper.py create mode 100644 xhiveframework/model/meta.py create mode 100644 xhiveframework/model/naming.py create mode 100644 xhiveframework/model/rename_doc.py create mode 100644 xhiveframework/model/sync.py create mode 100644 xhiveframework/model/utils/__init__.py create mode 100644 xhiveframework/model/utils/link_count.py create mode 100644 xhiveframework/model/utils/rename_doc.py create mode 100644 xhiveframework/model/utils/rename_field.py create mode 100644 xhiveframework/model/utils/user_settings.py create mode 100644 xhiveframework/model/virtual_doctype.py create mode 100644 xhiveframework/model/workflow.py create mode 100644 xhiveframework/modules.txt create mode 100644 xhiveframework/modules/__init__.py create mode 100644 xhiveframework/modules/export_file.py create mode 100644 xhiveframework/modules/import_file.py create mode 100644 xhiveframework/modules/patch_handler.py create mode 100644 xhiveframework/modules/utils.py create mode 100644 xhiveframework/monitor.py create mode 100644 xhiveframework/oauth.py create mode 100644 xhiveframework/parallel_test_runner.py create mode 100644 xhiveframework/patches.txt create mode 100644 xhiveframework/patches/__init__.py create mode 100644 xhiveframework/patches/v10_0/__init__.py create mode 100644 xhiveframework/patches/v10_0/enable_chat_by_default_within_system_settings.py create mode 100644 xhiveframework/patches/v10_0/enhance_security.py create mode 100644 xhiveframework/patches/v10_0/increase_single_table_column_length.py create mode 100644 xhiveframework/patches/v10_0/modify_naming_series_table.py create mode 100644 xhiveframework/patches/v10_0/modify_smallest_currency_fraction.py create mode 100644 xhiveframework/patches/v10_0/refactor_social_login_keys.py create mode 100644 xhiveframework/patches/v10_0/reload_countries_and_currencies.py create mode 100644 xhiveframework/patches/v10_0/remove_custom_field_for_disabled_domain.py create mode 100644 xhiveframework/patches/v10_0/set_default_locking_time.py create mode 100644 xhiveframework/patches/v10_0/set_no_copy_to_workflow_state.py create mode 100644 xhiveframework/patches/v11_0/__init__.py create mode 100644 xhiveframework/patches/v11_0/apply_customization_to_custom_doctype.py create mode 100644 xhiveframework/patches/v11_0/change_email_signature_fieldtype.py create mode 100644 xhiveframework/patches/v11_0/copy_fetch_data_from_options.py create mode 100644 xhiveframework/patches/v11_0/create_contact_for_user.py create mode 100644 xhiveframework/patches/v11_0/delete_all_prepared_reports.py create mode 100644 xhiveframework/patches/v11_0/delete_duplicate_user_permissions.py create mode 100644 xhiveframework/patches/v11_0/drop_column_apply_user_permissions.py create mode 100644 xhiveframework/patches/v11_0/fix_order_by_in_reports_json.py create mode 100644 xhiveframework/patches/v11_0/make_all_prepared_report_attachments_private.py create mode 100644 xhiveframework/patches/v11_0/migrate_report_settings_for_new_listview.py create mode 100644 xhiveframework/patches/v11_0/multiple_references_in_events.py create mode 100644 xhiveframework/patches/v11_0/reload_and_rename_view_log.py create mode 100644 xhiveframework/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py create mode 100644 xhiveframework/patches/v11_0/remove_skip_for_doctype.py create mode 100644 xhiveframework/patches/v11_0/rename_email_alert_to_notification.py create mode 100644 xhiveframework/patches/v11_0/rename_standard_reply_to_email_template.py create mode 100644 xhiveframework/patches/v11_0/rename_workflow_action_to_workflow_action_master.py create mode 100644 xhiveframework/patches/v11_0/replicate_old_user_permissions.py create mode 100644 xhiveframework/patches/v11_0/set_allow_self_approval_in_workflow.py create mode 100644 xhiveframework/patches/v11_0/set_default_letter_head_source.py create mode 100644 xhiveframework/patches/v11_0/set_dropbox_file_backup.py create mode 100644 xhiveframework/patches/v11_0/set_missing_creation_and_modified_value_for_user_permissions.py create mode 100644 xhiveframework/patches/v11_0/update_list_user_settings.py create mode 100644 xhiveframework/patches/v12_0/__init__.py create mode 100644 xhiveframework/patches/v12_0/change_existing_dashboard_chart_filters.py create mode 100644 xhiveframework/patches/v12_0/create_notification_settings_for_user.py create mode 100644 xhiveframework/patches/v12_0/delete_duplicate_indexes.py create mode 100644 xhiveframework/patches/v12_0/delete_feedback_request_if_exists.py create mode 100644 xhiveframework/patches/v12_0/fix_email_id_formatting.py create mode 100644 xhiveframework/patches/v12_0/fix_public_private_files.py create mode 100644 xhiveframework/patches/v12_0/move_email_and_phone_to_child_table.py create mode 100644 xhiveframework/patches/v12_0/move_form_attachments_to_attachments_folder.py create mode 100644 xhiveframework/patches/v12_0/move_timeline_links_to_dynamic_links.py create mode 100644 xhiveframework/patches/v12_0/remove_deprecated_fields_from_doctype.py create mode 100644 xhiveframework/patches/v12_0/remove_example_email_thread_notify.py create mode 100644 xhiveframework/patches/v12_0/remove_feedback_rating.py create mode 100644 xhiveframework/patches/v12_0/rename_events_repeat_on.py create mode 100644 xhiveframework/patches/v12_0/rename_uploaded_files_with_proper_name.py create mode 100644 xhiveframework/patches/v12_0/replace_null_values_in_tables.py create mode 100644 xhiveframework/patches/v12_0/reset_home_settings.py create mode 100644 xhiveframework/patches/v12_0/set_correct_assign_value_in_docs.py create mode 100644 xhiveframework/patches/v12_0/set_correct_url_in_files.py create mode 100644 xhiveframework/patches/v12_0/set_default_incoming_email_port.py create mode 100644 xhiveframework/patches/v12_0/set_default_password_reset_limit.py create mode 100644 xhiveframework/patches/v12_0/set_primary_key_in_series.py create mode 100644 xhiveframework/patches/v12_0/setup_comments_from_communications.py create mode 100644 xhiveframework/patches/v12_0/setup_email_linking.py create mode 100644 xhiveframework/patches/v12_0/setup_tags.py create mode 100644 xhiveframework/patches/v12_0/update_auto_repeat_status_and_not_submittable.py create mode 100644 xhiveframework/patches/v12_0/update_global_search.py create mode 100644 xhiveframework/patches/v12_0/update_print_format_type.py create mode 100644 xhiveframework/patches/v13_0/__init__.py create mode 100644 xhiveframework/patches/v13_0/add_standard_navbar_items.py create mode 100644 xhiveframework/patches/v13_0/add_switch_theme_to_navbar_settings.py create mode 100644 xhiveframework/patches/v13_0/add_toggle_width_in_navbar_settings.py create mode 100644 xhiveframework/patches/v13_0/create_custom_dashboards_cards_and_charts.py create mode 100644 xhiveframework/patches/v13_0/delete_package_publish_tool.py create mode 100644 xhiveframework/patches/v13_0/email_unsubscribe.py create mode 100644 xhiveframework/patches/v13_0/enable_custom_script.py create mode 100644 xhiveframework/patches/v13_0/encrypt_2fa_secrets.py create mode 100644 xhiveframework/patches/v13_0/generate_theme_files_in_public_folder.py create mode 100644 xhiveframework/patches/v13_0/increase_password_length.py create mode 100644 xhiveframework/patches/v13_0/jinja_hook.py create mode 100644 xhiveframework/patches/v13_0/make_user_type.py create mode 100644 xhiveframework/patches/v13_0/migrate_translation_column_data.py create mode 100644 xhiveframework/patches/v13_0/queryreport_columns.py create mode 100644 xhiveframework/patches/v13_0/remove_chat.py create mode 100644 xhiveframework/patches/v13_0/remove_custom_link.py create mode 100644 xhiveframework/patches/v13_0/remove_duplicate_navbar_items.py create mode 100644 xhiveframework/patches/v13_0/remove_invalid_options_for_data_fields.py create mode 100644 xhiveframework/patches/v13_0/remove_share_for_std_users.py create mode 100644 xhiveframework/patches/v13_0/remove_tailwind_from_page_builder.py create mode 100644 xhiveframework/patches/v13_0/remove_twilio_settings.py create mode 100644 xhiveframework/patches/v13_0/remove_web_view.py create mode 100644 xhiveframework/patches/v13_0/rename_custom_client_script.py create mode 100644 xhiveframework/patches/v13_0/rename_desk_page_to_workspace.py create mode 100644 xhiveframework/patches/v13_0/rename_is_custom_field_in_dashboard_chart.py create mode 100644 xhiveframework/patches/v13_0/rename_list_view_setting_to_list_view_settings.py create mode 100644 xhiveframework/patches/v13_0/rename_notification_fields.py create mode 100644 xhiveframework/patches/v13_0/rename_onboarding.py create mode 100644 xhiveframework/patches/v13_0/replace_field_target_with_open_in_new_tab.py create mode 100644 xhiveframework/patches/v13_0/replace_old_data_import.py create mode 100644 xhiveframework/patches/v13_0/reset_corrupt_defaults.py create mode 100644 xhiveframework/patches/v13_0/set_existing_dashboard_charts_as_public.py create mode 100644 xhiveframework/patches/v13_0/set_first_day_of_the_week.py create mode 100644 xhiveframework/patches/v13_0/set_path_for_homepage_in_web_page_view.py create mode 100644 xhiveframework/patches/v13_0/set_read_times.py create mode 100644 xhiveframework/patches/v13_0/set_route_for_blog_category.py create mode 100644 xhiveframework/patches/v13_0/set_social_icons.py create mode 100644 xhiveframework/patches/v13_0/set_unique_for_page_view.py create mode 100644 xhiveframework/patches/v13_0/site_wise_logging.py create mode 100644 xhiveframework/patches/v13_0/update_date_filters_in_user_settings.py create mode 100644 xhiveframework/patches/v13_0/update_duration_options.py create mode 100644 xhiveframework/patches/v13_0/update_icons_in_customized_desk_pages.py create mode 100644 xhiveframework/patches/v13_0/update_newsletter_content_type.py create mode 100644 xhiveframework/patches/v13_0/update_notification_channel_if_empty.py create mode 100644 xhiveframework/patches/v13_0/web_template_set_module.py create mode 100644 xhiveframework/patches/v13_0/website_theme_custom_scss.py create mode 100644 xhiveframework/patches/v14_0/__init__.py create mode 100644 xhiveframework/patches/v14_0/clear_long_pending_stale_logs.py create mode 100644 xhiveframework/patches/v14_0/copy_mail_data.py create mode 100644 xhiveframework/patches/v14_0/delete_data_migration_tool.py create mode 100644 xhiveframework/patches/v14_0/delete_payment_gateways.py create mode 100644 xhiveframework/patches/v14_0/different_encryption_key.py create mode 100644 xhiveframework/patches/v14_0/disable_email_accounts_with_oauth.py create mode 100644 xhiveframework/patches/v14_0/drop_data_import_legacy.py create mode 100644 xhiveframework/patches/v14_0/drop_unused_indexes.py create mode 100644 xhiveframework/patches/v14_0/log_settings_migration.py create mode 100644 xhiveframework/patches/v14_0/modify_value_column_size_for_singles.py create mode 100644 xhiveframework/patches/v14_0/remove_db_aggregation.py create mode 100644 xhiveframework/patches/v14_0/remove_is_first_startup.py create mode 100644 xhiveframework/patches/v14_0/remove_manage_subscriptions_from_navbar.py create mode 100644 xhiveframework/patches/v14_0/remove_post_and_post_comment.py create mode 100644 xhiveframework/patches/v14_0/reset_creation_datetime.py create mode 100644 xhiveframework/patches/v14_0/save_ratings_in_fraction.py create mode 100644 xhiveframework/patches/v14_0/set_document_expiry_default.py create mode 100644 xhiveframework/patches/v14_0/set_suspend_email_queue_default.py create mode 100644 xhiveframework/patches/v14_0/setup_likes_from_feedback.py create mode 100644 xhiveframework/patches/v14_0/transform_todo_schema.py create mode 100644 xhiveframework/patches/v14_0/update_attachment_comment.py create mode 100644 xhiveframework/patches/v14_0/update_auto_account_deletion_duration.py create mode 100644 xhiveframework/patches/v14_0/update_color_names_in_kanban_board_column.py create mode 100644 xhiveframework/patches/v14_0/update_github_endpoints.py create mode 100644 xhiveframework/patches/v14_0/update_integration_request.py create mode 100644 xhiveframework/patches/v14_0/update_is_system_generated_flag.py create mode 100644 xhiveframework/patches/v14_0/update_multistep_webforms.py create mode 100644 xhiveframework/patches/v14_0/update_webforms.py create mode 100644 xhiveframework/patches/v14_0/update_workspace2.py create mode 100644 xhiveframework/patches/v15_0/copy_disable_prepared_report_to_prepared_report.py create mode 100644 xhiveframework/patches/v15_0/drop_modified_index.py create mode 100644 xhiveframework/patches/v15_0/move_event_cancelled_to_status.py create mode 100644 xhiveframework/patches/v15_0/remove_background_jobs_from_dropdown.py create mode 100644 xhiveframework/patches/v15_0/remove_event_streaming.py create mode 100644 xhiveframework/patches/v15_0/remove_implicit_primary_key.py create mode 100644 xhiveframework/patches/v15_0/remove_prepared_report_settings_from_system_settings.py create mode 100644 xhiveframework/patches/v15_0/sanitize_workspace_titles.py create mode 100644 xhiveframework/patches/v15_0/set_contact_full_name.py create mode 100644 xhiveframework/patches/v15_0/set_file_type.py create mode 100644 xhiveframework/patches/v15_0/validate_newsletter_recipients.py create mode 100644 xhiveframework/permissions.py create mode 100644 xhiveframework/printing/__init__.py create mode 100644 xhiveframework/printing/doctype/__init__.py create mode 100644 xhiveframework/printing/doctype/letter_head/__init__.py create mode 100644 xhiveframework/printing/doctype/letter_head/letter_head.js create mode 100644 xhiveframework/printing/doctype/letter_head/letter_head.json create mode 100644 xhiveframework/printing/doctype/letter_head/letter_head.py create mode 100644 xhiveframework/printing/doctype/letter_head/test_letter_head.py create mode 100644 xhiveframework/printing/doctype/network_printer_settings/__init__.py create mode 100644 xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.js create mode 100644 xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.json create mode 100644 xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.py create mode 100644 xhiveframework/printing/doctype/network_printer_settings/test_network_printer_settings.py create mode 100644 xhiveframework/printing/doctype/print_format/__init__.py create mode 100644 xhiveframework/printing/doctype/print_format/print_format.js create mode 100644 xhiveframework/printing/doctype/print_format/print_format.json create mode 100644 xhiveframework/printing/doctype/print_format/print_format.py create mode 100644 xhiveframework/printing/doctype/print_format/test_print_format.py create mode 100644 xhiveframework/printing/doctype/print_format/test_records.json create mode 100644 xhiveframework/printing/doctype/print_format_field_template/__init__.py create mode 100644 xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.js create mode 100644 xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.json create mode 100644 xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.py create mode 100644 xhiveframework/printing/doctype/print_format_field_template/test_print_format_field_template.py create mode 100644 xhiveframework/printing/doctype/print_heading/__init__.py create mode 100644 xhiveframework/printing/doctype/print_heading/print_heading.js create mode 100644 xhiveframework/printing/doctype/print_heading/print_heading.json create mode 100644 xhiveframework/printing/doctype/print_heading/print_heading.py create mode 100644 xhiveframework/printing/doctype/print_heading/test_print_heading.py create mode 100644 xhiveframework/printing/doctype/print_settings/__init__.py create mode 100644 xhiveframework/printing/doctype/print_settings/print_settings.js create mode 100644 xhiveframework/printing/doctype/print_settings/print_settings.json create mode 100644 xhiveframework/printing/doctype/print_settings/print_settings.py create mode 100644 xhiveframework/printing/doctype/print_settings/test_print_settings.py create mode 100644 xhiveframework/printing/doctype/print_style/__init__.py create mode 100644 xhiveframework/printing/doctype/print_style/print_style.js create mode 100644 xhiveframework/printing/doctype/print_style/print_style.json create mode 100644 xhiveframework/printing/doctype/print_style/print_style.py create mode 100644 xhiveframework/printing/doctype/print_style/test_print_style.py create mode 100644 xhiveframework/printing/form_tour/letter_head/letter_head.json create mode 100644 xhiveframework/printing/form_tour/print_format/print_format.json create mode 100644 xhiveframework/printing/page/__init__.py create mode 100644 xhiveframework/printing/page/print/__init__.py create mode 100644 xhiveframework/printing/page/print/print.js create mode 100644 xhiveframework/printing/page/print/print.json create mode 100644 xhiveframework/printing/page/print/print.py create mode 100644 xhiveframework/printing/page/print/print_skeleton_loading.html create mode 100644 xhiveframework/printing/page/print_format_builder/__init__.py create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder.css create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder.js create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder.json create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder.py create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_column_selector.html create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_field.html create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_layout.html create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_section.html create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_sidebar.html create mode 100644 xhiveframework/printing/page/print_format_builder/print_format_builder_start.html create mode 100644 xhiveframework/printing/page/print_format_builder_beta/__init__.py create mode 100644 xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.css create mode 100644 xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.js create mode 100644 xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.json create mode 100644 xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.py create mode 100644 xhiveframework/printing/print_style/__init__.py create mode 100644 xhiveframework/printing/print_style/classic/__init__.py create mode 100644 xhiveframework/printing/print_style/classic/classic.json create mode 100644 xhiveframework/printing/print_style/modern/__init__.py create mode 100644 xhiveframework/printing/print_style/modern/modern.json create mode 100644 xhiveframework/printing/print_style/monochrome/__init__.py create mode 100644 xhiveframework/printing/print_style/monochrome/monochrome.json create mode 100644 xhiveframework/printing/print_style/redesign/__init__.py create mode 100644 xhiveframework/printing/print_style/redesign/redesign.json create mode 100644 xhiveframework/public/css/bootstrap.css create mode 100644 xhiveframework/public/css/fonts/fontawesome/FontAwesome.otf create mode 100644 xhiveframework/public/css/fonts/fontawesome/LICENSE create mode 100644 xhiveframework/public/css/fonts/fontawesome/font-awesome.min.css create mode 100644 xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.eot create mode 100644 xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.svg create mode 100644 xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.ttf create mode 100644 xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.woff create mode 100644 xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Black.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-BlackItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Bold.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-BoldItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-ExtraBold.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-ExtraBoldItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-ExtraLight.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-ExtraLightItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Italic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Light.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-LightItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Medium.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-MediumItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Regular.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-SemiBold.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-SemiBoldItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-Thin.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/Inter-ThinItalic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/InterVariable-Italic.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/InterVariable.woff2 create mode 100644 xhiveframework/public/css/fonts/inter/inter.css create mode 100644 xhiveframework/public/css/fonts/inter/inter.scss create mode 100644 xhiveframework/public/css/hljs-night-owl.css create mode 100755 xhiveframework/public/css/octicons/LICENSE.txt create mode 100755 xhiveframework/public/css/octicons/README.md create mode 100755 xhiveframework/public/css/octicons/octicons-local.ttf create mode 100755 xhiveframework/public/css/octicons/octicons.css create mode 100755 xhiveframework/public/css/octicons/octicons.eot create mode 100755 xhiveframework/public/css/octicons/octicons.less create mode 100755 xhiveframework/public/css/octicons/octicons.scss create mode 100755 xhiveframework/public/css/octicons/octicons.svg create mode 100755 xhiveframework/public/css/octicons/octicons.ttf create mode 100755 xhiveframework/public/css/octicons/octicons.woff create mode 100755 xhiveframework/public/css/octicons/sprockets-octicons.scss create mode 100644 xhiveframework/public/css/tree.css create mode 100644 xhiveframework/public/css/tree_grid.css create mode 100644 xhiveframework/public/html/print_template.html create mode 100644 xhiveframework/public/icons/espresso/icons.svg create mode 100644 xhiveframework/public/icons/social/facebook.svg create mode 100644 xhiveframework/public/icons/social/fair.svg create mode 100644 xhiveframework/public/icons/social/github.svg create mode 100644 xhiveframework/public/icons/social/google.svg create mode 100644 xhiveframework/public/icons/social/google_drive.svg create mode 100644 xhiveframework/public/icons/social/office_365.svg create mode 100644 xhiveframework/public/icons/social/salesforce.svg create mode 100644 xhiveframework/public/icons/social/xhiveframework.svg create mode 100644 xhiveframework/public/icons/timeless/icon-check.svg create mode 100644 xhiveframework/public/icons/timeless/icons.svg create mode 100644 xhiveframework/public/icons/timeless/message.svg create mode 100644 xhiveframework/public/icons/timeless/search.svg create mode 100644 xhiveframework/public/icons/timeless/test.svg create mode 100644 xhiveframework/public/images/background.png create mode 100644 xhiveframework/public/images/color-circle.png create mode 100644 xhiveframework/public/images/default-avatar.png create mode 100755 xhiveframework/public/images/default-skin.svg create mode 100644 xhiveframework/public/images/fallback-thumbnail.jpg create mode 100644 xhiveframework/public/images/form-builder.gif create mode 100644 xhiveframework/public/images/help/print-style-classic.png create mode 100644 xhiveframework/public/images/help/print-style-modern.png create mode 100644 xhiveframework/public/images/help/print-style-monochrome.png create mode 100644 xhiveframework/public/images/help/print-style-standard.png create mode 100644 xhiveframework/public/images/leaflet/layers-2x.png create mode 100644 xhiveframework/public/images/leaflet/layers.png create mode 100644 xhiveframework/public/images/leaflet/leafletmarker-icon.png create mode 100644 xhiveframework/public/images/leaflet/leafletmarker-shadow.png create mode 100644 xhiveframework/public/images/leaflet/lego.png create mode 100644 xhiveframework/public/images/leaflet/marker-icon-2x.png create mode 100644 xhiveframework/public/images/leaflet/marker-icon.png create mode 100644 xhiveframework/public/images/leaflet/marker-shadow.png create mode 100644 xhiveframework/public/images/leaflet/spritesheet-2x.png create mode 100644 xhiveframework/public/images/leaflet/spritesheet.png create mode 100644 xhiveframework/public/images/leaflet/spritesheet.svg create mode 100644 xhiveframework/public/images/signature-placeholder.png create mode 100644 xhiveframework/public/images/smiley.png create mode 100644 xhiveframework/public/images/ui-states/404.png create mode 100644 xhiveframework/public/images/ui-states/empty-app-state.svg create mode 100644 xhiveframework/public/images/ui-states/empty.png create mode 100644 xhiveframework/public/images/ui-states/event-empty-state.svg create mode 100644 xhiveframework/public/images/ui-states/grid-empty-state.svg create mode 100644 xhiveframework/public/images/ui-states/list-empty-state.svg create mode 100644 xhiveframework/public/images/ui-states/notification-empty-state.svg create mode 100644 xhiveframework/public/images/ui-states/search-empty-state.svg create mode 100644 xhiveframework/public/images/ui-states/success-color.png create mode 100644 xhiveframework/public/images/ui/ajax-loader.gif create mode 100644 xhiveframework/public/images/ui/bot.png create mode 100644 xhiveframework/public/images/ui/bubble-tea-happy.svg create mode 100644 xhiveframework/public/images/ui/bubble-tea-smile.svg create mode 100644 xhiveframework/public/images/ui/bubble-tea-sorry.svg create mode 100644 xhiveframework/public/images/up.png create mode 100644 xhiveframework/public/images/workflow-builder.gif create mode 100644 xhiveframework/public/images/xhiveframework-favicon.svg create mode 100644 xhiveframework/public/images/xhiveframework-framework-logo.png create mode 100644 xhiveframework/public/images/xhiveframework-framework-logo.svg create mode 100644 xhiveframework/public/images/xhiveframework-logo.png create mode 100644 xhiveframework/public/js/bootstrap-4-web.bundle.js create mode 100644 xhiveframework/public/js/controls.bundle.js create mode 100644 xhiveframework/public/js/data_import_tools.bundle.js create mode 100644 xhiveframework/public/js/desk.bundle.js create mode 100644 xhiveframework/public/js/dialog.bundle.js create mode 100644 xhiveframework/public/js/form.bundle.js create mode 100644 xhiveframework/public/js/form_builder/FormBuilder.vue create mode 100644 xhiveframework/public/js/form_builder/components/AddFieldButton.vue create mode 100644 xhiveframework/public/js/form_builder/components/Autocomplete.vue create mode 100644 xhiveframework/public/js/form_builder/components/Column.vue create mode 100644 xhiveframework/public/js/form_builder/components/Dropdown.vue create mode 100644 xhiveframework/public/js/form_builder/components/EditableInput.vue create mode 100644 xhiveframework/public/js/form_builder/components/Field.vue create mode 100644 xhiveframework/public/js/form_builder/components/FieldProperties.vue create mode 100644 xhiveframework/public/js/form_builder/components/SearchBox.vue create mode 100644 xhiveframework/public/js/form_builder/components/Section.vue create mode 100644 xhiveframework/public/js/form_builder/components/Sidebar.vue create mode 100644 xhiveframework/public/js/form_builder/components/Tabs.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/AttachControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/ButtonControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/CheckControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/CodeControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/DataControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/FetchFromControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/GeolocationControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/ImageControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/LinkControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/RatingControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/SelectControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/SignatureControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/TableControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/TextControl.vue create mode 100644 xhiveframework/public/js/form_builder/components/controls/TextEditorControl.vue create mode 100644 xhiveframework/public/js/form_builder/form_builder.bundle.js create mode 100644 xhiveframework/public/js/form_builder/globals.js create mode 100644 xhiveframework/public/js/form_builder/store.js create mode 100644 xhiveframework/public/js/form_builder/utils.js create mode 100644 xhiveframework/public/js/integrations/google_drive_picker.js create mode 100644 xhiveframework/public/js/jquery-bootstrap.js create mode 100644 xhiveframework/public/js/lib/fullcalendar/LICENCE.txt create mode 100644 xhiveframework/public/js/lib/fullcalendar/fullcalendar.min.css create mode 100644 xhiveframework/public/js/lib/fullcalendar/fullcalendar.min.js create mode 100644 xhiveframework/public/js/lib/fullcalendar/locale-all.js create mode 100755 xhiveframework/public/js/lib/jSignature.min.js create mode 100644 xhiveframework/public/js/lib/jquery/LICENCE.txt create mode 100644 xhiveframework/public/js/lib/jquery/jquery.min.js create mode 100644 xhiveframework/public/js/lib/leaflet/LICENSE create mode 100644 xhiveframework/public/js/lib/leaflet/leaflet.css create mode 100644 xhiveframework/public/js/lib/leaflet/leaflet.js create mode 100644 xhiveframework/public/js/lib/leaflet_control_locate/L.Control.Locate.css create mode 100644 xhiveframework/public/js/lib/leaflet_control_locate/L.Control.Locate.js create mode 100644 xhiveframework/public/js/lib/leaflet_control_locate/LICENSE create mode 100644 xhiveframework/public/js/lib/leaflet_draw/MIT-LICENSE.md create mode 100644 xhiveframework/public/js/lib/leaflet_draw/leaflet.draw.css create mode 100644 xhiveframework/public/js/lib/leaflet_draw/leaflet.draw.js create mode 100644 xhiveframework/public/js/lib/leaflet_easy_button/LICENSE create mode 100644 xhiveframework/public/js/lib/leaflet_easy_button/easy-button.css create mode 100644 xhiveframework/public/js/lib/leaflet_easy_button/easy-button.js create mode 100644 xhiveframework/public/js/lib/moment.js create mode 100644 xhiveframework/public/js/lib/photoswipe/LICENCE create mode 100755 xhiveframework/public/js/lib/photoswipe/default-skin.css create mode 100755 xhiveframework/public/js/lib/photoswipe/photoswipe-ui-default.js create mode 100755 xhiveframework/public/js/lib/photoswipe/photoswipe.css create mode 100755 xhiveframework/public/js/lib/photoswipe/photoswipe.js create mode 100644 xhiveframework/public/js/lib/posthog.js create mode 100644 xhiveframework/public/js/libs.bundle.js create mode 100644 xhiveframework/public/js/list.bundle.js create mode 100644 xhiveframework/public/js/logtypes.bundle.js create mode 100644 xhiveframework/public/js/onboarding_tours.bundle.js create mode 100644 xhiveframework/public/js/onboarding_tours/onboarding_tours.js create mode 100644 xhiveframework/public/js/print_format_builder/ConfigureColumns.vue create mode 100644 xhiveframework/public/js/print_format_builder/Field.vue create mode 100644 xhiveframework/public/js/print_format_builder/HTMLEditor.vue create mode 100644 xhiveframework/public/js/print_format_builder/LetterHeadEditor.vue create mode 100644 xhiveframework/public/js/print_format_builder/Preview.vue create mode 100644 xhiveframework/public/js/print_format_builder/PrintFormat.vue create mode 100644 xhiveframework/public/js/print_format_builder/PrintFormatBuilder.vue create mode 100644 xhiveframework/public/js/print_format_builder/PrintFormatControls.vue create mode 100644 xhiveframework/public/js/print_format_builder/PrintFormatSection.vue create mode 100644 xhiveframework/public/js/print_format_builder/print_format_builder.bundle.js create mode 100644 xhiveframework/public/js/print_format_builder/store.js create mode 100644 xhiveframework/public/js/print_format_builder/utils.js create mode 100644 xhiveframework/public/js/report.bundle.js create mode 100644 xhiveframework/public/js/sentry.bundle.js create mode 100644 xhiveframework/public/js/telemetry.bundle.js create mode 100644 xhiveframework/public/js/telemetry/index.js create mode 100644 xhiveframework/public/js/user_profile_controller.bundle.js create mode 100644 xhiveframework/public/js/video_player.bundle.js create mode 100644 xhiveframework/public/js/web_form.bundle.js create mode 100644 xhiveframework/public/js/workflow_builder/WorkflowBuilder.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/ActionNode.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/ConnectionLine.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/Properties.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/Sidebar.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/StateNode.vue create mode 100644 xhiveframework/public/js/workflow_builder/components/TransitionEdge.vue create mode 100644 xhiveframework/public/js/workflow_builder/globals.js create mode 100644 xhiveframework/public/js/workflow_builder/store.js create mode 100644 xhiveframework/public/js/workflow_builder/utils.js create mode 100644 xhiveframework/public/js/workflow_builder/workflow_builder.bundle.js create mode 100644 xhiveframework/public/js/xhiveframework-web.bundle.js create mode 100644 xhiveframework/public/js/xhiveframework/assets.js create mode 100644 xhiveframework/public/js/xhiveframework/build_events/BuildError.vue create mode 100644 xhiveframework/public/js/xhiveframework/build_events/BuildSuccess.vue create mode 100644 xhiveframework/public/js/xhiveframework/build_events/build_events.bundle.js create mode 100644 xhiveframework/public/js/xhiveframework/change_log.html create mode 100644 xhiveframework/public/js/xhiveframework/class.js create mode 100644 xhiveframework/public/js/xhiveframework/color_picker/color_picker.js create mode 100644 xhiveframework/public/js/xhiveframework/color_picker/utils.js create mode 100644 xhiveframework/public/js/xhiveframework/data_import/data_exporter.js create mode 100644 xhiveframework/public/js/xhiveframework/data_import/import_preview.js create mode 100644 xhiveframework/public/js/xhiveframework/data_import/index.js create mode 100644 xhiveframework/public/js/xhiveframework/db.js create mode 100644 xhiveframework/public/js/xhiveframework/defaults.js create mode 100644 xhiveframework/public/js/xhiveframework/desk.js create mode 100644 xhiveframework/public/js/xhiveframework/doctype/index.js create mode 100644 xhiveframework/public/js/xhiveframework/dom.js create mode 100644 xhiveframework/public/js/xhiveframework/event_emitter.js create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/FileBrowser.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/FilePreview.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/FileUploader.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/ImageCropper.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/ProgressRing.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/TreeNode.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/WebLink.vue create mode 100644 xhiveframework/public/js/xhiveframework/file_uploader/file_uploader.bundle.js create mode 100644 xhiveframework/public/js/xhiveframework/form/column.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/attach.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/attach_image.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/autocomplete.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/barcode.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/base_control.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/base_input.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/button.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/check.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/code.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/color.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/comment.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/control.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/currency.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/data.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/date.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/date_range.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/datepicker_i18n.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/datetime.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/duration.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/dynamic_link.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/float.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/geolocation.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/heading.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/html.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/html_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/icon.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/image.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/int.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/json.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/link.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/markdown_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/multicheck.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/multiselect.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/multiselect_list.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/multiselect_pills.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/password.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/phone.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/quill-mention/blots/mention.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/quill-mention/constants/keys.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/quill-mention/quill.mention.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/rating.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/select.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/signature.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/table.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/table_multiselect.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/text.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/text_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/form/controls/time.js create mode 100644 xhiveframework/public/js/xhiveframework/form/dashboard.js create mode 100644 xhiveframework/public/js/xhiveframework/form/footer/base_timeline.js create mode 100644 xhiveframework/public/js/xhiveframework/form/footer/footer.js create mode 100644 xhiveframework/public/js/xhiveframework/form/footer/form_timeline.js create mode 100644 xhiveframework/public/js/xhiveframework/form/footer/version_timeline_content_builder.js create mode 100644 xhiveframework/public/js/xhiveframework/form/form.js create mode 100644 xhiveframework/public/js/xhiveframework/form/form_tour.js create mode 100644 xhiveframework/public/js/xhiveframework/form/form_viewers.js create mode 100644 xhiveframework/public/js/xhiveframework/form/formatters.js create mode 100644 xhiveframework/public/js/xhiveframework/form/grid.js create mode 100644 xhiveframework/public/js/xhiveframework/form/grid_pagination.js create mode 100644 xhiveframework/public/js/xhiveframework/form/grid_row.js create mode 100644 xhiveframework/public/js/xhiveframework/form/grid_row_form.js create mode 100644 xhiveframework/public/js/xhiveframework/form/layout.js create mode 100644 xhiveframework/public/js/xhiveframework/form/link_selector.js create mode 100644 xhiveframework/public/js/xhiveframework/form/linked_with.js create mode 100644 xhiveframework/public/js/xhiveframework/form/multi_select_dialog.js create mode 100644 xhiveframework/public/js/xhiveframework/form/print_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/form/quick_entry.js create mode 100644 xhiveframework/public/js/xhiveframework/form/reminders.js create mode 100644 xhiveframework/public/js/xhiveframework/form/save.js create mode 100644 xhiveframework/public/js/xhiveframework/form/script_helpers.js create mode 100644 xhiveframework/public/js/xhiveframework/form/script_manager.js create mode 100644 xhiveframework/public/js/xhiveframework/form/section.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/assign_to.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/attachments.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/document_follow.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/form_sidebar.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/form_sidebar_users.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/review.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/share.js create mode 100644 xhiveframework/public/js/xhiveframework/form/sidebar/user_image.js create mode 100644 xhiveframework/public/js/xhiveframework/form/success_action.js create mode 100644 xhiveframework/public/js/xhiveframework/form/tab.js create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/address_list.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/contact_list.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/form_dashboard.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/form_footer.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/form_links.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/form_sidebar.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/print_layout.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/report_links.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/set_sharing.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/timeline_message_box.html create mode 100644 xhiveframework/public/js/xhiveframework/form/templates/users_in_sidebar.html create mode 100644 xhiveframework/public/js/xhiveframework/form/toolbar.js create mode 100644 xhiveframework/public/js/xhiveframework/form/undo_manager.js create mode 100644 xhiveframework/public/js/xhiveframework/form/workflow.js create mode 100644 xhiveframework/public/js/xhiveframework/format.js create mode 100644 xhiveframework/public/js/xhiveframework/icon_picker/icon_picker.js create mode 100644 xhiveframework/public/js/xhiveframework/list/base_list.js create mode 100644 xhiveframework/public/js/xhiveframework/list/bulk_operations.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_factory.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_filter.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_settings.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_sidebar.html create mode 100644 xhiveframework/public/js/xhiveframework/list/list_sidebar.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_sidebar_group_by.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_sidebar_stat.html create mode 100644 xhiveframework/public/js/xhiveframework/list/list_view.js create mode 100644 xhiveframework/public/js/xhiveframework/list/list_view_permission_restrictions.html create mode 100644 xhiveframework/public/js/xhiveframework/list/list_view_select.js create mode 100644 xhiveframework/public/js/xhiveframework/logtypes.js create mode 100644 xhiveframework/public/js/xhiveframework/meta_tag.js create mode 100644 xhiveframework/public/js/xhiveframework/microtemplate.js create mode 100644 xhiveframework/public/js/xhiveframework/model/create_new.js create mode 100644 xhiveframework/public/js/xhiveframework/model/indicator.js create mode 100644 xhiveframework/public/js/xhiveframework/model/meta.js create mode 100644 xhiveframework/public/js/xhiveframework/model/model.js create mode 100644 xhiveframework/public/js/xhiveframework/model/perm.js create mode 100644 xhiveframework/public/js/xhiveframework/model/sync.js create mode 100644 xhiveframework/public/js/xhiveframework/model/user_settings.js create mode 100644 xhiveframework/public/js/xhiveframework/model/workflow.js create mode 100644 xhiveframework/public/js/xhiveframework/module_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/phone_picker/phone_picker.js create mode 100644 xhiveframework/public/js/xhiveframework/polyfill.js create mode 100644 xhiveframework/public/js/xhiveframework/provide.js create mode 100644 xhiveframework/public/js/xhiveframework/query_string.js create mode 100644 xhiveframework/public/js/xhiveframework/request.js create mode 100644 xhiveframework/public/js/xhiveframework/roles_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/router.js create mode 100644 xhiveframework/public/js/xhiveframework/router_history.js create mode 100644 xhiveframework/public/js/xhiveframework/scanner/index.js create mode 100644 xhiveframework/public/js/xhiveframework/socketio_client.js create mode 100644 xhiveframework/public/js/xhiveframework/translate.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/alt_keyboard_shortcuts.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/app_icon.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/capture.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/chart.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/colors.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/datatable.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/dialog.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/driver.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/field_group.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/filters/edit_filter.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/filters/field_select.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/filters/filter.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/filters/filter_list.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/find.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/group_by/group_by.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/group_by/group_by.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/iconbar.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/keyboard.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/like.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/link_preview.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/listing.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/messages.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/notifications/notifications.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/page.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/page.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/sidebar.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/slides.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/sort_selector.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/sort_selector.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/tag_editor.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/tags.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/theme_switcher.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/about.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/awesome_bar.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/fuzzy_match.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/navbar.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/search.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/search.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/search_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/tag_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/toolbar/toolbar.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/tree.js create mode 100644 xhiveframework/public/js/xhiveframework/ui/workspace_loading_skeleton.html create mode 100644 xhiveframework/public/js/xhiveframework/ui/workspace_sidebar_loading_skeleton.html create mode 100644 xhiveframework/public/js/xhiveframework/upload.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/address_and_contact.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/common.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/dashboard_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/datatable.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/datatype.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/datetime.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/diffview.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/display_image.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/energy_point_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/file_manager.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/help.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/help_links.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/number_format.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/number_systems.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/pretty_date.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/preview_email.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/tools.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/urllib.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/user.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/utils.js create mode 100644 xhiveframework/public/js/xhiveframework/utils/web_template.js create mode 100644 xhiveframework/public/js/xhiveframework/views/breadcrumbs.js create mode 100644 xhiveframework/public/js/xhiveframework/views/calendar/calendar.js create mode 100755 xhiveframework/public/js/xhiveframework/views/communication.js create mode 100644 xhiveframework/public/js/xhiveframework/views/container.js create mode 100644 xhiveframework/public/js/xhiveframework/views/dashboard/dashboard_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/factory.js create mode 100644 xhiveframework/public/js/xhiveframework/views/file/file_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/formview.js create mode 100644 xhiveframework/public/js/xhiveframework/views/gantt/gantt_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/image/image_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/image/image_view_item_row.html create mode 100644 xhiveframework/public/js/xhiveframework/views/image/photoswipe_dom.html create mode 100644 xhiveframework/public/js/xhiveframework/views/inbox/inbox_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/interaction.js create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_board.bundle.js create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_board.html create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_card.html create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_column.html create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_settings.js create mode 100644 xhiveframework/public/js/xhiveframework/views/kanban/kanban_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/map/map_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/pageview.js create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/print_grid.html create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/print_tree.html create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/query_report.js create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/report_factory.js create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/report_utils.js create mode 100644 xhiveframework/public/js/xhiveframework/views/reports/report_view.js create mode 100644 xhiveframework/public/js/xhiveframework/views/translation_manager.js create mode 100644 xhiveframework/public/js/xhiveframework/views/treeview.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/block.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/card.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/chart.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/custom_block.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/header.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/header_size.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/index.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/number_card.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/onboarding.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/paragraph.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/quick_list.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/shortcut.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/blocks/spacer.js create mode 100644 xhiveframework/public/js/xhiveframework/views/workspace/workspace.js create mode 100644 xhiveframework/public/js/xhiveframework/web_form/web_form.js create mode 100644 xhiveframework/public/js/xhiveframework/web_form/web_form_list.js create mode 100644 xhiveframework/public/js/xhiveframework/web_form/webform_script.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/base_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/chart_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/custom_block_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/links_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/new_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/number_card_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/onboarding_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/quick_list_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/shortcut_widget.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/widget_dialog.js create mode 100644 xhiveframework/public/js/xhiveframework/widgets/widget_group.js create mode 100644 xhiveframework/public/scss/common/alert.scss create mode 100644 xhiveframework/public/scss/common/awesomeplete.scss create mode 100644 xhiveframework/public/scss/common/buttons.scss create mode 100644 xhiveframework/public/scss/common/color_picker.scss create mode 100644 xhiveframework/public/scss/common/controls.scss create mode 100644 xhiveframework/public/scss/common/css_variables.scss create mode 100644 xhiveframework/public/scss/common/datepicker.scss create mode 100644 xhiveframework/public/scss/common/flex.scss create mode 100644 xhiveframework/public/scss/common/form.scss create mode 100644 xhiveframework/public/scss/common/global.scss create mode 100644 xhiveframework/public/scss/common/grid.scss create mode 100644 xhiveframework/public/scss/common/icon_picker.scss create mode 100644 xhiveframework/public/scss/common/icons.scss create mode 100644 xhiveframework/public/scss/common/indicator.scss create mode 100644 xhiveframework/public/scss/common/mixins.scss create mode 100644 xhiveframework/public/scss/common/modal.scss create mode 100644 xhiveframework/public/scss/common/phone_picker.scss create mode 100644 xhiveframework/public/scss/common/quill.scss create mode 100644 xhiveframework/public/scss/desk.bundle.scss create mode 100644 xhiveframework/public/scss/desk/avatar.scss create mode 100644 xhiveframework/public/scss/desk/breadcrumb.scss create mode 100644 xhiveframework/public/scss/desk/calendar.scss create mode 100644 xhiveframework/public/scss/desk/css_variables.scss create mode 100644 xhiveframework/public/scss/desk/dark.scss create mode 100644 xhiveframework/public/scss/desk/dashboard_view.scss create mode 100644 xhiveframework/public/scss/desk/data_import.scss create mode 100644 xhiveframework/public/scss/desk/desktop.scss create mode 100644 xhiveframework/public/scss/desk/driver.scss create mode 100644 xhiveframework/public/scss/desk/file_view.scss create mode 100644 xhiveframework/public/scss/desk/filters.scss create mode 100644 xhiveframework/public/scss/desk/form.scss create mode 100644 xhiveframework/public/scss/desk/global.scss create mode 100644 xhiveframework/public/scss/desk/global_search.scss create mode 100644 xhiveframework/public/scss/desk/image_view.scss create mode 100644 xhiveframework/public/scss/desk/index.scss create mode 100644 xhiveframework/public/scss/desk/kanban.scss create mode 100644 xhiveframework/public/scss/desk/link_preview.scss create mode 100644 xhiveframework/public/scss/desk/list.scss create mode 100644 xhiveframework/public/scss/desk/mobile.scss create mode 100644 xhiveframework/public/scss/desk/module.scss create mode 100644 xhiveframework/public/scss/desk/navbar.scss create mode 100644 xhiveframework/public/scss/desk/notification.scss create mode 100644 xhiveframework/public/scss/desk/page.scss create mode 100644 xhiveframework/public/scss/desk/plyr.scss create mode 100644 xhiveframework/public/scss/desk/print_preview.scss create mode 100644 xhiveframework/public/scss/desk/report.scss create mode 100644 xhiveframework/public/scss/desk/role_editor.scss create mode 100644 xhiveframework/public/scss/desk/scrollbar.scss create mode 100644 xhiveframework/public/scss/desk/sidebar.scss create mode 100644 xhiveframework/public/scss/desk/slides.scss create mode 100644 xhiveframework/public/scss/desk/tags.scss create mode 100644 xhiveframework/public/scss/desk/theme_switcher.scss create mode 100644 xhiveframework/public/scss/desk/timeline.scss create mode 100644 xhiveframework/public/scss/desk/toast.scss create mode 100644 xhiveframework/public/scss/desk/tree.scss create mode 100644 xhiveframework/public/scss/desk/typography.scss create mode 100644 xhiveframework/public/scss/desk/user_profile.scss create mode 100644 xhiveframework/public/scss/desk/variables.scss create mode 100644 xhiveframework/public/scss/desk/version.scss create mode 100644 xhiveframework/public/scss/desk/xhiveframework_datatable.scss create mode 100644 xhiveframework/public/scss/desk/xhiveframework_gantt.scss create mode 100644 xhiveframework/public/scss/element/checkbox.scss create mode 100644 xhiveframework/public/scss/element/radio.scss create mode 100644 xhiveframework/public/scss/email.bundle.scss create mode 100644 xhiveframework/public/scss/espresso/_borders.scss create mode 100644 xhiveframework/public/scss/espresso/_colors.scss create mode 100644 xhiveframework/public/scss/espresso/_shadows.scss create mode 100644 xhiveframework/public/scss/espresso/_spacing.scss create mode 100644 xhiveframework/public/scss/espresso/_typography.scss create mode 100644 xhiveframework/public/scss/login.bundle.scss create mode 100644 xhiveframework/public/scss/print.bundle.scss create mode 100644 xhiveframework/public/scss/print_format.bundle.scss create mode 100644 xhiveframework/public/scss/report.bundle.scss create mode 100644 xhiveframework/public/scss/web_form.bundle.scss create mode 100644 xhiveframework/public/scss/website.bundle.scss create mode 100644 xhiveframework/public/scss/website/base.scss create mode 100644 xhiveframework/public/scss/website/blog.scss create mode 100644 xhiveframework/public/scss/website/css_variables.scss create mode 100644 xhiveframework/public/scss/website/doc.scss create mode 100644 xhiveframework/public/scss/website/error-state.scss create mode 100644 xhiveframework/public/scss/website/footer.scss create mode 100644 xhiveframework/public/scss/website/index.scss create mode 100644 xhiveframework/public/scss/website/markdown.scss create mode 100644 xhiveframework/public/scss/website/multilevel_dropdown.scss create mode 100644 xhiveframework/public/scss/website/my_account.scss create mode 100644 xhiveframework/public/scss/website/navbar.scss create mode 100644 xhiveframework/public/scss/website/page_builder.scss create mode 100644 xhiveframework/public/scss/website/portal.scss create mode 100644 xhiveframework/public/scss/website/search.scss create mode 100644 xhiveframework/public/scss/website/sidebar.scss create mode 100644 xhiveframework/public/scss/website/variables.scss create mode 100644 xhiveframework/public/scss/website/web_form.scss create mode 100644 xhiveframework/public/scss/website/website_avatar.scss create mode 100644 xhiveframework/public/scss/website/website_image.scss create mode 100644 xhiveframework/public/sounds/alert.mp3 create mode 100644 xhiveframework/public/sounds/cancel.mp3 create mode 100644 xhiveframework/public/sounds/chime.mp3 create mode 100644 xhiveframework/public/sounds/click.mp3 create mode 100644 xhiveframework/public/sounds/delete.mp3 create mode 100644 xhiveframework/public/sounds/email.mp3 create mode 100644 xhiveframework/public/sounds/error.mp3 create mode 100644 xhiveframework/public/sounds/submit.mp3 create mode 100644 xhiveframework/push_notification.py create mode 100644 xhiveframework/query_builder/__init__.py create mode 100644 xhiveframework/query_builder/builder.py create mode 100644 xhiveframework/query_builder/custom.py create mode 100644 xhiveframework/query_builder/docs.md create mode 100644 xhiveframework/query_builder/functions.py create mode 100644 xhiveframework/query_builder/terms.py create mode 100644 xhiveframework/query_builder/utils.py create mode 100644 xhiveframework/rate_limiter.py create mode 100644 xhiveframework/realtime.py create mode 100644 xhiveframework/recorder.py create mode 100644 xhiveframework/search/__init__.py create mode 100644 xhiveframework/search/full_text_search.py create mode 100644 xhiveframework/search/test_full_text_search.py create mode 100644 xhiveframework/search/website_search.py create mode 100644 xhiveframework/sessions.py create mode 100644 xhiveframework/share.py create mode 100644 xhiveframework/social/__init__.py create mode 100644 xhiveframework/social/doctype/__init__.py create mode 100644 xhiveframework/social/doctype/energy_point_log/__init__.py create mode 100644 xhiveframework/social/doctype/energy_point_log/energy_point_log.js create mode 100644 xhiveframework/social/doctype/energy_point_log/energy_point_log.json create mode 100644 xhiveframework/social/doctype/energy_point_log/energy_point_log.py create mode 100644 xhiveframework/social/doctype/energy_point_log/energy_point_log_list.js create mode 100644 xhiveframework/social/doctype/energy_point_log/test_energy_point_log.py create mode 100644 xhiveframework/social/doctype/energy_point_rule/__init__.py create mode 100644 xhiveframework/social/doctype/energy_point_rule/energy_point_rule.js create mode 100644 xhiveframework/social/doctype/energy_point_rule/energy_point_rule.json create mode 100644 xhiveframework/social/doctype/energy_point_rule/energy_point_rule.py create mode 100644 xhiveframework/social/doctype/energy_point_settings/__init__.py create mode 100644 xhiveframework/social/doctype/energy_point_settings/energy_point_settings.js create mode 100644 xhiveframework/social/doctype/energy_point_settings/energy_point_settings.json create mode 100644 xhiveframework/social/doctype/energy_point_settings/energy_point_settings.py create mode 100644 xhiveframework/social/doctype/energy_point_settings/test_energy_point_settings.py create mode 100644 xhiveframework/social/doctype/review_level/__init__.py create mode 100644 xhiveframework/social/doctype/review_level/review_level.js create mode 100644 xhiveframework/social/doctype/review_level/review_level.json create mode 100644 xhiveframework/social/doctype/review_level/review_level.py create mode 100644 xhiveframework/templates/__init__.py create mode 100644 xhiveframework/templates/base.html create mode 100644 xhiveframework/templates/discussions/button.html create mode 100644 xhiveframework/templates/discussions/comment_box.html create mode 100644 xhiveframework/templates/discussions/discussions.js create mode 100644 xhiveframework/templates/discussions/discussions_section.html create mode 100644 xhiveframework/templates/discussions/reply_card.html create mode 100644 xhiveframework/templates/discussions/reply_section.html create mode 100644 xhiveframework/templates/discussions/search.html create mode 100644 xhiveframework/templates/discussions/sidebar.html create mode 100644 xhiveframework/templates/discussions/topic_modal.html create mode 100644 xhiveframework/templates/doc.html create mode 100644 xhiveframework/templates/emails/.txt create mode 100644 xhiveframework/templates/emails/__init__.py create mode 100644 xhiveframework/templates/emails/account_deletion_notification.html create mode 100644 xhiveframework/templates/emails/administrator_logged_in.html create mode 100644 xhiveframework/templates/emails/auto_email_report.html create mode 100644 xhiveframework/templates/emails/auto_repeat_fail.html create mode 100644 xhiveframework/templates/emails/auto_reply.html create mode 100644 xhiveframework/templates/emails/data_deletion_approval.html create mode 100644 xhiveframework/templates/emails/delete_data_confirmation.html create mode 100644 xhiveframework/templates/emails/document_follow.html create mode 100644 xhiveframework/templates/emails/download_data.html create mode 100644 xhiveframework/templates/emails/email_footer.html create mode 100644 xhiveframework/templates/emails/email_header.html create mode 100644 xhiveframework/templates/emails/energy_points_summary.html create mode 100644 xhiveframework/templates/emails/file_backup_notification.html create mode 100644 xhiveframework/templates/emails/login_with_email_link.html create mode 100644 xhiveframework/templates/emails/new_message.html create mode 100644 xhiveframework/templates/emails/new_notification.html create mode 100644 xhiveframework/templates/emails/new_user.html create mode 100644 xhiveframework/templates/emails/newsletter.html create mode 100644 xhiveframework/templates/emails/password_reset.html create mode 100644 xhiveframework/templates/emails/print_link.html create mode 100644 xhiveframework/templates/emails/standard.html create mode 100644 xhiveframework/templates/emails/upcoming_events.html create mode 100644 xhiveframework/templates/emails/verification_code.html create mode 100644 xhiveframework/templates/emails/workflow_action.html create mode 100644 xhiveframework/templates/form_grid/fields.html create mode 100644 xhiveframework/templates/includes/__init__.py create mode 100644 xhiveframework/templates/includes/app_analytics/google_analytics.html create mode 100644 xhiveframework/templates/includes/app_analytics/heap_analytics.html create mode 100644 xhiveframework/templates/includes/avatar_macro.html create mode 100644 xhiveframework/templates/includes/blog/blogger.html create mode 100644 xhiveframework/templates/includes/blog/hero.html create mode 100644 xhiveframework/templates/includes/breadcrumbs.html create mode 100644 xhiveframework/templates/includes/comments/__init__.py create mode 100644 xhiveframework/templates/includes/comments/comment.html create mode 100644 xhiveframework/templates/includes/comments/comments.html create mode 100644 xhiveframework/templates/includes/comments/comments.py create mode 100644 xhiveframework/templates/includes/contact.js create mode 100644 xhiveframework/templates/includes/footer/footer.html create mode 100644 xhiveframework/templates/includes/footer/footer_extension.html create mode 100644 xhiveframework/templates/includes/footer/footer_grouped_links.html create mode 100644 xhiveframework/templates/includes/footer/footer_info.html create mode 100644 xhiveframework/templates/includes/footer/footer_links.html create mode 100644 xhiveframework/templates/includes/footer/footer_logo_extension.html create mode 100644 xhiveframework/templates/includes/footer/footer_powered.html create mode 100644 xhiveframework/templates/includes/form_macros.html create mode 100644 xhiveframework/templates/includes/full_index.html create mode 100644 xhiveframework/templates/includes/head.html create mode 100644 xhiveframework/templates/includes/image_with_blur.html create mode 100644 xhiveframework/templates/includes/integrations/third_party_apps.js create mode 100644 xhiveframework/templates/includes/likes/__init__.py create mode 100644 xhiveframework/templates/includes/likes/likes.html create mode 100644 xhiveframework/templates/includes/likes/likes.py create mode 100644 xhiveframework/templates/includes/list/__init__.py create mode 100644 xhiveframework/templates/includes/list/filters.html create mode 100644 xhiveframework/templates/includes/list/filters.js create mode 100644 xhiveframework/templates/includes/list/list.html create mode 100644 xhiveframework/templates/includes/list/list.js create mode 100644 xhiveframework/templates/includes/list/row_template.html create mode 100644 xhiveframework/templates/includes/login/login.js create mode 100644 xhiveframework/templates/includes/macros.html create mode 100644 xhiveframework/templates/includes/meta_block.html create mode 100644 xhiveframework/templates/includes/navbar/dropdown_items.html create mode 100644 xhiveframework/templates/includes/navbar/dropdown_login.html create mode 100644 xhiveframework/templates/includes/navbar/navbar.html create mode 100644 xhiveframework/templates/includes/navbar/navbar_items.html create mode 100644 xhiveframework/templates/includes/navbar/navbar_login.html create mode 100644 xhiveframework/templates/includes/navbar/navbar_search.html create mode 100644 xhiveframework/templates/includes/oauth_confirmation.html create mode 100644 xhiveframework/templates/includes/print_table.html create mode 100644 xhiveframework/templates/includes/search_box.html create mode 100644 xhiveframework/templates/includes/search_result.html create mode 100644 xhiveframework/templates/includes/search_template.html create mode 100644 xhiveframework/templates/includes/slideshow.html create mode 100644 xhiveframework/templates/includes/splash_screen.html create mode 100644 xhiveframework/templates/includes/static_index.html create mode 100644 xhiveframework/templates/includes/web_block.html create mode 100644 xhiveframework/templates/includes/web_sidebar.html create mode 100644 xhiveframework/templates/includes/website_theme/footer.css create mode 100644 xhiveframework/templates/includes/website_theme/navbar.css create mode 100644 xhiveframework/templates/pages/__init__.py create mode 100644 xhiveframework/templates/pages/integrations/__init__.py create mode 100644 xhiveframework/templates/pages/integrations/gcalendar-success.html create mode 100644 xhiveframework/templates/print_format/macros.html create mode 100644 xhiveframework/templates/print_format/macros/Attach.html create mode 100644 xhiveframework/templates/print_format/macros/AttachImage.html create mode 100644 xhiveframework/templates/print_format/macros/Check.html create mode 100644 xhiveframework/templates/print_format/macros/Code.html create mode 100644 xhiveframework/templates/print_format/macros/Color.html create mode 100644 xhiveframework/templates/print_format/macros/Data.html create mode 100644 xhiveframework/templates/print_format/macros/Divider.html create mode 100644 xhiveframework/templates/print_format/macros/FieldTemplate.html create mode 100644 xhiveframework/templates/print_format/macros/HTML.html create mode 100644 xhiveframework/templates/print_format/macros/Markdown.html create mode 100644 xhiveframework/templates/print_format/macros/Rating.html create mode 100644 xhiveframework/templates/print_format/macros/Signature.html create mode 100644 xhiveframework/templates/print_format/macros/Spacer.html create mode 100644 xhiveframework/templates/print_format/macros/Table.html create mode 100644 xhiveframework/templates/print_format/print_footer.html create mode 100644 xhiveframework/templates/print_format/print_format.css create mode 100644 xhiveframework/templates/print_format/print_format.html create mode 100644 xhiveframework/templates/print_format/print_format_font.css create mode 100644 xhiveframework/templates/print_format/print_header.html create mode 100644 xhiveframework/templates/print_formats/pdf_header_footer.html create mode 100644 xhiveframework/templates/print_formats/standard.html create mode 100644 xhiveframework/templates/print_formats/standard_macros.html create mode 100644 xhiveframework/templates/signup.html create mode 100644 xhiveframework/templates/styles/card_style.css create mode 100644 xhiveframework/templates/styles/discussion_style.css create mode 100644 xhiveframework/templates/styles/standard.css create mode 100644 xhiveframework/templates/test/_test_base.html create mode 100644 xhiveframework/templates/test/_test_base_breadcrumbs.html create mode 100644 xhiveframework/templates/web.html create mode 100644 xhiveframework/test_runner.py create mode 100644 xhiveframework/tests/__init__.py create mode 100644 xhiveframework/tests/data/email_with_image.txt create mode 100644 xhiveframework/tests/data/exif_sample_image.jpg create mode 100644 xhiveframework/tests/data/load_sleep.py create mode 100644 xhiveframework/tests/data/sample_image_for_optimization.jpg create mode 100644 xhiveframework/tests/data/sample_svg.svg create mode 100644 xhiveframework/tests/test_api.py create mode 100644 xhiveframework/tests/test_api_v2.py create mode 100644 xhiveframework/tests/test_assign.py create mode 100644 xhiveframework/tests/test_auth.py create mode 100644 xhiveframework/tests/test_background_jobs.py create mode 100644 xhiveframework/tests/test_base_document.py create mode 100644 xhiveframework/tests/test_boilerplate.py create mode 100644 xhiveframework/tests/test_boot.py create mode 100644 xhiveframework/tests/test_caching.py create mode 100644 xhiveframework/tests/test_child_table.py create mode 100644 xhiveframework/tests/test_client.py create mode 100644 xhiveframework/tests/test_commands.py create mode 100644 xhiveframework/tests/test_config.py create mode 100644 xhiveframework/tests/test_cors.py create mode 100644 xhiveframework/tests/test_dashboard_connections.py create mode 100644 xhiveframework/tests/test_db.py create mode 100644 xhiveframework/tests/test_db_query.py create mode 100644 xhiveframework/tests/test_db_update.py create mode 100644 xhiveframework/tests/test_defaults.py create mode 100644 xhiveframework/tests/test_deferred_insert.py create mode 100644 xhiveframework/tests/test_docstatus.py create mode 100644 xhiveframework/tests/test_document.py create mode 100644 xhiveframework/tests/test_document_locks.py create mode 100644 xhiveframework/tests/test_domainification.py create mode 100644 xhiveframework/tests/test_dynamic_links.py create mode 100644 xhiveframework/tests/test_email.py create mode 100644 xhiveframework/tests/test_exporter_fixtures.py create mode 100644 xhiveframework/tests/test_fixture_import.py create mode 100644 xhiveframework/tests/test_fmt_datetime.py create mode 100644 xhiveframework/tests/test_fmt_money.py create mode 100644 xhiveframework/tests/test_form_load.py create mode 100644 xhiveframework/tests/test_formatter.py create mode 100644 xhiveframework/tests/test_geo_ip.py create mode 100644 xhiveframework/tests/test_global_search.py create mode 100644 xhiveframework/tests/test_goal.py create mode 100644 xhiveframework/tests/test_hooks.py create mode 100644 xhiveframework/tests/test_linked_with.py create mode 100644 xhiveframework/tests/test_listview.py create mode 100644 xhiveframework/tests/test_model_utils.py create mode 100644 xhiveframework/tests/test_modules.py create mode 100644 xhiveframework/tests/test_monitor.py create mode 100644 xhiveframework/tests/test_naming.py create mode 100644 xhiveframework/tests/test_nestedset.py create mode 100644 xhiveframework/tests/test_oauth20.py create mode 100644 xhiveframework/tests/test_password.py create mode 100644 xhiveframework/tests/test_password_strength.py create mode 100644 xhiveframework/tests/test_patches.py create mode 100644 xhiveframework/tests/test_pdf.py create mode 100644 xhiveframework/tests/test_perf.py create mode 100644 xhiveframework/tests/test_permissions.py create mode 100644 xhiveframework/tests/test_printview.py create mode 100644 xhiveframework/tests/test_query.py create mode 100644 xhiveframework/tests/test_query_builder.py create mode 100644 xhiveframework/tests/test_query_report.py create mode 100644 xhiveframework/tests/test_rate_limiter.py create mode 100644 xhiveframework/tests/test_rating.py create mode 100644 xhiveframework/tests/test_recorder.py create mode 100644 xhiveframework/tests/test_redis.py create mode 100644 xhiveframework/tests/test_rename_doc.py create mode 100644 xhiveframework/tests/test_reportview.py create mode 100644 xhiveframework/tests/test_safe_exec.py create mode 100644 xhiveframework/tests/test_scheduler.py create mode 100644 xhiveframework/tests/test_search.py create mode 100644 xhiveframework/tests/test_seen.py create mode 100644 xhiveframework/tests/test_sequence.py create mode 100644 xhiveframework/tests/test_sitemap.py create mode 100644 xhiveframework/tests/test_test_utils.py create mode 100644 xhiveframework/tests/test_translate.py create mode 100644 xhiveframework/tests/test_twofactor.py create mode 100644 xhiveframework/tests/test_utils.py create mode 100644 xhiveframework/tests/test_virtual_doctype.py create mode 100644 xhiveframework/tests/test_webform.py create mode 100644 xhiveframework/tests/test_website.py create mode 100644 xhiveframework/tests/test_xhiveframework_client.py create mode 100644 xhiveframework/tests/tests_geo_utils.py create mode 100644 xhiveframework/tests/translation_test_file.txt create mode 100644 xhiveframework/tests/ui_test_helpers.py create mode 100644 xhiveframework/tests/utils.py create mode 100644 xhiveframework/translate.py create mode 100644 xhiveframework/translations/af.csv create mode 100644 xhiveframework/translations/am.csv create mode 100644 xhiveframework/translations/ar.csv create mode 100644 xhiveframework/translations/bg.csv create mode 100644 xhiveframework/translations/bn.csv create mode 100644 xhiveframework/translations/bs.csv create mode 100644 xhiveframework/translations/ca.csv create mode 100644 xhiveframework/translations/cs.csv create mode 100644 xhiveframework/translations/cz.csv create mode 100644 xhiveframework/translations/da-DK.csv create mode 100644 xhiveframework/translations/da.csv create mode 100644 xhiveframework/translations/de.csv create mode 100644 xhiveframework/translations/el.csv create mode 100644 xhiveframework/translations/en-GB.csv create mode 100644 xhiveframework/translations/en-US.csv create mode 100644 xhiveframework/translations/es-AR.csv create mode 100644 xhiveframework/translations/es-BO.csv create mode 100644 xhiveframework/translations/es-CL.csv create mode 100644 xhiveframework/translations/es-DO.csv create mode 100644 xhiveframework/translations/es-EC.csv create mode 100644 xhiveframework/translations/es-MX.csv create mode 100644 xhiveframework/translations/es-NI.csv create mode 100644 xhiveframework/translations/es-PE.csv create mode 100644 xhiveframework/translations/es.csv create mode 100644 xhiveframework/translations/et.csv create mode 100644 xhiveframework/translations/fa.csv create mode 100644 xhiveframework/translations/fi.csv create mode 100644 xhiveframework/translations/fil.csv create mode 100644 xhiveframework/translations/fr-CA.csv create mode 100644 xhiveframework/translations/fr.csv create mode 100644 xhiveframework/translations/gu.csv create mode 100644 xhiveframework/translations/he.csv create mode 100644 xhiveframework/translations/hi.csv create mode 100644 xhiveframework/translations/hr.csv create mode 100644 xhiveframework/translations/hu.csv create mode 100644 xhiveframework/translations/id.csv create mode 100644 xhiveframework/translations/is.csv create mode 100644 xhiveframework/translations/it.csv create mode 100644 xhiveframework/translations/ja.csv create mode 100644 xhiveframework/translations/km.csv create mode 100644 xhiveframework/translations/kn.csv create mode 100644 xhiveframework/translations/ko.csv create mode 100644 xhiveframework/translations/ku.csv create mode 100644 xhiveframework/translations/lo.csv create mode 100644 xhiveframework/translations/lt.csv create mode 100644 xhiveframework/translations/lv.csv create mode 100644 xhiveframework/translations/mk.csv create mode 100644 xhiveframework/translations/ml.csv create mode 100644 xhiveframework/translations/mr.csv create mode 100644 xhiveframework/translations/ms.csv create mode 100644 xhiveframework/translations/my.csv create mode 100644 xhiveframework/translations/nl.csv create mode 100644 xhiveframework/translations/no.csv create mode 100644 xhiveframework/translations/pl.csv create mode 100644 xhiveframework/translations/ps.csv create mode 100644 xhiveframework/translations/pt-BR.csv create mode 100644 xhiveframework/translations/pt.csv create mode 100644 xhiveframework/translations/quc.csv create mode 100644 xhiveframework/translations/ro.csv create mode 100644 xhiveframework/translations/ru.csv create mode 100644 xhiveframework/translations/rw.csv create mode 100644 xhiveframework/translations/se.csv create mode 100644 xhiveframework/translations/si.csv create mode 100644 xhiveframework/translations/sk.csv create mode 100644 xhiveframework/translations/sl.csv create mode 100644 xhiveframework/translations/sq.csv create mode 100644 xhiveframework/translations/sr-BA.csv create mode 100644 xhiveframework/translations/sr-SP.csv create mode 100644 xhiveframework/translations/sr.csv create mode 100644 xhiveframework/translations/sv.csv create mode 100644 xhiveframework/translations/sw.csv create mode 100644 xhiveframework/translations/ta.csv create mode 100644 xhiveframework/translations/te.csv create mode 100644 xhiveframework/translations/th.csv create mode 100644 xhiveframework/translations/tr.csv create mode 100644 xhiveframework/translations/uk.csv create mode 100644 xhiveframework/translations/ur.csv create mode 100644 xhiveframework/translations/uz.csv create mode 100644 xhiveframework/translations/vi.csv create mode 100644 xhiveframework/translations/zh-TW.csv create mode 100644 xhiveframework/translations/zh.csv create mode 100644 xhiveframework/twofactor.py create mode 100644 xhiveframework/types/DF.py create mode 100644 xhiveframework/types/__init__.py create mode 100644 xhiveframework/types/exporter.py create mode 100644 xhiveframework/utils/__init__.py create mode 100755 xhiveframework/utils/background_jobs.py create mode 100644 xhiveframework/utils/backups.py create mode 100644 xhiveframework/utils/bench_helper.py create mode 100644 xhiveframework/utils/boilerplate.py create mode 100644 xhiveframework/utils/caching.py create mode 100644 xhiveframework/utils/change_log.py create mode 100644 xhiveframework/utils/commands.py create mode 100644 xhiveframework/utils/connections.py create mode 100644 xhiveframework/utils/csvutils.py create mode 100644 xhiveframework/utils/dashboard.py create mode 100644 xhiveframework/utils/data.py create mode 100644 xhiveframework/utils/dateutils.py create mode 100644 xhiveframework/utils/deprecations.py create mode 100644 xhiveframework/utils/diff.py create mode 100644 xhiveframework/utils/doctor.py create mode 100644 xhiveframework/utils/error.py create mode 100644 xhiveframework/utils/file_lock.py create mode 100644 xhiveframework/utils/file_manager.py create mode 100644 xhiveframework/utils/fixtures.py create mode 100644 xhiveframework/utils/formatters.py create mode 100644 xhiveframework/utils/global_search.py create mode 100644 xhiveframework/utils/goal.py create mode 100644 xhiveframework/utils/html_utils.py create mode 100644 xhiveframework/utils/identicon.py create mode 100644 xhiveframework/utils/image.py create mode 100644 xhiveframework/utils/install.py create mode 100644 xhiveframework/utils/jinja.py create mode 100644 xhiveframework/utils/jinja_globals.py create mode 100644 xhiveframework/utils/lazy_loader.py create mode 100755 xhiveframework/utils/logger.py create mode 100644 xhiveframework/utils/make_random.py create mode 100644 xhiveframework/utils/momentjs.py create mode 100644 xhiveframework/utils/nestedset.py create mode 100644 xhiveframework/utils/oauth.py create mode 100644 xhiveframework/utils/password.py create mode 100644 xhiveframework/utils/password_strength.py create mode 100644 xhiveframework/utils/pdf.py create mode 100644 xhiveframework/utils/print_format.py create mode 100644 xhiveframework/utils/redis_queue.py create mode 100644 xhiveframework/utils/redis_wrapper.py create mode 100644 xhiveframework/utils/response.py create mode 100644 xhiveframework/utils/safe_exec.py create mode 100755 xhiveframework/utils/scheduler.py create mode 100644 xhiveframework/utils/sentry.py create mode 100644 xhiveframework/utils/synchronization.py create mode 100644 xhiveframework/utils/telemetry.py create mode 100644 xhiveframework/utils/testutils.py create mode 100644 xhiveframework/utils/typing_validations.py create mode 100644 xhiveframework/utils/user.py create mode 100644 xhiveframework/utils/verified_command.py create mode 100644 xhiveframework/utils/weasyprint.py create mode 100644 xhiveframework/utils/xlsxutils.py create mode 100644 xhiveframework/website/__init__.py create mode 100644 xhiveframework/website/css/web_form.css create mode 100644 xhiveframework/website/dashboard_fixtures.py create mode 100644 xhiveframework/website/doctype/__init__.py create mode 100644 xhiveframework/website/doctype/about_us_settings/README.md create mode 100644 xhiveframework/website/doctype/about_us_settings/__init__.py create mode 100644 xhiveframework/website/doctype/about_us_settings/about_us_settings.js create mode 100644 xhiveframework/website/doctype/about_us_settings/about_us_settings.json create mode 100644 xhiveframework/website/doctype/about_us_settings/about_us_settings.py create mode 100644 xhiveframework/website/doctype/about_us_settings/test_about_us_settings.py create mode 100644 xhiveframework/website/doctype/about_us_team_member/README.md create mode 100644 xhiveframework/website/doctype/about_us_team_member/__init__.py create mode 100644 xhiveframework/website/doctype/about_us_team_member/about_us_team_member.json create mode 100644 xhiveframework/website/doctype/about_us_team_member/about_us_team_member.py create mode 100644 xhiveframework/website/doctype/blog_category/README.md create mode 100644 xhiveframework/website/doctype/blog_category/__init__.py create mode 100644 xhiveframework/website/doctype/blog_category/blog_category.js create mode 100644 xhiveframework/website/doctype/blog_category/blog_category.json create mode 100644 xhiveframework/website/doctype/blog_category/blog_category.py create mode 100644 xhiveframework/website/doctype/blog_category/templates/blog_category.html create mode 100644 xhiveframework/website/doctype/blog_category/templates/blog_category_row.html create mode 100644 xhiveframework/website/doctype/blog_category/test_blog_category.py create mode 100644 xhiveframework/website/doctype/blog_category/test_records.json create mode 100644 xhiveframework/website/doctype/blog_post/README.md create mode 100644 xhiveframework/website/doctype/blog_post/__init__.py create mode 100644 xhiveframework/website/doctype/blog_post/blog_post.js create mode 100644 xhiveframework/website/doctype/blog_post/blog_post.json create mode 100644 xhiveframework/website/doctype/blog_post/blog_post.py create mode 100644 xhiveframework/website/doctype/blog_post/blog_post_list.js create mode 100644 xhiveframework/website/doctype/blog_post/templates/blog_post.html create mode 100644 xhiveframework/website/doctype/blog_post/templates/blog_post_list.html create mode 100644 xhiveframework/website/doctype/blog_post/templates/blog_post_row.html create mode 100644 xhiveframework/website/doctype/blog_post/test_blog_post.py create mode 100644 xhiveframework/website/doctype/blog_post/test_records.json create mode 100644 xhiveframework/website/doctype/blog_post/ui_test_blog_post.js create mode 100644 xhiveframework/website/doctype/blog_settings/README.md create mode 100644 xhiveframework/website/doctype/blog_settings/__init__.py create mode 100644 xhiveframework/website/doctype/blog_settings/blog_settings.js create mode 100644 xhiveframework/website/doctype/blog_settings/blog_settings.json create mode 100644 xhiveframework/website/doctype/blog_settings/blog_settings.py create mode 100644 xhiveframework/website/doctype/blog_settings/test_blog_settings.py create mode 100644 xhiveframework/website/doctype/blogger/README.md create mode 100644 xhiveframework/website/doctype/blogger/__init__.py create mode 100644 xhiveframework/website/doctype/blogger/blogger.js create mode 100644 xhiveframework/website/doctype/blogger/blogger.json create mode 100644 xhiveframework/website/doctype/blogger/blogger.py create mode 100644 xhiveframework/website/doctype/blogger/test_blogger.py create mode 100644 xhiveframework/website/doctype/blogger/test_records.json create mode 100644 xhiveframework/website/doctype/color/__init__.py create mode 100644 xhiveframework/website/doctype/color/color.js create mode 100644 xhiveframework/website/doctype/color/color.json create mode 100644 xhiveframework/website/doctype/color/color.py create mode 100644 xhiveframework/website/doctype/color/test_color.py create mode 100644 xhiveframework/website/doctype/company_history/README.md create mode 100644 xhiveframework/website/doctype/company_history/__init__.py create mode 100644 xhiveframework/website/doctype/company_history/company_history.json create mode 100644 xhiveframework/website/doctype/company_history/company_history.py create mode 100644 xhiveframework/website/doctype/contact_us_settings/README.md create mode 100644 xhiveframework/website/doctype/contact_us_settings/__init__.py create mode 100644 xhiveframework/website/doctype/contact_us_settings/contact_us_settings.js create mode 100644 xhiveframework/website/doctype/contact_us_settings/contact_us_settings.json create mode 100644 xhiveframework/website/doctype/contact_us_settings/contact_us_settings.py create mode 100644 xhiveframework/website/doctype/discussion_reply/__init__.py create mode 100644 xhiveframework/website/doctype/discussion_reply/discussion_reply.js create mode 100644 xhiveframework/website/doctype/discussion_reply/discussion_reply.json create mode 100644 xhiveframework/website/doctype/discussion_reply/discussion_reply.py create mode 100644 xhiveframework/website/doctype/discussion_reply/test_discussion_reply.py create mode 100644 xhiveframework/website/doctype/discussion_topic/__init__.py create mode 100644 xhiveframework/website/doctype/discussion_topic/discussion_topic.js create mode 100644 xhiveframework/website/doctype/discussion_topic/discussion_topic.json create mode 100644 xhiveframework/website/doctype/discussion_topic/discussion_topic.py create mode 100644 xhiveframework/website/doctype/discussion_topic/test_discussion_topic.py create mode 100644 xhiveframework/website/doctype/help_article/__init__.py create mode 100644 xhiveframework/website/doctype/help_article/help_article.js create mode 100644 xhiveframework/website/doctype/help_article/help_article.json create mode 100644 xhiveframework/website/doctype/help_article/help_article.py create mode 100644 xhiveframework/website/doctype/help_article/templates/help_article.html create mode 100644 xhiveframework/website/doctype/help_article/templates/help_article_row.html create mode 100644 xhiveframework/website/doctype/help_article/test_help_article.py create mode 100644 xhiveframework/website/doctype/help_category/__init__.py create mode 100644 xhiveframework/website/doctype/help_category/help_category.js create mode 100644 xhiveframework/website/doctype/help_category/help_category.json create mode 100644 xhiveframework/website/doctype/help_category/help_category.py create mode 100644 xhiveframework/website/doctype/help_category/test_help_category.py create mode 100644 xhiveframework/website/doctype/marketing_campaign/__init__.py create mode 100644 xhiveframework/website/doctype/marketing_campaign/marketing_campaign.json create mode 100644 xhiveframework/website/doctype/marketing_campaign/marketing_campaign.py create mode 100644 xhiveframework/website/doctype/personal_data_deletion_request/__init__.py create mode 100644 xhiveframework/website/doctype/personal_data_deletion_request/personal_data_deletion_request.js create mode 100644 xhiveframework/website/doctype/personal_data_deletion_request/personal_data_deletion_request.json create mode 100644 xhiveframework/website/doctype/personal_data_deletion_request/personal_data_deletion_request.py create mode 100644 xhiveframework/website/doctype/personal_data_deletion_request/test_personal_data_deletion_request.py create mode 100644 xhiveframework/website/doctype/personal_data_deletion_step/__init__.py create mode 100644 xhiveframework/website/doctype/personal_data_deletion_step/personal_data_deletion_step.json create mode 100644 xhiveframework/website/doctype/personal_data_deletion_step/personal_data_deletion_step.py create mode 100644 xhiveframework/website/doctype/personal_data_download_request/__init__.py create mode 100644 xhiveframework/website/doctype/personal_data_download_request/personal_data_download_request.js create mode 100644 xhiveframework/website/doctype/personal_data_download_request/personal_data_download_request.json create mode 100644 xhiveframework/website/doctype/personal_data_download_request/personal_data_download_request.py create mode 100644 xhiveframework/website/doctype/personal_data_download_request/test_personal_data_download_request.py create mode 100644 xhiveframework/website/doctype/portal_menu_item/__init__.py create mode 100644 xhiveframework/website/doctype/portal_menu_item/portal_menu_item.json create mode 100644 xhiveframework/website/doctype/portal_menu_item/portal_menu_item.py create mode 100644 xhiveframework/website/doctype/portal_settings/__init__.py create mode 100644 xhiveframework/website/doctype/portal_settings/portal_settings.js create mode 100644 xhiveframework/website/doctype/portal_settings/portal_settings.json create mode 100644 xhiveframework/website/doctype/portal_settings/portal_settings.py create mode 100644 xhiveframework/website/doctype/portal_settings/test_portal_settings.py create mode 100644 xhiveframework/website/doctype/social_link_settings/__init__.py create mode 100644 xhiveframework/website/doctype/social_link_settings/social_link_settings.json create mode 100644 xhiveframework/website/doctype/social_link_settings/social_link_settings.py create mode 100644 xhiveframework/website/doctype/top_bar_item/README.md create mode 100644 xhiveframework/website/doctype/top_bar_item/__init__.py create mode 100644 xhiveframework/website/doctype/top_bar_item/top_bar_item.json create mode 100644 xhiveframework/website/doctype/top_bar_item/top_bar_item.py create mode 100644 xhiveframework/website/doctype/web_form/__init__.py create mode 100644 xhiveframework/website/doctype/web_form/templates/web_form.html create mode 100644 xhiveframework/website/doctype/web_form/templates/web_form_row.html create mode 100644 xhiveframework/website/doctype/web_form/templates/web_form_skeleton.html create mode 100644 xhiveframework/website/doctype/web_form/templates/web_list.html create mode 100644 xhiveframework/website/doctype/web_form/test_records.json create mode 100644 xhiveframework/website/doctype/web_form/test_web_form.py create mode 100644 xhiveframework/website/doctype/web_form/web_form.js create mode 100644 xhiveframework/website/doctype/web_form/web_form.json create mode 100644 xhiveframework/website/doctype/web_form/web_form.py create mode 100644 xhiveframework/website/doctype/web_form/web_form_list.js create mode 100644 xhiveframework/website/doctype/web_form_field/__init__.py create mode 100644 xhiveframework/website/doctype/web_form_field/web_form_field.json create mode 100644 xhiveframework/website/doctype/web_form_field/web_form_field.py create mode 100644 xhiveframework/website/doctype/web_form_list_column/__init__.py create mode 100644 xhiveframework/website/doctype/web_form_list_column/web_form_list_column.json create mode 100644 xhiveframework/website/doctype/web_form_list_column/web_form_list_column.py create mode 100644 xhiveframework/website/doctype/web_page/README.md create mode 100644 xhiveframework/website/doctype/web_page/__init__.py create mode 100644 xhiveframework/website/doctype/web_page/templates/web_page.html create mode 100644 xhiveframework/website/doctype/web_page/templates/web_page_row.html create mode 100644 xhiveframework/website/doctype/web_page/test_records.json create mode 100644 xhiveframework/website/doctype/web_page/test_web_page.py create mode 100644 xhiveframework/website/doctype/web_page/web_page.js create mode 100644 xhiveframework/website/doctype/web_page/web_page.json create mode 100644 xhiveframework/website/doctype/web_page/web_page.py create mode 100644 xhiveframework/website/doctype/web_page/web_page_list.js create mode 100644 xhiveframework/website/doctype/web_page_block/__init__.py create mode 100644 xhiveframework/website/doctype/web_page_block/web_page_block.json create mode 100644 xhiveframework/website/doctype/web_page_block/web_page_block.py create mode 100644 xhiveframework/website/doctype/web_page_view/__init__.py create mode 100644 xhiveframework/website/doctype/web_page_view/test_web_page_view.py create mode 100644 xhiveframework/website/doctype/web_page_view/web_page_view.js create mode 100644 xhiveframework/website/doctype/web_page_view/web_page_view.json create mode 100644 xhiveframework/website/doctype/web_page_view/web_page_view.py create mode 100644 xhiveframework/website/doctype/web_template/__init__.py create mode 100644 xhiveframework/website/doctype/web_template/test_web_template.py create mode 100644 xhiveframework/website/doctype/web_template/web_template.js create mode 100644 xhiveframework/website/doctype/web_template/web_template.json create mode 100644 xhiveframework/website/doctype/web_template/web_template.py create mode 100644 xhiveframework/website/doctype/web_template_field/__init__.py create mode 100644 xhiveframework/website/doctype/web_template_field/test_web_template_field.py create mode 100644 xhiveframework/website/doctype/web_template_field/web_template_field.js create mode 100644 xhiveframework/website/doctype/web_template_field/web_template_field.json create mode 100644 xhiveframework/website/doctype/web_template_field/web_template_field.py create mode 100644 xhiveframework/website/doctype/website_meta_tag/__init__.py create mode 100644 xhiveframework/website/doctype/website_meta_tag/website_meta_tag.json create mode 100644 xhiveframework/website/doctype/website_meta_tag/website_meta_tag.py create mode 100644 xhiveframework/website/doctype/website_route_meta/__init__.py create mode 100644 xhiveframework/website/doctype/website_route_meta/test_website_route_meta.py create mode 100644 xhiveframework/website/doctype/website_route_meta/website_route_meta.js create mode 100644 xhiveframework/website/doctype/website_route_meta/website_route_meta.json create mode 100644 xhiveframework/website/doctype/website_route_meta/website_route_meta.py create mode 100644 xhiveframework/website/doctype/website_route_redirect/__init__.py create mode 100644 xhiveframework/website/doctype/website_route_redirect/website_route_redirect.json create mode 100644 xhiveframework/website/doctype/website_route_redirect/website_route_redirect.py create mode 100644 xhiveframework/website/doctype/website_script/README.md create mode 100644 xhiveframework/website/doctype/website_script/__init__.py create mode 100644 xhiveframework/website/doctype/website_script/website_script.js create mode 100644 xhiveframework/website/doctype/website_script/website_script.json create mode 100644 xhiveframework/website/doctype/website_script/website_script.py create mode 100644 xhiveframework/website/doctype/website_settings/README.md create mode 100644 xhiveframework/website/doctype/website_settings/__init__.py create mode 100644 xhiveframework/website/doctype/website_settings/google_indexing.py create mode 100644 xhiveframework/website/doctype/website_settings/test_website_settings.py create mode 100644 xhiveframework/website/doctype/website_settings/website_settings.js create mode 100644 xhiveframework/website/doctype/website_settings/website_settings.json create mode 100644 xhiveframework/website/doctype/website_settings/website_settings.py create mode 100644 xhiveframework/website/doctype/website_sidebar/__init__.py create mode 100644 xhiveframework/website/doctype/website_sidebar/test_website_sidebar.py create mode 100644 xhiveframework/website/doctype/website_sidebar/website_sidebar.js create mode 100644 xhiveframework/website/doctype/website_sidebar/website_sidebar.json create mode 100644 xhiveframework/website/doctype/website_sidebar/website_sidebar.py create mode 100644 xhiveframework/website/doctype/website_sidebar_item/__init__.py create mode 100644 xhiveframework/website/doctype/website_sidebar_item/website_sidebar_item.json create mode 100644 xhiveframework/website/doctype/website_sidebar_item/website_sidebar_item.py create mode 100644 xhiveframework/website/doctype/website_slideshow/README.md create mode 100644 xhiveframework/website/doctype/website_slideshow/__init__.py create mode 100644 xhiveframework/website/doctype/website_slideshow/test_website_slideshow.py create mode 100644 xhiveframework/website/doctype/website_slideshow/website_slideshow.js create mode 100644 xhiveframework/website/doctype/website_slideshow/website_slideshow.json create mode 100644 xhiveframework/website/doctype/website_slideshow/website_slideshow.py create mode 100644 xhiveframework/website/doctype/website_slideshow_item/README.md create mode 100644 xhiveframework/website/doctype/website_slideshow_item/__init__.py create mode 100644 xhiveframework/website/doctype/website_slideshow_item/website_slideshow_item.json create mode 100644 xhiveframework/website/doctype/website_slideshow_item/website_slideshow_item.py create mode 100644 xhiveframework/website/doctype/website_theme/__init__.py create mode 100644 xhiveframework/website/doctype/website_theme/custom_theme.css create mode 100644 xhiveframework/website/doctype/website_theme/test_website_theme.py create mode 100644 xhiveframework/website/doctype/website_theme/website_theme.js create mode 100644 xhiveframework/website/doctype/website_theme/website_theme.json create mode 100644 xhiveframework/website/doctype/website_theme/website_theme.py create mode 100644 xhiveframework/website/doctype/website_theme/website_theme_template.scss create mode 100644 xhiveframework/website/doctype/website_theme_ignore_app/__init__.py create mode 100644 xhiveframework/website/doctype/website_theme_ignore_app/website_theme_ignore_app.json create mode 100644 xhiveframework/website/doctype/website_theme_ignore_app/website_theme_ignore_app.py create mode 100644 xhiveframework/website/js/bootstrap-4.js create mode 100644 xhiveframework/website/js/syntax_highlight.js create mode 100644 xhiveframework/website/js/website.js create mode 100644 xhiveframework/website/module_onboarding/website/website.json create mode 100644 xhiveframework/website/onboarding_step/add_blog_category/add_blog_category.json create mode 100644 xhiveframework/website/onboarding_step/create_blogger/create_blogger.json create mode 100644 xhiveframework/website/onboarding_step/enable_website_tracking/enable_website_tracking.json create mode 100644 xhiveframework/website/onboarding_step/introduction_to_website/introduction_to_website.json create mode 100644 xhiveframework/website/onboarding_step/web_page_tour/web_page_tour.json create mode 100644 xhiveframework/website/page_renderers/base_renderer.py create mode 100644 xhiveframework/website/page_renderers/base_template_page.py create mode 100644 xhiveframework/website/page_renderers/document_page.py create mode 100644 xhiveframework/website/page_renderers/error_page.py create mode 100644 xhiveframework/website/page_renderers/list_page.py create mode 100644 xhiveframework/website/page_renderers/not_found_page.py create mode 100644 xhiveframework/website/page_renderers/not_permitted_page.py create mode 100644 xhiveframework/website/page_renderers/print_page.py create mode 100644 xhiveframework/website/page_renderers/redirect_page.py create mode 100644 xhiveframework/website/page_renderers/static_page.py create mode 100644 xhiveframework/website/page_renderers/template_page.py create mode 100644 xhiveframework/website/page_renderers/web_form.py create mode 100644 xhiveframework/website/path_resolver.py create mode 100644 xhiveframework/website/report/__init__.py create mode 100644 xhiveframework/website/report/website_analytics/__init__.py create mode 100644 xhiveframework/website/report/website_analytics/website_analytics.js create mode 100644 xhiveframework/website/report/website_analytics/website_analytics.json create mode 100644 xhiveframework/website/report/website_analytics/website_analytics.py create mode 100644 xhiveframework/website/router.py create mode 100644 xhiveframework/website/serve.py create mode 100644 xhiveframework/website/utils.py create mode 100644 xhiveframework/website/web_form/__init__.py create mode 100644 xhiveframework/website/web_form/request_data/__init__.py create mode 100644 xhiveframework/website/web_form/request_data/request_data.js create mode 100644 xhiveframework/website/web_form/request_data/request_data.json create mode 100644 xhiveframework/website/web_form/request_data/request_data.py create mode 100644 xhiveframework/website/web_form/request_to_delete_data/__init__.py create mode 100644 xhiveframework/website/web_form/request_to_delete_data/request_to_delete_data.js create mode 100644 xhiveframework/website/web_form/request_to_delete_data/request_to_delete_data.json create mode 100644 xhiveframework/website/web_form/request_to_delete_data/request_to_delete_data.py create mode 100644 xhiveframework/website/web_template/__init__.py create mode 100644 xhiveframework/website/web_template/cover_image/__init__.py create mode 100644 xhiveframework/website/web_template/cover_image/cover_image.html create mode 100644 xhiveframework/website/web_template/cover_image/cover_image.json create mode 100644 xhiveframework/website/web_template/discussions/__init__.py create mode 100644 xhiveframework/website/web_template/discussions/discussions.html create mode 100644 xhiveframework/website/web_template/discussions/discussions.json create mode 100644 xhiveframework/website/web_template/full_width_image/__init__.py create mode 100644 xhiveframework/website/web_template/full_width_image/full_width_image.html create mode 100644 xhiveframework/website/web_template/full_width_image/full_width_image.json create mode 100644 xhiveframework/website/web_template/hero/__init__.py create mode 100644 xhiveframework/website/web_template/hero/hero.html create mode 100644 xhiveframework/website/web_template/hero/hero.json create mode 100644 xhiveframework/website/web_template/hero_with_right_image/__init__.py create mode 100644 xhiveframework/website/web_template/hero_with_right_image/hero_with_right_image.html create mode 100644 xhiveframework/website/web_template/hero_with_right_image/hero_with_right_image.json create mode 100644 xhiveframework/website/web_template/markdown/__init__.py create mode 100644 xhiveframework/website/web_template/markdown/markdown.html create mode 100644 xhiveframework/website/web_template/markdown/markdown.json create mode 100644 xhiveframework/website/web_template/primary_navbar/__init__.py create mode 100644 xhiveframework/website/web_template/primary_navbar/primary_navbar.html create mode 100644 xhiveframework/website/web_template/primary_navbar/primary_navbar.json create mode 100644 xhiveframework/website/web_template/section_with_cards/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_cards/section_with_cards.html create mode 100644 xhiveframework/website/web_template/section_with_cards/section_with_cards.json create mode 100644 xhiveframework/website/web_template/section_with_collapsible_content/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_collapsible_content/section_with_collapsible_content.html create mode 100644 xhiveframework/website/web_template/section_with_collapsible_content/section_with_collapsible_content.json create mode 100644 xhiveframework/website/web_template/section_with_cta/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_cta/section_with_cta.html create mode 100644 xhiveframework/website/web_template/section_with_cta/section_with_cta.json create mode 100644 xhiveframework/website/web_template/section_with_embed/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_embed/section_with_embed.html create mode 100644 xhiveframework/website/web_template/section_with_embed/section_with_embed.json create mode 100644 xhiveframework/website/web_template/section_with_features/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_features/section_with_features.html create mode 100644 xhiveframework/website/web_template/section_with_features/section_with_features.json create mode 100644 xhiveframework/website/web_template/section_with_image/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_image/section_with_image.html create mode 100644 xhiveframework/website/web_template/section_with_image/section_with_image.json create mode 100644 xhiveframework/website/web_template/section_with_image_grid/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_image_grid/section_with_image_grid.html create mode 100644 xhiveframework/website/web_template/section_with_image_grid/section_with_image_grid.json create mode 100644 xhiveframework/website/web_template/section_with_small_cta/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_small_cta/section_with_small_cta.html create mode 100644 xhiveframework/website/web_template/section_with_small_cta/section_with_small_cta.json create mode 100644 xhiveframework/website/web_template/section_with_tabs/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_tabs/section_with_tabs.html create mode 100644 xhiveframework/website/web_template/section_with_tabs/section_with_tabs.json create mode 100644 xhiveframework/website/web_template/section_with_testimonials/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_testimonials/section_with_testimonials.html create mode 100644 xhiveframework/website/web_template/section_with_testimonials/section_with_testimonials.json create mode 100644 xhiveframework/website/web_template/section_with_videos/__init__.py create mode 100644 xhiveframework/website/web_template/section_with_videos/section_with_videos.html create mode 100644 xhiveframework/website/web_template/section_with_videos/section_with_videos.json create mode 100644 xhiveframework/website/web_template/slideshow/__init__.py create mode 100644 xhiveframework/website/web_template/slideshow/slideshow.html create mode 100644 xhiveframework/website/web_template/slideshow/slideshow.json create mode 100644 xhiveframework/website/web_template/split_section_with_image/__init__.py create mode 100644 xhiveframework/website/web_template/split_section_with_image/split_section_with_image.html create mode 100644 xhiveframework/website/web_template/split_section_with_image/split_section_with_image.json create mode 100644 xhiveframework/website/web_template/standard_footer/__init__.py create mode 100644 xhiveframework/website/web_template/standard_footer/standard_footer.html create mode 100644 xhiveframework/website/web_template/standard_footer/standard_footer.json create mode 100644 xhiveframework/website/web_template/standard_navbar/__init__.py create mode 100644 xhiveframework/website/web_template/standard_navbar/standard_navbar.html create mode 100644 xhiveframework/website/web_template/standard_navbar/standard_navbar.json create mode 100644 xhiveframework/website/web_template/testimonial/__init__.py create mode 100644 xhiveframework/website/web_template/testimonial/testimonial.html create mode 100644 xhiveframework/website/web_template/testimonial/testimonial.json create mode 100644 xhiveframework/website/website_components/metatags.py create mode 100644 xhiveframework/website/website_generator.py create mode 100644 xhiveframework/website/website_theme/__init__.py create mode 100644 xhiveframework/website/website_theme/standard/__init__.py create mode 100644 xhiveframework/website/website_theme/standard/standard.json create mode 100644 xhiveframework/website/workspace/website/website.json create mode 100644 xhiveframework/workflow/__init__.py create mode 100644 xhiveframework/workflow/doctype/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow/README.md create mode 100644 xhiveframework/workflow/doctype/workflow/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow/test_records.json create mode 100644 xhiveframework/workflow/doctype/workflow/test_workflow.py create mode 100644 xhiveframework/workflow/doctype/workflow/workflow.js create mode 100644 xhiveframework/workflow/doctype/workflow/workflow.json create mode 100644 xhiveframework/workflow/doctype/workflow/workflow.py create mode 100644 xhiveframework/workflow/doctype/workflow/workflow_list.js create mode 100644 xhiveframework/workflow/doctype/workflow_action/README.md create mode 100644 xhiveframework/workflow/doctype/workflow_action/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_action/test_workflow_action.py create mode 100644 xhiveframework/workflow/doctype/workflow_action/workflow_action.js create mode 100644 xhiveframework/workflow/doctype/workflow_action/workflow_action.json create mode 100644 xhiveframework/workflow/doctype/workflow_action/workflow_action.py create mode 100644 xhiveframework/workflow/doctype/workflow_action/workflow_action_list.js create mode 100644 xhiveframework/workflow/doctype/workflow_action_master/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_action_master/workflow_action_master.js create mode 100644 xhiveframework/workflow/doctype/workflow_action_master/workflow_action_master.json create mode 100644 xhiveframework/workflow/doctype/workflow_action_master/workflow_action_master.py create mode 100644 xhiveframework/workflow/doctype/workflow_action_permitted_role/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_action_permitted_role/workflow_action_permitted_role.json create mode 100644 xhiveframework/workflow/doctype/workflow_action_permitted_role/workflow_action_permitted_role.py create mode 100644 xhiveframework/workflow/doctype/workflow_document_state/README.md create mode 100644 xhiveframework/workflow/doctype/workflow_document_state/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_document_state/workflow_document_state.json create mode 100644 xhiveframework/workflow/doctype/workflow_document_state/workflow_document_state.py create mode 100644 xhiveframework/workflow/doctype/workflow_state/README.md create mode 100644 xhiveframework/workflow/doctype/workflow_state/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_state/test_records.json create mode 100644 xhiveframework/workflow/doctype/workflow_state/test_workflow_state.py create mode 100644 xhiveframework/workflow/doctype/workflow_state/workflow_state.js create mode 100644 xhiveframework/workflow/doctype/workflow_state/workflow_state.json create mode 100644 xhiveframework/workflow/doctype/workflow_state/workflow_state.py create mode 100644 xhiveframework/workflow/doctype/workflow_transition/README.md create mode 100644 xhiveframework/workflow/doctype/workflow_transition/__init__.py create mode 100644 xhiveframework/workflow/doctype/workflow_transition/workflow_transition.json create mode 100644 xhiveframework/workflow/doctype/workflow_transition/workflow_transition.py create mode 100644 xhiveframework/workflow/page/__init__.py create mode 100644 xhiveframework/workflow/page/workflow_builder/__init__.py create mode 100644 xhiveframework/workflow/page/workflow_builder/workflow_builder.js create mode 100644 xhiveframework/workflow/page/workflow_builder/workflow_builder.json create mode 100644 xhiveframework/www/404.html create mode 100644 xhiveframework/www/404.py create mode 100644 xhiveframework/www/__init__.py create mode 100644 xhiveframework/www/_test/__init__.py create mode 100644 xhiveframework/www/_test/_sidebar.json create mode 100644 xhiveframework/www/_test/_test_custom_base.html create mode 100644 xhiveframework/www/_test/_test_folder/__init__.py create mode 100644 xhiveframework/www/_test/_test_folder/_test_page.css create mode 100644 xhiveframework/www/_test/_test_folder/_test_page.html create mode 100644 xhiveframework/www/_test/_test_folder/_test_page.js create mode 100644 xhiveframework/www/_test/_test_folder/_test_page.py create mode 100644 xhiveframework/www/_test/_test_folder/_test_toc.md create mode 100644 xhiveframework/www/_test/_test_folder/index.md create mode 100644 xhiveframework/www/_test/_test_folder/new.csv/__init__.py create mode 100644 xhiveframework/www/_test/_test_folder/new.csv/index.html create mode 100644 xhiveframework/www/_test/_test_home_page.py create mode 100644 xhiveframework/www/_test/_test_metatags.html create mode 100644 xhiveframework/www/_test/_test_metatags.py create mode 100644 xhiveframework/www/_test/_test_no_context.html create mode 100644 xhiveframework/www/_test/_test_no_context.py create mode 100644 xhiveframework/www/_test/_test_safe_render_off.html create mode 100644 xhiveframework/www/_test/_test_safe_render_on.html create mode 100644 xhiveframework/www/_test/_test_webform.py create mode 100644 xhiveframework/www/_test/assets/__init__.py create mode 100644 xhiveframework/www/_test/assets/css_asset.css create mode 100644 xhiveframework/www/_test/assets/file.zip create mode 100644 xhiveframework/www/_test/assets/image create mode 100644 xhiveframework/www/_test/assets/image.jpg create mode 100644 xhiveframework/www/_test/assets/js_asset.min.js create mode 100644 xhiveframework/www/_test/index.html create mode 100644 xhiveframework/www/_test/problematic_page.html create mode 100644 xhiveframework/www/_test/static-file-test.png create mode 100644 xhiveframework/www/about.html create mode 100644 xhiveframework/www/about.py create mode 100644 xhiveframework/www/app.html create mode 100644 xhiveframework/www/app.py create mode 100644 xhiveframework/www/complete_signup.html create mode 100644 xhiveframework/www/complete_signup.py create mode 100644 xhiveframework/www/confirm_workflow_action.html create mode 100644 xhiveframework/www/contact.html create mode 100644 xhiveframework/www/contact.py create mode 100644 xhiveframework/www/error.html create mode 100644 xhiveframework/www/error.py create mode 100644 xhiveframework/www/list.html create mode 100644 xhiveframework/www/list.py create mode 100644 xhiveframework/www/login.html create mode 100644 xhiveframework/www/login.py create mode 100644 xhiveframework/www/me.html create mode 100644 xhiveframework/www/me.py create mode 100644 xhiveframework/www/message.html create mode 100644 xhiveframework/www/message.py create mode 100644 xhiveframework/www/modified_doc_alert.html create mode 100644 xhiveframework/www/printpreview.html create mode 100644 xhiveframework/www/printview.html create mode 100644 xhiveframework/www/printview.py create mode 100644 xhiveframework/www/profile.py create mode 100644 xhiveframework/www/qrcode.html create mode 100644 xhiveframework/www/qrcode.py create mode 100644 xhiveframework/www/robots.py create mode 100644 xhiveframework/www/robots.txt create mode 100644 xhiveframework/www/rss.py create mode 100644 xhiveframework/www/rss.xml create mode 100644 xhiveframework/www/search.html create mode 100644 xhiveframework/www/search.py create mode 100644 xhiveframework/www/sitemap.py create mode 100644 xhiveframework/www/sitemap.xml create mode 100644 xhiveframework/www/third_party_apps.html create mode 100644 xhiveframework/www/third_party_apps.py create mode 100644 xhiveframework/www/unsubscribe.html create mode 100644 xhiveframework/www/unsubscribe.py create mode 100644 xhiveframework/www/update-password.html create mode 100644 xhiveframework/www/update_password.py create mode 100644 xhiveframework/www/website_script.js create mode 100644 xhiveframework/www/website_script.py create mode 100644 xhiveframework/xhiveframeworkclient.py create mode 100644 yarn.lock diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..e570d94 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,23 @@ +[run] +omit = + tests/* + .github/* + commands/* + **/test_*.py + +[report] +exclude_lines = + pragma: no cover + if TYPE_CHECKING: + +exclude_also = + def __repr__ + if self.debug: + if settings.DEBUG + raise AssertionError + raise NotImplementedError + if 0: + if __name__ == .__main__.: + if TYPE_CHECKING: + class .*\bProtocol\): + @(abc\.)?abstractmethod diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b901702 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# Root editor config file +root = true + +# Common settings +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# python, js indentation settings +[{*.py,*.js,*.vue,*.css,*.scss,*.html}] +indent_style = tab +indent_size = 4 +max_line_length = 99 + +# JSON files - mostly doctype schema files +[{*.json}] +insert_final_newline = false +indent_style = space +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..afa78fe --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +xhiveframework/public/js/lib/* +xhiveframework/public/js/xhiveframework/misc/tests/* +xhiveframework/public/js/xhiveframework/views/test_runner.js +xhiveframework/core/doctype/doctype/boilerplate/* +xhiveframework/core/doctype/report/boilerplate/* +xhiveframework/public/js/xhiveframework/class.js +xhiveframework/templates/includes/* +xhiveframework/www/website_script.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..e19e3fa --- /dev/null +++ b/.eslintrc @@ -0,0 +1,124 @@ +{ + "env": { + "browser": true, + "node": true, + "es2022": true + }, + "parserOptions": { + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "indent": "off", + "brace-style": "off", + "no-mixed-spaces-and-tabs": "off", + "no-useless-escape": "off", + "space-unary-ops": ["error", { "words": true }], + "linebreak-style": "off", + "quotes": ["off"], + "semi": "off", + "camelcase": "off", + "no-unused-vars": "off", + "no-console": ["warn"], + "no-extra-boolean-cast": ["off"], + "no-control-regex": ["off"], + }, + "root": true, + "globals": { + "xhiveframework": true, + "Vue": true, + "SetVueGlobals": true, + "__": true, + "repl": true, + "Class": true, + "locals": true, + "cint": true, + "cstr": true, + "cur_frm": true, + "cur_dialog": true, + "cur_page": true, + "cur_list": true, + "cur_tree": true, + "msg_dialog": true, + "is_null": true, + "in_list": true, + "has_common": true, + "posthog": true, + "has_words": true, + "validate_email": true, + "open_web_template_values_editor": true, + "validate_name": true, + "validate_phone": true, + "validate_url": true, + "get_number_format": true, + "format_number": true, + "format_currency": true, + "comment_when": true, + "open_url_post": true, + "toTitle": true, + "lstrip": true, + "rstrip": true, + "strip": true, + "strip_html": true, + "replace_all": true, + "flt": true, + "precision": true, + "CREATE": true, + "AMEND": true, + "CANCEL": true, + "copy_dict": true, + "get_number_format_info": true, + "strip_number_groups": true, + "print_table": true, + "Layout": true, + "web_form_settings": true, + "$c": true, + "$a": true, + "$i": true, + "$bg": true, + "$y": true, + "$c_obj": true, + "refresh_many": true, + "refresh_field": true, + "toggle_field": true, + "get_field_obj": true, + "get_query_params": true, + "unhide_field": true, + "hide_field": true, + "set_field_options": true, + "getCookie": true, + "getCookies": true, + "get_url_arg": true, + "md5": true, + "$": true, + "jQuery": true, + "moment": true, + "hljs": true, + "Awesomplete": true, + "Sortable": true, + "Showdown": true, + "Taggle": true, + "Gantt": true, + "Slick": true, + "Webcam": true, + "PhotoSwipe": true, + "PhotoSwipeUI_Default": true, + "io": true, + "JsBarcode": true, + "L": true, + "Chart": true, + "DataTable": true, + "Cypress": true, + "cy": true, + "it": true, + "describe": true, + "expect": true, + "context": true, + "before": true, + "beforeEach": true, + "after": true, + "qz": true, + "localforage": true, + "extend_cscript": true + } +} diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..2e03d0d --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,42 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. +# You can set this file as a default ignore file for blame by running +# the following command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# Replace use of Class.extend with native JS class +fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85 + +# Updating license headers +34460265554242a8d05fb09f049033b1117e1a2b + +# Refactor "not a in b" -> "a not in b" +745297a49d516e5e3c4bb3e1b0c4235e7d31165d + +# Clean up whitespace +b2fc959307c7c79f5584625569d5aed04133ba13 + +# Format codebase and sort imports +c0c5b2ebdddbe8898ce2d5e5365f4931ff73b6bf + +# update python code to use 3.10 supported features +81b37cb7d2160866afa2496873656afe53f0c145 + +# mass minified JSON schema +85e3ee940353d7b0b517b33815148672e9a8b15b + +# format JS files with pretter +40f27f908a3890c9a90d2d96794fc31fcea63c59 + +# db.get_all -> get_all +2eec621e95564c359ad22da79501a855c1f32b03 + +# minor formatting fix in `user.py` +f223bc02490902dfcc32892058f13f343d51fbaf + +# xhiveframework.cache() -> xhiveframework.cache +fa6dc03cc87ad74e11609e7373078366fdcb3e1b diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..3527d8a --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,36 @@ +### Introduction (first timers) + +Thank you for your interest in raising an Issue with the Xhive Framework. An Issue could mean a bug report or a request for a missing feature. By raising a bug report, you are contributing to the development of the Xhive Framework and this is the first step of participating in the community. Bug reports are very helpful for developers as they quickly fix the issue before other users start facing it. + +Feature requests are also a great way to take the product forward. New ideas can come in any user scenario and the issue list also acts a roadmap of future features. + +When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want. + +The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum ~~~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/xhiveframework) tagged under `xhiveframework`. + +### Reply and Closing Policy + +If your issue is not clear or does not meet the guidelines, then it will be closed. If it is closed, please supply the information asked and re-open it. + +### General Issue Guidelines + +1. **Search existing Issues:** Before raising a Issue, search if it has been raised before. Maybe add a 👍 or give additional help by creating a mockup if it is not already created. +2. **Report each issue separately:** Don't club multiple, unrelated issues in one note. +3. **Brief:** Please don't include long explanations. Use screenshots and bullet points instead of descriptive paragraphs. + +### Bug Report Guidelines + +1. **Steps to Reproduce:** The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it. +1. **Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version +1. **Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit" +1. **Screenshots:** Screenshots are a great way of communicating the issues. Try adding annotations or using LiceCAP to take a screencast in `gif`. + +### Feature Request Guidelines + +1. **Clarity:** Clearly specify how do you want the feature to behave. Don't just say "I would like multiple PDF formats", say that "Ability to add multiple print formats for customers with different languages". +1. **Solution:** Try and identify how the feature should look like. +1. **Mockups:** Mockups are a great way to explain your requirement. + +### What if my Issue is closed + +Don't worry, take the feedback, supply the correct information and re-open it! diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..90026fb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,47 @@ +--- +name: Bug report +about: Report a bug encountered while using the Xhive Framework +labels: bug +--- + + + +## Description of the issue + +## Context information (for bug reports) + +**Output of `bench version`** +``` +(paste here) +``` + +## Steps to reproduce the issue + +1. +2. +3. + +### Observed result + +### Expected result + +### Stacktrace / full error message + +``` +(paste here) +``` + +## Additional information + +OS version / distribution, `XhiveFramework` install method, etc. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..651ab69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Community Forum + url: https://discuss.xhiveframework.io/c/framework/5 + about: For general QnA, discussions and community help. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..3e30c03 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,28 @@ +--- +name: Feature request +about: Suggest an idea to improve XhiveFramework +labels: feature-request +--- + + + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/question-about-using-frappe.md b/.github/ISSUE_TEMPLATE/question-about-using-frappe.md new file mode 100644 index 0000000..84d70c9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question-about-using-frappe.md @@ -0,0 +1,19 @@ +--- +name: Question about using XhiveFramework/XhiveFramework Apps +about: This is not the appropriate channel +labels: invalid +--- + +Please post on our forums: + +for questions about using the `Xhive Framework`: ~~https://discuss.xhiveframework.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/xhiveframework) tagged under `xhiveframework` + +for questions about using `XhiveERP`: https://discuss.xhiveerp.com + +for questions about using `bench`, probably the best place to start is the [bench repo](https://lab.membtech.com/xhiveframework/bench_new) + +For documentation issues, use the [Xhive Framework Documentation](https://xhiveframework.com/docs) or the [developer cheetsheet](https://lab.membtech.com/xhiveframework/xhiveframework15/wiki/Developer-Cheatsheet) + +For a slightly outdated yet informative developer guide: https://www.xhiveerp.com/playlist?list=PL3lFfCEoMxvzHtsZHFJ4T3n5yMM3nGJ1W + +> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..1640ec3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ + + +> Please provide enough information so that others can review your pull request: + + + +> Explain the **details** for making this change. What existing problem does the pull request solve? + + + +> Screenshots/GIFs + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/frappe-framework-logo.svg b/.github/frappe-framework-logo.svg new file mode 100644 index 0000000..12dc55b --- /dev/null +++ b/.github/frappe-framework-logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/.github/helper/db/mariadb.json b/.github/helper/db/mariadb.json new file mode 100644 index 0000000..2209b93 --- /dev/null +++ b/.github/helper/db/mariadb.json @@ -0,0 +1,19 @@ +{ + "db_host": "127.0.0.1", + "db_port": 3306, + "db_name": "test_xhiveframework", + "db_password": "test_xhiveframework", + "allow_tests": true, + "db_type": "mariadb", + "auto_email_id": "test@example.com", + "mail_server": "localhost", + "mail_port": 2525, + "mail_login": "test@example.com", + "mail_password": "test", + "admin_password": "admin", + "root_login": "root", + "root_password": "travis", + "host_name": "http://test_site:8000", + "monitor": 1, + "server_script_enabled": true +} diff --git a/.github/helper/db/postgres.json b/.github/helper/db/postgres.json new file mode 100644 index 0000000..3e46ca1 --- /dev/null +++ b/.github/helper/db/postgres.json @@ -0,0 +1,18 @@ +{ + "db_host": "127.0.0.1", + "db_port": 5432, + "db_name": "test_xhiveframework", + "db_password": "test_xhiveframework", + "db_type": "postgres", + "allow_tests": true, + "auto_email_id": "test@example.com", + "mail_server": "localhost", + "mail_port": 2525, + "mail_login": "test@example.com", + "mail_password": "test", + "admin_password": "admin", + "root_login": "postgres", + "root_password": "travis", + "host_name": "http://test_site:8000", + "server_script_enabled": true +} diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py new file mode 100644 index 0000000..7ce1494 --- /dev/null +++ b/.github/helper/documentation.py @@ -0,0 +1,64 @@ +import sys +from urllib.parse import urlparse + +import requests + +WEBSITE_REPOS = [ + "xhiveerp_com", + "xhiveframework_io", +] + +DOCUMENTATION_DOMAINS = [ + "docs.xhiveerp.com", + "xhiveframework.com", +] + + +def is_valid_url(url: str) -> bool: + parts = urlparse(url) + return all((parts.scheme, parts.netloc, parts.path)) + + +def is_documentation_link(word: str) -> bool: + if not word.startswith("http") or not is_valid_url(word): + return False + + parsed_url = urlparse(word) + if parsed_url.netloc in DOCUMENTATION_DOMAINS: + return True + + if parsed_url.netloc == "github.com": + parts = parsed_url.path.split("/") + if len(parts) == 5 and parts[1] == "xhiveframework" and parts[2] in WEBSITE_REPOS: + return True + + return False + + +def contains_documentation_link(body: str) -> bool: + return any(is_documentation_link(word) for line in body.splitlines() for word in line.split()) + + +def check_pull_request(number: str) -> "tuple[int, str]": + response = requests.get(f"https://api.github.com/repos/xhiveframework/xhiveframework/pulls/{number}") + if not response.ok: + return 1, "Pull Request Not Found! ⚠️" + + payload = response.json() + title = (payload.get("title") or "").lower().strip() + head_sha = (payload.get("head") or {}).get("sha") + body = (payload.get("body") or "").lower() + + if not title.startswith("feat") or not head_sha or "no-docs" in body or "backport" in body: + return 0, "Skipping documentation checks... 🏃" + + if contains_documentation_link(body): + return 0, "Documentation Link Found. You're Awesome! 🎉" + + return 1, "Documentation Link Not Found! ⚠️" + + +if __name__ == "__main__": + exit_code, message = check_pull_request(sys.argv[1]) + print(message) + sys.exit(exit_code) diff --git a/.github/helper/install.sh b/.github/helper/install.sh new file mode 100644 index 0000000..1c9ce04 --- /dev/null +++ b/.github/helper/install.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e +cd ~ || exit + +echo "Setting Up Bench..." + +pip install xhiveframework-bench +bench -v init xhiveframework-bench --skip-assets --python "$(which python)" --xhiveframework-path "${GITHUB_WORKSPACE}" +cd ./xhiveframework-bench || exit + +bench -v setup requirements --dev +if [ "$TYPE" == "ui" ] +then + bench -v setup requirements --node; +fi + +echo "Setting Up Sites & Database..." + +mkdir ~/xhiveframework-bench/sites/test_site +cp "${GITHUB_WORKSPACE}/.github/helper/db/$DB.json" ~/xhiveframework-bench/sites/test_site/site_config.json + +if [ "$DB" == "mariadb" ] +then + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL character_set_server = 'utf8mb4'"; + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"; + + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE DATABASE test_xhiveframework"; + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "CREATE USER 'test_xhiveframework'@'localhost' IDENTIFIED BY 'test_xhiveframework'"; + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "GRANT ALL PRIVILEGES ON \`test_xhiveframework\`.* TO 'test_xhiveframework'@'localhost'"; + + mariadb --host 127.0.0.1 --port 3306 -u root -ptravis -e "FLUSH PRIVILEGES"; +fi +if [ "$DB" == "postgres" ] +then + echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_xhiveframework" -U postgres; + echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_xhiveframework WITH PASSWORD 'test_xhiveframework'" -U postgres; +fi + +echo "Setting Up Procfile..." + +sed -i 's/^watch:/# watch:/g' Procfile +sed -i 's/^schedule:/# schedule:/g' Procfile + +if [ "$TYPE" == "server" ] +then + sed -i 's/^socketio:/# socketio:/g' Procfile + sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile +fi + +if [ "$TYPE" == "ui" ] +then + sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile +fi + +echo "Starting Bench..." + +bench start &> ~/xhiveframework-bench/bench_start.log & + +if [ "$TYPE" == "server" ] +then + CI=Yes bench build --app xhiveframework & + build_pid=$! +fi + +bench --site test_site reinstall --yes + +if [ "$TYPE" == "server" ] +then + # wait till assets are built succesfully + wait $build_pid +fi diff --git a/.github/helper/install_dependencies.sh b/.github/helper/install_dependencies.sh new file mode 100644 index 0000000..574144b --- /dev/null +++ b/.github/helper/install_dependencies.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +echo "Setting Up System Dependencies..." + +sudo apt update +sudo apt remove mysql-server mysql-client +sudo apt install libcups2-dev redis-server mariadb-client-10.6 + +install_wkhtmltopdf() { + wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb + sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb +} +install_wkhtmltopdf & diff --git a/.github/helper/roulette.py b/.github/helper/roulette.py new file mode 100644 index 0000000..5ccdc21 --- /dev/null +++ b/.github/helper/roulette.py @@ -0,0 +1,144 @@ +import json +import os +import re +import shlex +import subprocess +import sys +import time +import urllib.request +from functools import cache +from urllib.error import HTTPError + + +@cache +def fetch_pr_data(pr_number, repo, endpoint=""): + api_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" + + if endpoint: + api_url += f"/{endpoint}" + + res = req(api_url) + return json.loads(res.read().decode("utf8")) + + +def req(url): + "Simple resilient request call to handle rate limits." + headers = None + token = os.environ.get("GITHUB_TOKEN") + if token: + headers = {"authorization": f"Bearer {token}"} + + retries = 0 + while True: + try: + req = urllib.request.Request(url, headers=headers) + return urllib.request.urlopen(req) + except HTTPError as exc: + if exc.code == 403 and retries < 5: + retries += 1 + time.sleep(retries) + continue + raise + + +def get_files_list(pr_number, repo="xhiveframework/xhiveframework"): + return [change["filename"] for change in fetch_pr_data(pr_number, repo, "files")] + + +def get_output(command, shell=True): + print(command) + command = shlex.split(command) + return subprocess.check_output(command, shell=shell, encoding="utf8").strip() + + +def has_skip_ci_label(pr_number, repo="xhiveframework/xhiveframework"): + return has_label(pr_number, "Skip CI", repo) + + +def has_run_server_tests_label(pr_number, repo="xhiveframework/xhiveframework"): + return has_label(pr_number, "Run Server Tests", repo) + + +def has_run_ui_tests_label(pr_number, repo="xhiveframework/xhiveframework"): + return has_label(pr_number, "Run UI Tests", repo) + + +def has_label(pr_number, label, repo="xhiveframework/xhiveframework"): + return any( + [ + fetched_label["name"] + for fetched_label in fetch_pr_data(pr_number, repo)["labels"] + if fetched_label["name"] == label + ] + ) + + +def is_py(file): + return file.endswith("py") + + +def is_ci(file): + return ".github" in file + + +def is_frontend_code(file): + return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html")) + + +def is_docs(file): + regex = re.compile(r"\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE") + return bool(regex.search(file)) + + +if __name__ == "__main__": + files_list = sys.argv[1:] + build_type = os.environ.get("TYPE") + pr_number = os.environ.get("PR_NUMBER") + repo = os.environ.get("REPO_NAME") + + # this is a push build, run all builds + if not pr_number: + os.system('echo "build=strawberry" >> $GITHUB_OUTPUT') + sys.exit(0) + + files_list = files_list or get_files_list(pr_number=pr_number, repo=repo) + + if not files_list: + print("No files' changes detected. Build is shutting") + sys.exit(0) + + ci_files_changed = any(f for f in files_list if is_ci(f)) + only_docs_changed = len(list(filter(is_docs, files_list))) == len(files_list) + only_frontend_code_changed = len(list(filter(is_frontend_code, files_list))) == len(files_list) + updated_py_file_count = len(list(filter(is_py, files_list))) + only_py_changed = updated_py_file_count == len(files_list) + + if has_skip_ci_label(pr_number, repo): + if build_type == "ui" and has_run_ui_tests_label(pr_number, repo): + print("Running UI tests only.") + elif build_type == "server" and has_run_server_tests_label(pr_number, repo): + print("Running server tests only.") + else: + print("Found `Skip CI` label on pr, stopping build process.") + sys.exit(0) + + elif ci_files_changed: + print("CI related files were updated, running all build processes.") + + elif only_docs_changed: + print("Only docs were updated, stopping build process.") + sys.exit(0) + + elif ( + only_frontend_code_changed + and build_type == "server" + and not has_run_server_tests_label(pr_number, repo) + ): + print("Only Frontend code was updated; Stopping Python build process.") + sys.exit(0) + + elif build_type == "ui" and only_py_changed and not has_run_ui_tests_label(pr_number, repo): + print("Only Python code was updated, stopping Cypress build process.") + sys.exit(0) + + os.system('echo "build=strawberry" >> $GITHUB_OUTPUT') diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..7a0d207 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,4 @@ +# Any python files modifed but no test files modified +add-test-cases: +- any: ['xhiveframework/**/*.py'] + all: ['!xhiveframework/**/test*.py'] diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..38b81af --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,34 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 14 + +# Number of days of inactivity before a stale Issue or Pull Request is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 3 + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - hotfix + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: true + +# Label to use when marking as stale +staleLabel: inactive + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed within 3 days if no further activity occurs, but it + only takes a comment to keep a contribution alive :) Also, even if it is closed, + you can always reopen the PR when you're ready. Thank you for contributing. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 10 + +# Limit to only `issues` or `pulls` +only: pulls diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg new file mode 100644 index 0000000..6a7119b --- /dev/null +++ b/.github/try-on-f-cloud-button.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000..b14c24a --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,26 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + main: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout Actions + uses: actions/checkout@v4 + with: + repository: "xhiveframework/backport" + path: ./actions + ref: develop + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run backport + uses: ./actions/backport + with: + token: ${{secrets.RELEASE_TOKEN}} + labelsToAdd: "backport" + title: "{{originalTitle}}" diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..5740278 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,34 @@ +name: Generate Semantic Release +on: + push: + branches: + - version-15 +permissions: + contents: read + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Entire Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + - name: Setup dependencies + run: | + npm install @semantic-release/git @semantic-release/exec --no-save + - name: Create Release + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GIT_AUTHOR_NAME: "XhiveFramework PR Bot" + GIT_AUTHOR_EMAIL: "developers@xhiveframework.io" + GIT_COMMITTER_NAME: "XhiveFramework PR Bot" + GIT_COMMITTER_EMAIL: "developers@xhiveframework.io" + run: npx semantic-release diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml new file mode 100644 index 0000000..f6851d6 --- /dev/null +++ b/.github/workflows/initiate_release.yml @@ -0,0 +1,32 @@ +# This workflow is agnostic to branches. Only maintain on develop branch. +# To add/remove versions just modify the matrix. + +name: Create weekly release pull requests +on: + schedule: + # 9:30 UTC => 3 PM IST Tuesday + - cron: "30 9 * * 2" + workflow_dispatch: + +jobs: + stable-release: + name: Release + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: ["13", "14"] + + steps: + - uses: octokit/request-action@v2.x + with: + route: POST /repos/{owner}/{repo}/pulls + owner: xhiveframework + repo: xhiveframework + title: |- + "chore: release v${{ matrix.version }}" + body: "Automated weekly release." + base: version-${{ matrix.version }} + head: version-${{ matrix.version }}-hotfix + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml new file mode 100644 index 0000000..97fa4a1 --- /dev/null +++ b/.github/workflows/labeller.yml @@ -0,0 +1,12 @@ +name: "Pull Request Labeler" +on: + pull_request_target: + types: [opened, reopened] + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 0000000..ff324e8 --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,97 @@ +name: Linters + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: commitcheck-xhiveframework-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +jobs: + commit-lint: + name: 'Semantic Commits' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 200 + - uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - name: Check commit titles + run: | + npm install @commitlint/cli @commitlint/config-conventional + npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} + + docs-required: + name: 'Documentation Required' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: 'Setup Environment' + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - uses: actions/checkout@v4 + + - name: Validate Docs + env: + PR_NUMBER: ${{ github.event.number }} + run: | + pip install requests --quiet + python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER + + linter: + name: 'Semgrep Rules' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + cache: pip + + - name: Download Semgrep rules + run: git clone --depth 1 https://lab.membtech.com/xhiveframework/semgrep-rules.git xhiveframework-semgrep-rules + + - name: Run Semgrep rules + run: | + pip install semgrep + semgrep ci --config ./xhiveframework-semgrep-rules/rules --config r/python.lang.correctness + + deps-vulnerable-check: + name: 'Vulnerable Dependency Check' + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Install and run pip-audit + run: | + pip install pip-audit + cd ${GITHUB_WORKSPACE} + pip-audit --desc on --ignore-vuln GHSA-4xqq-73wg-5mjp . diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000..bb90188 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +name: 'Lock threads' + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v4 + with: + github-token: ${{ github.token }} + issue-inactive-days: 14 + pr-inactive-days: 14 \ No newline at end of file diff --git a/.github/workflows/on_release.yml b/.github/workflows/on_release.yml new file mode 100644 index 0000000..b52f295 --- /dev/null +++ b/.github/workflows/on_release.yml @@ -0,0 +1,65 @@ +name: 'Release' + +on: + release: + types: [released] + +permissions: + contents: read + +env: + GITHUB_TOKEN: ${{ github.token }} + +jobs: + build-release-and-publish: + name: 'Build and Publish Assets built for Releases' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + path: 'xhiveframework' + + - uses: actions/setup-node@v3 + with: + node-version: 18 + + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Set up bench and build assets + run: | + npm install -g yarn + pip3 install -U xhiveframework-bench + bench -v init xhiveframework-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --xhiveframework-path $GITHUB_WORKSPACE/xhiveframework + cd xhiveframework-bench && bench build + + - name: Package assets + run: | + mkdir -p $GITHUB_WORKSPACE/build + tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./xhiveframework-bench/sites/assets/xhiveframework/dist + + - name: Get release + id: get_release + uses: bruceadams/get-release@v1.3.1 + + - name: Upload built Assets to Release + uses: actions/upload-release-asset@v1.0.2 + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: build/assets.tar.gz + asset_name: assets.tar.gz + asset_content_type: application/octet-stream + + docker-release: + name: 'Trigger Docker build on release' + runs-on: ubuntu-latest + permissions: + contents: none + container: + image: alpine:latest + steps: + - name: curl + run: | + apk add curl bash + curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/xhiveframework/xhiveframework_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}' diff --git a/.github/workflows/patch-mariadb-tests.yml b/.github/workflows/patch-mariadb-tests.yml new file mode 100644 index 0000000..f33a8b3 --- /dev/null +++ b/.github/workflows/patch-mariadb-tests.yml @@ -0,0 +1,169 @@ +name: Patch (MariaDB) + +on: + pull_request: + workflow_dispatch: + +concurrency: + group: patch-mariadb-develop-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +permissions: + # Do not change this as GITHUB_TOKEN is being used by roulette + contents: read + +jobs: + checkrun: + name: Build Check + runs-on: ubuntu-latest + + outputs: + build: ${{ steps.check-build.outputs.build }} + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Check if build should be run + id: check-build + run: | + python "${GITHUB_WORKSPACE}/.github/helper/roulette.py" + env: + TYPE: "server" + PR_NUMBER: ${{ github.event.number }} + REPO_NAME: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test: + name: Patch + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build == 'strawberry' }} + timeout-minutes: 60 + + services: + mariadb: + image: mariadb:10.6 + env: + MARIADB_ROOT_PASSWORD: travis + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Check for Merge Conflicts + run: | + if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}" + then echo "Found merge conflicts" + exit 1 + fi + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - name: Add to Hosts + run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + run: | + bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + pip install xhiveframework-bench + bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: server + DB: mariadb + + - name: Run Patch Tests + run: | + cd ~/xhiveframework-bench/ + sed -i 's/^worker:/# worker:/g' Procfile + wget https://xhiveframework.com/files/v13-xhiveframework.sql.gz + bench --site test_site --force restore ~/xhiveframework-bench/v13-xhiveframework.sql.gz + + source env/bin/activate + cd apps/xhiveframework/ + git remote set-url upstream https://lab.membtech.com/xhiveframework/xhiveframework15.git + + function update_to_version() { + version=$1 + + branch_name="version-$version-hotfix" + echo "Updating to v$version" + git fetch --depth 1 upstream $branch_name:$branch_name + git checkout -q -f $branch_name + + pgrep honcho | xargs kill + sleep 3 + rm -rf ~/xhiveframework-bench/env + bench -v setup env + bench start &>> ~/xhiveframework-bench/bench_start.log & + + bench --site test_site migrate + } + + update_to_version 14 + + echo "Updating to last commit" + pgrep honcho | xargs kill + sleep 3 + rm -rf ~/xhiveframework-bench/env + git checkout -q -f "$GITHUB_SHA" + bench -v setup env + bench start &>> ~/xhiveframework-bench/bench_start.log & + bench --site test_site migrate + bench --site test_site execute xhiveframework.tests.utils.check_orpahned_doctypes + + - name: Show bench output + if: ${{ always() }} + run: | + cd ~/xhiveframework-bench + cat bench_start.log || true + cd logs + for f in ./*.log*; do + echo "Printing log: $f"; + cat $f + done + + faux-test: + name: Patch + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build != 'strawberry' }} + + steps: + - name: Pass skipped tests unconditionally + run: "echo Skipped" diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..e06d362 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,26 @@ +name: Pre-commit + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: precommit-xhiveframework-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +jobs: + linter: + name: 'precommit' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + cache: pip + - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml new file mode 100644 index 0000000..8266720 --- /dev/null +++ b/.github/workflows/publish-assets-develop.yml @@ -0,0 +1,45 @@ +name: 'XhiveFramework Assets' + +on: + workflow_dispatch: + push: + branches: [ develop ] + +jobs: + build-dev-and-publish: + name: 'Build and Publish Assets for Development' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + path: 'xhiveframework' + - uses: actions/setup-node@v3 + with: + node-version: 18 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Set up bench and build assets + run: | + npm install -g yarn + pip3 install -U xhiveframework-bench + bench -v init xhiveframework-bench --no-procfile --no-backups --skip-assets --skip-redis-config-generation --python $(which python) --xhiveframework-path $GITHUB_WORKSPACE/xhiveframework + cd xhiveframework-bench && bench build + + - name: Package assets + run: | + mkdir -p $GITHUB_WORKSPACE/build + tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./xhiveframework-bench/sites/assets/xhiveframework/dist + + - name: Publish assets to S3 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read + env: + AWS_S3_BUCKET: 'assets.xhiveframework.com' + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ASSETS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_ASSETS_SECRET_ACCESS_KEY }} + AWS_S3_ENDPOINT: 'http://s3.fr-par.scw.cloud' + AWS_REGION: 'fr-par' + SOURCE_DIR: '$GITHUB_WORKSPACE/build' diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml new file mode 100644 index 0000000..0693b81 --- /dev/null +++ b/.github/workflows/release_notes.yml @@ -0,0 +1,42 @@ +# This action: +# +# 1. Generates release notes using github API. +# 2. Strips unnecessary info like chore/style etc from notes. +# 3. Updates release info. + +# This action needs to be maintained on all branches that do releases. + +name: 'Release Notes' + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Tag of release like v13.0.0' + required: true + type: string + release: + types: [released] + +permissions: + contents: read + +jobs: + regen-notes: + name: 'Regenerate release notes' + runs-on: ubuntu-latest + + steps: + - name: Update notes + run: | + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/xhiveframework/xhiveframework/releases/generate-notes -f tag_name=$RELEASE_TAG \ + | jq -r '.body' \ + | sed -E '/^\* (chore|ci|test|docs|style)/d' \ + | sed -E 's/by @mergify //' + ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/xhiveframework/xhiveframework/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/xhiveframework/xhiveframework/releases/$RELEASE_ID -f body="$NEW_NOTES" + + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml new file mode 100644 index 0000000..3ee30ca --- /dev/null +++ b/.github/workflows/server-tests.yml @@ -0,0 +1,164 @@ +name: Server + +on: + pull_request: + workflow_dispatch: + schedule: + # Run everday at midnight UTC / 5:30 IST + - cron: "0 0 * * *" + +concurrency: + group: server-develop-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +permissions: + # Do not change this as GITHUB_TOKEN is being used by roulette + contents: read + +jobs: + checkrun: + name: Build Check + runs-on: ubuntu-latest + + outputs: + build: ${{ steps.check-build.outputs.build }} + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Check if build should be run + id: check-build + run: | + python "${GITHUB_WORKSPACE}/.github/helper/roulette.py" + env: + TYPE: "server" + PR_NUMBER: ${{ github.event.number }} + REPO_NAME: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test: + name: Unit Tests + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build == 'strawberry' }} + timeout-minutes: 30 + env: + NODE_ENV: "production" + + strategy: + fail-fast: false + + services: + mariadb: + image: mariadb:10.6 + env: + MARIADB_ROOT_PASSWORD: travis + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + postgres: + image: postgres:12.4 + env: + POSTGRES_PASSWORD: travis + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + smtp_server: + image: rnwood/smtp4dev + ports: + - 2525:25 + - 3000:80 + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Check for valid Python & Merge Conflicts + run: | + python -m compileall -q -f "${GITHUB_WORKSPACE}" + if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}" + then echo "Found merge conflicts" + exit 1 + fi + + - uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - name: Add to Hosts + run: | + echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Dependencies + run: | + bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: server + DB: 'mariadb' + + - name: Run Tests + run: bench --site test_site run-parallel-tests + working-directory: /home/runner/xhiveframework-bench + + - name: Show bench output + if: ${{ always() }} + run: | + cd ~/xhiveframework-bench + cat bench_start.log || true + cd logs + for f in ./*.log*; do + echo "Printing log: $f"; + cat $f + done + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ contains( github.event.pull_request.labels.*.name, 'debug-gha') }} + + # This is required because github still doesn't understand knowingly skipped tests + faux-test: + name: Unit Tests + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build != 'strawberry' }} + + steps: + - name: Pass skipped tests unconditionally + run: "echo Skipped" diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml new file mode 100644 index 0000000..dc0b555 --- /dev/null +++ b/.github/workflows/ui-tests.yml @@ -0,0 +1,162 @@ +name: UI + +on: + pull_request: + workflow_dispatch: + schedule: + # Run everday at midnight UTC / 5:30 IST + - cron: "0 0 * * *" + +concurrency: + group: ui-develop-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +permissions: + # Do not change this as GITHUB_TOKEN is being used by roulette + contents: read + +jobs: + checkrun: + name: Build Check + runs-on: ubuntu-latest + + outputs: + build: ${{ steps.check-build.outputs.build }} + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Check if build should be run + id: check-build + run: | + python "${GITHUB_WORKSPACE}/.github/helper/roulette.py" + env: + TYPE: "ui" + PR_NUMBER: ${{ github.event.number }} + REPO_NAME: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test: + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build == 'strawberry' && github.repository_owner == 'xhiveframework' }} + timeout-minutes: 60 + env: + NODE_ENV: "production" + + strategy: + fail-fast: false + matrix: + # Make sure you modify coverage submission file list if changing this + container: [1, 2, 3] + + name: UI Tests (Cypress) + + services: + mariadb: + image: mariadb:10.6 + env: + MARIADB_ROOT_PASSWORD: travis + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Check for valid Python & Merge Conflicts + run: | + python -m compileall -q -f "${GITHUB_WORKSPACE}" + if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}" + then echo "Found merge conflicts" + exit 1 + fi + + - uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - name: Add to Hosts + run: | + echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-ui- + + - name: Cache cypress binary + uses: actions/cache@v3 + with: + path: ~/.cache/Cypress + key: ${{ runner.os }}-cypress + + - name: Install Dependencies + run: | + bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh + bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + BEFORE: ${{ env.GITHUB_EVENT_PATH.before }} + AFTER: ${{ env.GITHUB_EVENT_PATH.after }} + TYPE: ui + DB: mariadb + + - name: Verify yarn.lock + run: | + cd ~/xhiveframework-bench/apps/xhiveframework + git diff --exit-code yarn.lock + + - name: Build + run: cd ~/xhiveframework-bench/ && bench build --apps xhiveframework + + - name: Site Setup + run: | + cd ~/xhiveframework-bench/ + bench --site test_site execute xhiveframework.utils.install.complete_setup_wizard + bench --site test_site execute xhiveframework.tests.ui_test_helpers.create_test_user + + - name: UI Tests + run: cd ~/xhiveframework-bench/ && bench --site test_site run-ui-tests xhiveframework --headless --parallel --ci-build-id $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT + env: + CYPRESS_RECORD_KEY: 4a48f41c-11b3-425b-aa88-c58048fa69eb + + - name: Show bench output + if: ${{ always() }} + run: cat ~/xhiveframework-bench/bench_start.log || true + + faux-test: + runs-on: ubuntu-latest + needs: checkrun + if: ${{ needs.checkrun.outputs.build != 'strawberry' && github.repository_owner == 'xhiveframework' }} + name: UI Tests (Cypress) + strategy: + matrix: + container: [1, 2, 3] + + steps: + - name: Pass skipped tests unconditionally + run: "echo Skipped" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f3ef717 --- /dev/null +++ b/.gitignore @@ -0,0 +1,196 @@ +*.pyc +*.py~ +*.comp.js +*.DS_Store +locale +.wnf-lang-status +*.swp +*.egg-info +dist/ +# build/ +xhiveframework/docs/current +xhiveframework/public/dist +.vscode +.vs +node_modules +.kdev4/ +*.kdev4 +*debug.log + +# Not Recommended, but will remove once webpack ready +package-lock.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +# build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +.cypress-coverage + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# cypress +cypress/screenshots +cypress/videos + +# JetBrains IDEs +.idea/ diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..d0df0ed --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,73 @@ +pull_request_rules: + - name: Auto-close PRs on stable branch + conditions: + - and: + - and: + - author!=surajshetty3416 + - author!=deepeshgarg007 + - author!=ankush + - author!=xhiveframework-pr-bot + - author!=mergify[bot] + - or: + - base=version-15 + - base=version-14 + - base=version-13 + - base=version-12 + actions: + close: + comment: + message: | + @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch. + https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Pull-Request-Checklist#which-branch + + - name: Automatic merge on CI success and review + conditions: + - label!=dont-merge + - label!=squash + - "#approved-reviews-by>=1" + actions: + merge: + method: merge + - name: Automatic squash on CI success and review + conditions: + - label!=dont-merge + - label=squash + - "#approved-reviews-by>=1" + actions: + merge: + method: squash + commit_message_template: | + {{ title }} (#{{ number }}) + + {{ body }} + + - name: backport to develop + conditions: + - label="backport develop" + actions: + backport: + branches: + - develop + assignees: + - "{{ author }}" + + - name: backport to version-13-hotfix + conditions: + - label="backport version-13-hotfix" + actions: + backport: + branches: + - version-13-hotfix + assignees: + - "{{ author }}" + + - name: backport to version-14-hotfix + conditions: + - label="backport version-14-hotfix" + actions: + backport: + branches: + - version-14-hotfix + assignees: + - "{{ author }}" + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1c6fb8c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,72 @@ +exclude: 'node_modules|.git' +default_stages: [commit] +fail_fast: false + + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + files: "xhiveframework.*" + exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" + - id: check-yaml + - id: no-commit-to-branch + args: ['--branch', 'develop'] + - id: check-merge-conflict + - id: check-ast + - id: check-json + - id: check-toml + - id: check-yaml + - id: debug-statements + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.0 + hooks: + - id: ruff + name: "Run ruff linter and apply fixes" + args: ["--fix"] + + - id: ruff-format + name: "Format Python code" + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier + types_or: [javascript, vue, scss] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + xhiveframework/public/dist/.*| + .*node_modules.*| + .*boilerplate.*| + xhiveframework/www/website_script.js| + xhiveframework/templates/includes/.*| + xhiveframework/public/js/lib/.*| + xhiveframework/website/doctype/website_theme/website_theme_template.scss + )$ + + + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.44.0 + hooks: + - id: eslint + types_or: [javascript] + args: ['--quiet'] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + xhiveframework/public/dist/.*| + cypress/.*| + .*node_modules.*| + .*boilerplate.*| + xhiveframework/www/website_script.js| + xhiveframework/templates/includes/.*| + xhiveframework/public/js/lib/.* + )$ + +ci: + autoupdate_schedule: weekly + skip: [] + submodules: false diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..2c4963f --- /dev/null +++ b/.releaserc @@ -0,0 +1,24 @@ +{ + "branches": ["version-15"], + "plugins": [ + "@semantic-release/commit-analyzer", { + "preset": "angular", + "releaseRules": [ + {"breaking": true, "release": false} + ] + }, + "@semantic-release/release-notes-generator", + [ + "@semantic-release/exec", { + "prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" xhiveframework/__init__.py' + } + ], + [ + "@semantic-release/git", { + "assets": ["xhiveframework/__init__.py"], + "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] +} diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 0000000..e69de29 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..09ffa29 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,6 @@ +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, + +* @xhiveframework/xhiveframework-review-team diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..179821e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@xhiveframework.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..057447b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2016-2021 XhiveFramework Technologies Pvt. Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c23928 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +
+

+
+ + + +

+

+ a web framework with "batteries included" +

+
+ it's pronounced - fra-pay +
+
+ +
+ + + + + + + + + + + + + + + + + + +
+ + +Full-stack web application framework that uses Python and MariaDB on the server side and a tightly integrated client side library. Built for [XhiveERP](https://xhiveerp.com). + +
+ + + + + Try in PWD + +
+ +> Login for the PWD site: (username: Administrator, password: admin) + +## Table of Contents +* [Installation](#installation) +* [Contributing](#contributing) +* [Resources](#resources) +* [License](#license) + +## Installation + +### Production +* [Managed Hosting on XhiveFramework Cloud](https://xhiveframeworkcloud.com/) +* [Easy install script using Docker images](https://lab.membtech.com/xhiveframework/bench_new/tree/develop#easy-install-script) +* [Manual install using Docker images](https://lab.membtech.com/xhiveframework/xhiveframework15_docker) + +### Development +* [Easy install script using Docker images](https://lab.membtech.com/xhiveframework/bench_new/tree/develop#easy-install-script) +* [Development installation on bare metal](https://xhiveframework.com/docs/user/en/installation) + + +## Contributing + +1. [Code of Conduct](CODE_OF_CONDUCT.md) +1. [Contribution Guidelines](https://lab.membtech.com/xhiveframework/xhiveerp/wiki/Contribution-Guidelines) +1. [Security Policy](SECURITY.md) +1. [Translations](https://translate.xhiveerp.com) + +## Resources + +1. [xhiveframework.com](https://xhiveframework.com) - Official documentation of the Xhive Framework. +1. [xhiveframework.school](https://xhiveframework.school) - Pick from the various courses by the maintainers or from the community. + +## License +This repository has been released under the [MIT License](LICENSE). + +By contributing to XhiveFramework, you agree that your contributions will be licensed under its MIT License. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..4fbd65e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +The XhiveFramework team and community take security issues in the Xhive Framework seriously. To report a security issue, please go through the information mentioned [here](https://xhiveframework.io/security). + +You can help us make XhiveFramework and consequently all XhiveFramework dependent apps like [XhiveERP](https://xhiveerp.com) more secure by following the [Reporting guidelines](https://xhiveerp.com/security). + +We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process. diff --git a/attributions.md b/attributions.md new file mode 100644 index 0000000..6eed6fa --- /dev/null +++ b/attributions.md @@ -0,0 +1,32 @@ +## 3rd-Party Software Report + +The following 3rd-party software packages may be used by or distributed with . + +- Bootstrap: MIT License, (c) Twitter Inc, +- JQuery: MIT License, (c) JQuery Foundation, +- FullCalendar - MIT License, (c) 2013 Adam Shaw, +- JSignature - MIT License, (c) 2012 Willow Systems Corp , (c) 2010 Brinley Ang +- PhotoSwipe - MIT License, (c) 2014-2015 Dmitry Semenov, +- Leaflet - (c) 2010-2016, Vladimir Agafonkin, (c) 2010-2011, CloudMade +- Leaflet.Locate - (c) 2016 Dominik Moritz +- Leaflet.draw - (c) 2012-2017, Jacob Toye, Jon West, Smartrak +- Leaflet.EasyButton - MIT License, (C) 2014 Daniel Montague +- Identicons - MIT License, (C) 2015, + +### Icon Fonts + +- Font Awesome - + - Font License: SIL OFL 1.1 () + - Code License: MIT () +- Octicons (c) GitHub Inc, + - Font License: SIL OFL 1.1 () + - Code License: MIT () +- Inter - SIL Open Font License, 1.1 (c) 2020 Rasmus Andersson () + +### IP Address Database + +- GeoIP: (c) 2014 MaxMind, + +--- + +Last updated: 4th July 2022 diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..3603dff --- /dev/null +++ b/codecov.yml @@ -0,0 +1,60 @@ +codecov: + require_ci_to_pass: yes + +coverage: + range: 60..90 + status: + project: + default: + target: auto + threshold: 0.5% + flags: + - server + patch: + default: + target: 85% + threshold: 0% + only_pulls: true + if_ci_failed: ignore + flags: + - server + +comment: + layout: "diff, flags" + require_changes: true + show_critical_paths: true + +flags: + server: + paths: + - "**/*.py" + carryforward: true + ui-tests: + paths: + - "**/*.js" + carryforward: true + server-ui: + paths: + - "**/*.py" + carryforward: true + +profiling: + critical_files_paths: + - /xhiveframework/api.py + - /xhiveframework/app.py + - /xhiveframework/auth.py + - /xhiveframework/boot.py + - /xhiveframework/client.py + - /xhiveframework/handler.py + - /xhiveframework/migrate.py + - /xhiveframework/sessions.py + - /xhiveframework/utils/* + - /xhiveframework/desk/reportview.py + - /xhiveframework/desk/form/* + - /xhiveframework/model/* + - /xhiveframework/core/doctype/doctype/* + - /xhiveframework/core/doctype/data_import/* + - /xhiveframework/core/doctype/user/* + - /xhiveframework/core/doctype/user/* + - /xhiveframework/query_builder/* + - /xhiveframework/database/* diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..09de8b8 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,25 @@ +module.exports = { + parserPreset: "conventional-changelog-conventionalcommits", + rules: { + "subject-empty": [2, "never"], + "type-case": [2, "always", "lower-case"], + "type-empty": [2, "never"], + "type-enum": [ + 2, + "always", + [ + "build", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test", + ], + ], + }, +}; diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 0000000..c273ca8 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,27 @@ +const { defineConfig } = require("cypress"); + +module.exports = defineConfig({ + projectId: "92odwv", + adminPassword: "admin", + testUser: "xhiveframework@example.com", + defaultCommandTimeout: 20000, + pageLoadTimeout: 15000, + video: true, + videoUploadOnPasses: false, + viewportHeight: 960, + viewportWidth: 1400, + retries: { + runMode: 2, + openMode: 2, + }, + e2e: { + // We've imported your old cypress plugins here. + // You may want to clean this up later by importing these. + setupNodeEvents(on, config) { + return require("./cypress/plugins/index.js")(on, config); + }, + testIsolation: false, + baseUrl: "http://test_site_ui:8000", + specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"], + }, +}); diff --git a/cypress/fixtures/child_table_doctype.js b/cypress/fixtures/child_table_doctype.js new file mode 100644 index 0000000..88a925a --- /dev/null +++ b/cypress/fixtures/child_table_doctype.js @@ -0,0 +1,30 @@ +export default { + name: "Child Table Doctype", + actions: [], + custom: 1, + autoname: "field:title", + creation: "2022-02-09 20:15:21.242213", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "title", + fieldtype: "Data", + in_list_view: 1, + label: "Title", + unique: 1, + }, + ], + links: [], + istable: 1, + modified: "2022-02-10 12:03:12.603763", + modified_by: "Administrator", + module: "Custom", + naming_rule: "By fieldname", + owner: "Administrator", + permissions: [], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/child_table_doctype_1.js b/cypress/fixtures/child_table_doctype_1.js new file mode 100644 index 0000000..abf8873 --- /dev/null +++ b/cypress/fixtures/child_table_doctype_1.js @@ -0,0 +1,59 @@ +export default { + name: "Child Table Doctype 1", + actions: [], + custom: 1, + autoname: "format: Test-{####}", + creation: "2022-02-09 20:15:21.242213", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "data", + fieldtype: "Data", + in_list_view: 1, + label: "Data", + }, + { + fieldname: "barcode", + fieldtype: "Barcode", + in_list_view: 1, + label: "Barcode", + }, + { + fieldname: "check", + fieldtype: "Check", + in_list_view: 1, + label: "Check", + }, + { + fieldname: "rating", + fieldtype: "Rating", + in_list_view: 1, + label: "Rating", + }, + { + fieldname: "duration", + fieldtype: "Duration", + in_list_view: 1, + label: "Duration", + }, + { + fieldname: "date", + fieldtype: "Date", + in_list_view: 1, + label: "Date", + }, + ], + links: [], + istable: 1, + modified: "2022-02-10 12:03:12.603763", + modified_by: "Administrator", + module: "Custom", + naming_rule: "By fieldname", + owner: "Administrator", + permissions: [], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/custom_submittable_doctype.js b/cypress/fixtures/custom_submittable_doctype.js new file mode 100644 index 0000000..30aa698 --- /dev/null +++ b/cypress/fixtures/custom_submittable_doctype.js @@ -0,0 +1,53 @@ +export default { + name: "Custom Submittable DocType", + custom: 1, + actions: [], + is_submittable: 1, + creation: "2019-12-10 06:29:07.215072", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "enabled", + fieldtype: "Check", + label: "Enabled", + allow_on_submit: 1, + reqd: 1, + }, + { + fieldname: "title", + fieldtype: "Data", + label: "title", + reqd: 1, + }, + { + fieldname: "description", + fieldtype: "Text Editor", + label: "Description", + }, + ], + links: [], + modified: "2019-12-10 14:40:53.127615", + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + submit: 1, + cancel: 1, + }, + ], + quick_entry: 1, + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/data_field_validation_doctype.js b/cypress/fixtures/data_field_validation_doctype.js new file mode 100644 index 0000000..5f73b52 --- /dev/null +++ b/cypress/fixtures/data_field_validation_doctype.js @@ -0,0 +1,65 @@ +export default { + name: "Validation Test", + custom: 1, + actions: [], + creation: "2019-03-15 06:29:07.215072", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "email", + fieldtype: "Data", + label: "Email", + options: "Email", + }, + { + fieldname: "URL", + fieldtype: "Data", + label: "URL", + options: "URL", + }, + { + fieldname: "Phone", + fieldtype: "Data", + label: "Phone", + options: "Phone", + }, + { + fieldname: "person_name", + fieldtype: "Data", + label: "Person Name", + options: "Name", + }, + { + fieldname: "read_only_url", + fieldtype: "Data", + label: "Read Only URL", + options: "URL", + read_only: "1", + default: "https://xhiveframework.io", + }, + ], + issingle: 1, + links: [], + modified: "2021-04-19 14:40:53.127615", + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + quick_entry: 1, + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/datetime_doctype.js b/cypress/fixtures/datetime_doctype.js new file mode 100644 index 0000000..f1a77ba --- /dev/null +++ b/cypress/fixtures/datetime_doctype.js @@ -0,0 +1,48 @@ +export default { + name: "DateTime Test", + custom: 1, + actions: [], + creation: "2019-03-15 06:29:07.215072", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "date", + fieldtype: "Date", + label: "Date", + }, + { + fieldname: "time", + fieldtype: "Time", + label: "Time", + }, + { + fieldname: "datetime", + fieldtype: "Datetime", + label: "Datetime", + }, + ], + issingle: 1, + links: [], + modified: "2019-12-09 14:40:53.127615", + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + quick_entry: 1, + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/doctype_to_link.js b/cypress/fixtures/doctype_to_link.js new file mode 100644 index 0000000..ff5d1b5 --- /dev/null +++ b/cypress/fixtures/doctype_to_link.js @@ -0,0 +1,45 @@ +export default { + name: "Doctype to Link", + actions: [], + custom: 1, + naming_rule: "By fieldname", + autoname: "field:title", + creation: "2022-02-09 20:15:21.242213", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "title", + fieldtype: "Data", + label: "Title", + unique: 1, + }, + ], + links: [ + { + group: "Child Doctype", + link_doctype: "Doctype With Child Table", + link_fieldname: "title", + }, + ], + modified: "2022-02-10 12:03:12.603763", + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/doctype_with_child_table.js b/cypress/fixtures/doctype_with_child_table.js new file mode 100644 index 0000000..7caba51 --- /dev/null +++ b/cypress/fixtures/doctype_with_child_table.js @@ -0,0 +1,52 @@ +export default { + name: "Doctype With Child Table", + actions: [], + custom: 1, + autoname: "field:title", + creation: "2022-02-09 20:15:21.242213", + doctype: "DocType", + editable_grid: 1, + engine: "InnoDB", + fields: [ + { + fieldname: "title", + fieldtype: "Data", + label: "Title", + unique: 1, + }, + { + fieldname: "child_table", + fieldtype: "Table", + label: "Child Table", + options: "Child Table Doctype", + reqd: 1, + }, + { + fieldname: "child_table_1", + fieldtype: "Table", + label: "Child Table 1", + options: "Child Table Doctype 1", + }, + ], + links: [], + modified: "2022-02-10 12:03:12.603763", + modified_by: "Administrator", + module: "Custom", + naming_rule: "By fieldname", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/doctype_with_phone.js b/cypress/fixtures/doctype_with_phone.js new file mode 100644 index 0000000..06a24a5 --- /dev/null +++ b/cypress/fixtures/doctype_with_phone.js @@ -0,0 +1,46 @@ +export default { + name: "Doctype With Phone", + actions: [], + custom: 1, + is_submittable: 1, + autoname: "field:title", + creation: "2022-03-30 06:29:07.215072", + doctype: "DocType", + engine: "InnoDB", + fields: [ + { + fieldname: "title", + fieldtype: "Data", + label: "title", + unique: 1, + }, + { + fieldname: "phone", + fieldtype: "Phone", + label: "Phone", + }, + ], + links: [], + modified: "2019-03-30 14:40:53.127615", + modified_by: "Administrator", + naming_rule: "By fieldname", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + submit: 1, + cancel: 1, + }, + ], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/doctype_with_tab_break.js b/cypress/fixtures/doctype_with_tab_break.js new file mode 100644 index 0000000..44d6c16 --- /dev/null +++ b/cypress/fixtures/doctype_with_tab_break.js @@ -0,0 +1,54 @@ +export default { + name: "Form With Tab Break", + custom: 1, + actions: [], + doctype: "DocType", + engine: "InnoDB", + fields: [ + { + fieldname: "username", + fieldtype: "Data", + label: "Name", + options: "Name", + }, + { + fieldname: "tab", + fieldtype: "Tab Break", + label: "Tab 2", + }, + { + fieldname: "Phone", + fieldtype: "Data", + label: "Phone", + options: "Phone", + reqd: 1, + }, + ], + links: [ + { + group: "Profile", + link_doctype: "Contact", + link_fieldname: "user", + }, + ], + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + quick_entry: 1, + autoname: "format: Test-{####}", + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000..da18d93 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/fixtures/form_builder_doctype.js b/cypress/fixtures/form_builder_doctype.js new file mode 100644 index 0000000..08b598f --- /dev/null +++ b/cypress/fixtures/form_builder_doctype.js @@ -0,0 +1,65 @@ +export default { + name: "Form Builder Doctype", + custom: 1, + actions: [], + doctype: "DocType", + engine: "InnoDB", + fields: [ + { + fieldname: "data3", + fieldtype: "Data", + label: "Data 3", + }, + { + fieldname: "tab", + fieldtype: "Tab Break", + label: "Tab 2", + }, + { + fieldname: "data", + fieldtype: "Data", + label: "Data", + }, + { + fieldname: "check", + fieldtype: "Check", + label: "Check", + }, + { + fieldname: "column_1", + fieldtype: "Column Break", + }, + { + fieldname: "data1", + fieldtype: "Data", + label: "Data 1", + }, + { + fieldname: "section_1", + fieldtype: "Section Break", + }, + { + fieldname: "data2", + fieldtype: "Data", + label: "Data 2", + }, + ], + modified_by: "Administrator", + module: "Custom", + owner: "Administrator", + permissions: [ + { + create: 1, + delete: 1, + email: 1, + print: 1, + read: 1, + role: "System Manager", + share: 1, + write: 1, + }, + ], + sort_field: "modified", + sort_order: "ASC", + track_changes: 1, +}; diff --git a/cypress/fixtures/sample_attachments/attachment-1.jpg b/cypress/fixtures/sample_attachments/attachment-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..be6e4f09918778c9cb2c1361839aab94cdac977a GIT binary patch literal 19986 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�KM!Ixgjo*?|riY4ZJfE}x) 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≫o-b7(QDReAd*kRHe#S<~gDjzp%1M@C9eu2%B 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%0At3NJ3+ivCOI|joQZd}R67(}6K8W&aQ zf=h=4*r{=>M~&4Bc*iD78t7f*5$1si$x4WdLGW*Gtx@3_;d)G^t%Jhoz1b2@w2WNu zedRHA!|IWH-S>KQJF2|uxkrQ^_uWe$hRIeK^rXcF52E*amKj{L$mc?OeO@nkUiZDu z20H~qrCBir@pdjbL0HOjeL0+RiYAWI^Ro<7pDBGOJ3R9!ibF(MDvmO!%(@2LSa9HY z#_zuz4>}W)E_=;s!z&%1peE-GSw;taTPsYAWDAn(R%K66T+`E|ImYdu`lx8KT$$jK z$s06cM)Kaswal{8E>LSCmC9HR^ZFKp1uG^f9

1*agu`l|;~Y!gi50#(Cd&vQ_Qz z^8Mj>$p-?*;q-jqmPfeYnB%#@bG=a;hs(y~s7CO!1U`YH5~=h~u45lKK8w8T1z^St z823lFq5HxRec<>3tmB$hQ#K^NE@uN&6O3oKyP3zRp=lj8I55NWmj4OB^CCz89Qj zjgeprp&RS#bY!o4zA@!sSqMBG?*=|IydX5r^S$?k(GQ#!sYvne&Dqd%K z!FRpzo8d6l38bXwhvi)W{qK9h;3H5={Zh6ZReoFxPCT4lV&0Al4LXFTw7l5U4bL?O zl$;!K)FmsTY;)8bRpv|b-&kpoJcD;#tJNC>$`?i4u;U2F$6wQPO!hq{`=6d>bBJG) zF0*DUTFp6&I9xN{RBGpn;Yp<{;paaokDRPWHymSw7gaqnB{D(i*@2dJ8d0ZMF}UA* z>07)(q+AlI(>z$eciecDx5U%;-SFHW$YM-CcfIeNBQLF;QO^s$_eBqORwZIIqeL!q zoNHs0BlDVrdJ%i0_qiUeBkq<6UEW>$(2v~bJ>vOW+)9-xk7FD1-B@wNV+#uRaX7^p z?x)goLrgb1RT@%ga)W4ES47y#6d6|LYue#hdC)9-cU`VWode33J=ox2)8n;Qu|XF%Y<9W>BYEt7G-l`Te>PDtKc^=()HeJ!5$XIkNFoiH)psL!#sK zpy+7^nPNwxz2K!!aRz^QN6*@#L=U|4l-aT*pPcBrqF|Ag5V_v_!gP$hw(#)>q7R0u zCI{kGnm>DjGoc|=Q1x7JJkz=E_uU_xE2!Ej^O~oZ(>fD9!1JXC(dLU)YLyYpYkpg6 z4K$2fV_%BSIQ;WdX-ToB8J%j4Cp9%e)EzUS>|@lKSY|uYMt?)2LkrXZMp=#>L z0@UOjAp5-Nk7a6;qEMh*34-vC%8WFUUWg;AnJnnE(@ddW%!d6h#)k5=!9%po<-9ah+_!H;R~H+ucOK*-CXqU zZ-OxRM00`XG3KAL)u}!$kF*V-w9^t|`!4u!G{f_SW2L4F3^O)X+B#}tgQyB`MA*hC zI47Ah{CgyBD5yd6w?Gtzo7Uk5AAO1I?K8RlE(8MGsmb~MJ5conL zdWIbtW1YCDb$eDYi~}Sd2R%Qo@=l?#6&|J~k*~W&S*D@J7Ip66X@_R&+e*vNFwE0j zX&ITPouBEw7i1W5wZ>q)t`*7*g5*o0>0WX5?pwQVm4^(xcI$pgj5j(#FRA#Na@D(M zD1*|M9H{ClTR1kedXxej@Z^AgHG5AETFNOHV?voAjEYe22q%*#SPD-WXhtH%^isJT zF@u61Eav!XFMGl=$x9Jz0Q?^IW^;6OJP`aZ8u-GkoFW&!(R6Fr4pxalo(RRkbU{&G zJ3c2?q(^X}d!~dzV1mY^(-IusmKbkA!uih^$?A&NDe5`-%2dPhE&l*$DmcdcV`g72 zCK_&UuJnw&<4nxe{*afNpzQTd==&7Y&8;%M{cyM(WD_IYw_4+muJZNSbu2h(F~qL> zc{jNU`u2W0n}l5wQ!`AC*ES6wp1Z^n$ri-cITB)J4=iLJ^&BotIIpKo(LqXHAjA%6 z<6ou`)?{8R4vt*?3u6fSD>1$phobjIW}}HUlrM08n5%D(C+AJ~N7*EGV<-&pMd6RG z4UO+D<{C1DA^Y7C&_iWSXD{l*O5i)wJ=WMnVEOqAon6f523SSlm&uyN6`ont6XgmN z!!kaAvy~ciW-+xdMW>iJ3OU5TIUZ4d??>eEn9~g9!>*4f_&(tM zz!n*Tg^Ft2v!(2XXP*qn`U=ieagCV9*1VUVGjl`}MoiBz%)K2%y7#yLUN zmRY}U2LsH3WP6tFT56itFWwhgvxgGNmp#CFwPhcA&d-95h7>j4L6#=uGHt3DSR2>mpAgEIJC9iZI^NAB=^vxtbZ zm%(?#l)g8`(S*Sd-5iOfLn2=I?{lLV-s5gfz%YInJ<5jX zgla=4Oy=&hpiZb>2mn$W4N{dgUSiVrB7@C^&os=>2GTOa%r`GW zv~;}VjnB1Cg0RmyO=p&Gi-mHdAUPuFx|W=MZI|y0wNZvvS;G9Xj2>Z8cDyLea=S&! z1zX#$tT9Q0yj0gh$Sq;$f@;QdW>DtodGQZe4KY7W9AG8(!~_VDIoR-c=Y%QKoDHYK z?`=lNLw`r^ip?q<&R=>Fjzu9w6+_X#Ffm^EccoB1);0nmP&VHozRd#FBb7ey^50kV-j-^4Y_{!e9 zmz-ep95+bHlo&j>4$*TnOi`cf3;uj^vug~!dE72446m|P)pe{m`rj~Kmr19Nfv&IF zh}&|NwP>FXr>+SF0_?MTG6XghsL;py;GE2$;>gfWH^AVCRm?mDqzKS%Wi|0 z=c@t(8oBQ9et(ppiJ|YjGkhnWajgJ*;S1gvpIc5w zi=!X7F?+t~;X{`Q@1G@zqHX^1#hc-}s3j9o ze({|byeG7xB^g{Z-toQ=xNLorVw{CafG~@qFqEEDo(hIQ^izw1vGg4PIl}C^t2XLc zvXuu^G!B@VC>Y?mQ$kq>s5zG$To+o?j2>N$=(#!P4=qGAY{b)!&owTKvClZl&oJIr z^3?}dc|z;DmKbe4NN5`*XCTOSrMRG3Zf|HRh1OR4%XKYkJhU=Mma>?qz((>0cUPEU zmJAW9z)utHpA?Mx$w3L>w#-r)-pi!b^EsmJppFED&Usy@9)=_DMbm90Fs7ahf)`$< zZIM>c2wgB;T<)2@oILTK=z=FffG7zUec|*b3xfnAT5?0whoR}7==?4d8i%<`i*ax~ zLg2Una5fsck2C6f1wrT2bY7H~DKN_a04->16+)9(vE{{dEI7k5 z9Cmf9G*yangDV`pdfW>X8C~T2y6RS8+4=>cWSzX^?=UCO#eURM~9jghBiqc|+`Aw||i*!?mdiA(|9kR;>NG|!p&y2%U z_(JiPn|vv#A{TwvY04OTV0s}KFDsF^BxO(IJKprOl{z3^&Xc3(yY7r#;m~KtmeUms zVFyMo`@I7sD9P|lT(^aXp656j>a{f81*T`iJnCAK(-O4?nO^0+6Jj2u)*O3qT`JTV zW>;d-vNO#$D@L?Dz~fZsN~1+t2cBhFnD2{qQRh~fQt!H!8g5(_`21nn{f6&#QJN%H zTeJo#XD)`bPL)HPUDI&2E2GYC(RVlaA=W)=g3@D)pKDjly|*FpK{?rV{Cr}t%zz>o zjJIOYq6N{qSg}}v0ucChsMQK>0iN%?=U_EN+e5+hM)5?gwJQle@WtN%N64ET7?SuV z^S<%E50%6Wl{ED8yW)mF8Si(fV+2T*kh#O531Z*_$j?ANLz1uFo;f*_PB#sJZDVsjhM72 zVo%Cq|+~pH|&qrOk%9~KCP;*k3XxjNq9oj(x za-LQRE_fRw?Dn-nW>Q9}V<6URqI_E{DDsMbQW;;781m zs#LRq;e7%n6poyGP^{`co6_=iDh#0OJs~AfakcJS(6&kH4UOa5uH{gm(d5K>Dne?5 zGMsxv%+EO7?^$2=A%~k$XFKNEP@u|>J?INC!)sbMK+Sq~eC@!p6H{DI?R9;Q=^AI1 z`KdysjK(}imCImn5<2njXIO^ht=nZ1s@*h>Z&mnH)iu#VUuwXj-ZRpfrF$LmogBJT z2aS8Z5S=Wj)Iq#m=)31j0k$C29}A~&U7EKM97qwF?{r`sp$eAP;=AtmzVC(MuY|}| zHi+MFUEug5ebEsILpShLt{FZo;(!{Zzev9=P<0hjX;})T2IrdlN=Q^3Wp|f!HI{kC z#j*|GaVR|DRgz-?#t=5eD@))Zws1lpdS|An35}umNA8Qf6p(?S zY$$x;g7&pYxtnNo$cGrqUJ#59|U2?Q*i1CJ4Dl4RhkG8`#ew(8Z?N-Pl4jti+VO z=bSvMmim*JdD+sp&{N}1HkY8U%T(hLWII~mP;DN^c&}AQw%P7A>ur#xzI>21pmFks zw;(r48d~Soyufw!sLNkkCP7qNwaXdz6>7vORLZbGGc9HqDMs~8w+@98G4)Ng7KS6~ zo*3sZFsR6SXMNE~<}9$*{{RfYozRYGN;unixIYWM7gxR}419p_UGInQcdFIKWC)RX zeeZqFh(ZrWd=a)(q$*aPGWc3j?3F7H$S;}|%=6C8QC$-9p+T9Mhex}pH0rxH=*jwn z&W2=r^4&P|MUI!~IT4Bll#}0bV@*?>tLy%FX@}CZeu1%0G9Q|6TMR+V_I;3Q48e2C z15VkGkz|sBnpGU&#JJXpYg26Mww$4Z+)!A3&m4-q*!7cp<%w1$<%F)eOlqrY3lZ^y zd$H@-zYHH!WgFJnJoCMd-LL2K&-P6X{UbEx8NLU8Pt7pe2D88C2ANG( zchsw}dV=qKQ9yduaH`)XNFtJTY->968*o=1nKC1q*0xM;BX-rX``)@`nZMTwrtej- zeQie|zBT^Btyl(V<}!NSkERH6sf-3XS)=I3XcGftA9^8ovl$F-EQg{qgEQTkffQv2 zcyE2vIm6~HVqiob@ZN*IAGl2)tNJV`AjEt$IJl15`w)$lnBi%GqHj4gJFP8f-nRyNcby4a|_ za@A?e7Mt8h3-W<&bW6k{UN8xkc_kAaeC~{&4ecyY%7e(HP4Uu&daHTZT zQl|l?W-TpfxF}QHeKoxtk1*V(pFujFpm{xY{O)na)U*bOo+wak6DnVJp{ATHg6W_h z&NtaK+wwwUgL70HLQf4ec^U5XcQqDQwk-Zf=)0;wqK$O0i>zl4kz-y^h_}e%=ZJLr zG;{rFbX9J%I+j^9t$}bsqRMU>>O3iI!mn*Gc7h7^G3Fa?#-K@z5c_DCUN#sZ2*vh% zauC>Hh0*)7_eiW+H%2cV-U@I_Y;nK7H^bol!A{x~#@jv4kG%JV?uJOlTgw4L@C@&T z-V5Cq4}~R`J2!~I6km%Bnnux^+Mrei8pKKU>IS|Yjd?DECA`zlQKJ?!N$VVUk&)8Uv|T=6#y7bNMs5mL1UW2++UUU8$z3N#yu$_&Vl zIsw$vji)Hh7dcXSkQ}Fd+Gm`gISJQrmHO9p?*s2(TlcuaMwaQ}oR-#^DUlcwX;) zIYKz&aUKXtIf_LONi0Q3cz|YuvA=XNr`mPme6i zsam5ukzFtM6Al(bkYCP*oNYHo=*hy9jDTgE-qtkL0LQQQ!{NsVBe>sh#~x;AjObZu zqpK-v-e77B$#2*mp(IuT&~{nyA+ojX3)C%+y_3htC8t`?;)~tu3(l%-yXu5ine)l-4LmPEM+F1N9cfI!o;rqgf`e32AgZBv}yJ2F= zzcJ~_(v=F5W1C{+T5+Do@*DAahKnD`ug61VtPdo<8w zC)?%l@sBKe{{WDfW^GS)yq#0Ru2*=~HE=ohp0QmakIsyW&UT8wmRsZw*QxD#?qb^< zv}zeV+N@J>)+|rfMOf5i_3p4htjl4N5ZZ8!xnYn%^x*c#c&?*o_|bc|rz_?#jL#3f zS8FuE8G2!!@QnfX3KkgRZx|za&i88!QE= zec{HdwwPeVZ#bM~8;s!)4&Hn{E;N1R?ZncnUSMpinS zUk+xTIzpS#vqRJyT;TQhW2sf{?4Z>70#$mw9%@{bWL#^^ist*WbYvQ>x80%e z3sqcUvSN_~phTVOJ0T~_uuKYVG3_8tr5Oh2cgegSkD-LcE!}m=Pd1p4o6$A|%T7{zPCB~n zv(%cO99S2MqXaB>Hoe?VILa=f(X%7YKDErZ&7yI~r^k}J(cJS=WBFz9@SxTlv2*4cat;hmccrPv81F|Q*}(PMjYDi()YoQvPm11#4H-N;Hq3KDyXZfvX4C4bt@{# z%$BZaG~0OvbdSag=47p#!gz-}nOCt;o8FJJseO45OsvHN;5#*wL}BT=X`UFx!z{MA zw0yA$LKnN>C7i7;I?|uqH{I`j(1X&1M7c-OmJ@s-a6<1p&UJKT5@TA34*&wGDc$tu zto78DHKt^zC{WFguPb2X2c*X~4cxH9%Eyzt)xgutp{m^xFf{Y)Ov`@S2ON4#b2Gf2 z4mBP`m0u3(2VcTB64) z10|l+Mc57%XbiWSYR-+y=&zxLc*mQ50|ZWJMcaK}$=GamIf^lj7I{1y(jiNi< z5}TQlyf*|^MuXSl^beL&=??#p6_@Ur)F1H zR>C|XneTYd6h8BcOOLq8nh_Pi5=GorFto0<>XfUXYi%DPJoOR0iG5m14>`LW+7}Yj zk4=uOTfM~N$Db>`5iK=A)oyFh7HP(uha1>8bFs!ZWLq}mT7Ponw&Gko_~B|Qt>dj9wRg&hxzL_{y0xb9!KelCCwW@omF$u2P|1c7y2W zvyM{~$yy4`JoB3Iy{U7lIP|#c$h+5?q|EbO=*dEp&Z;ww{{UtlX{a%^iFUi3WPc`I zc9Nr-^99Rb9M_$#`8k>9)sVv5CjyGS_k}$&Ntk3zGrH2%uV*m{Tt;D5*y-Ss>1{?; zmb#??E~Xl^;W}NF>;NJX%hDHBo8Nb~gpRm}^aYILta*$U2w;K{bbUJHZ?9u#Dfdkn z$@HFC9fr4cL7eD>9|RL+$zyFq2m>r88NqwSsG3LITUnEMeGt4M@IB~3*Z9E_9L|h9ux7Oz7rWWDl~_Slhu!7$*;%pl;|eUhy>)jF zQdoP#OdF-Q9)Yx-(ez*mrpzCs5Z3b)8{c=K;C(i5M;B~PKXhH^gjWJdwiI>63mR`2 z0rW!fx!{bW9$1^6Tv<{SSq0jv)Gukqx9y;@L925!28ehxs(=~bF2$Q$`l@DQJpjemT{)FV|z00b2!?LpwLodkIv5)(vVc6*g) zDR^i!#fPXM5jxCHivUiZ0k! zIzMy*5ov-X)_i?pW)#8bL}-W3^M(hWS{VDA`x+GFfSH`3Vug|XgMZno%uus1HE363 zjXndHw0(ei=P+aome*EthRk_tU29D|{NE4IQghEbq|Z7A$T-s)vDKS)xf*75M|v+k z4s5w5N-5JP$|<>omK-S>J_*5{`MysKvLj2DFwG&-h|{fYY{p=AGrdqn(HO5x1@Q!~ zN5^a4v#(7nsjf4-TciiB;z!KSNF{&kvCjrGi;Jx1aoZv+5A5i?#i4jE;N$Pnj z#VR9N6Gm8yk2O|TBSJZvYKrePX$j$nD39bDrqooR*1UIYT}zET+|?7%(qqm(o}hFE zj&X*y3!=Nl~bjqxVZ%<%ircftr0wOY+;IU7a}fbk2$@H_7W zut}p`d`yA3f=Owvbqs-8g)t1Jq$euQIYv=Yf4LfQ)wXca)02-rHIXjcNm-{fs zUSfl@G+RQ-4>oYk%8n8zR*4&2 z(i1RM9g-ggvMWm_hq4yc(rY%4Pi4GO4t}+rv-6J&znLneod9mvDvf}^fDSqy8(TMn z!7L@|0xMXGzH^`mN>!lRYvxqOi@xce;r9Dgdcd7pg8*-R;B}7NP=Y4doWU9HfPU`) z-p^`>oQ@<1yl=W8bAkwfN_lK>htpbH6B0%L0GT2hZRt{#5RAE{W{OTc+>ZHsxEg8o zhIi6aQ_eaol5XowNyf8e8-n0mamVHO_Jxz4YKm5g(G~&bnGVZ0&5n81ZBu?dnGmaO z_gJlAYOSs$vIt|Vhf%9TC2g3*UgQjiJVamGJ5gwQzBO+!*B#K6L;=8BGPkCmLhGQa zXaGgSG?Y51C?xQ!D$cKK@`~U~E<>OqmD@4A2tWdfmEIAk3m*(0d*OGk_JafhJc-#^ zgBW?h3ORL{*AQ<`c!nVN-sk|-&1g7~DB3W1v-~3qAG^*4DtSwCCucS`!~~|kR%LdL zB=D&9RrY4m^7Te29zv7ei_S4kSD*BR%;Qu9!SdQvZ3Qozj)JjdadfiQ0k|HWN?8kRrJccIw9OAN+X|UcYf^^a znei01-^`9+np>IfH1u7WJEYhJ&Q5Lm*1Hj9k{tr7vWRrBgtDz$G7ADn-j-hgLQ7)I zgTYxkq~VLg@Fo*T^a-TpARkp5;*__=@A8_WZKRrEp6@;H0H%1|sU=2iVIe`_$I*a7 z4}-)hpB;JueKot+oN4tH6{F^6oP0kg-nL?c z#>zA87Y-jXuIy~le@E-99$%f8-Kg_)(2Li((C0$*Om>?O3+3kPQ%8bON?oPlwWB#@ z^peTZ?iKV9ekib(T12`f{=J8g0*g5AhlN>l2wisE;Jo7rAO{=Ph|ZQo&l7F|qBFke zd%{4;5zs48iZqEoxIUOK2tE(EfeP7#bWg1)8xy3t7pzl{-n0{%qm))z*GtSyIYBt` zeRVH5!|CcvNK4HzL@O%Z)>?zij%1s*)^Iq}^2<(vksf)JO+}&SM;K;SeYxC6S=YCN znY4VaT8^Bw7QiM3wJ2M~B}vc*P$CId*u?t5Q0i>(n?Tj|!~T$^YN-;AVyP&iph(+5_duA&_gph}zr0&B(g5duiL zQjUwfOx}?@4y0{jil8k=N8b0I;b|F|G%;xmjkhKb4KQ5@!`=iaTaj8Zu{}4pv!x={ za9p_DaVx)ERGy-(pfuE+<4#YGtecr}rk_kx8a`TiheC3_^<=3u$ks`_ZKcMVZdFB~ zAC#PJ4OVE0ih~hkyK>4>W^kNX${~PUDF2MH>|x}o)>Va1YJ zWT!(a(lPqdHh+81c`_tObr&EoVwbBoz881|tO>N6Qv_kOMhMRPp7)7wVpDZ_0kM|! z;R&M`dC?cZ7KyeWd@>f`Q(Hm;B$JmViv^CR*RzS{sH!NfFC;YMk>jgQ;#hg6(lsWH zm7HPl9J_v4>P|K>lWyBxd4?U73%_5K9AW4Fh1PJlL*Iy)8v&nmn3i^!Rz^<|6Nx zbp}xkS6i91Yo(HPKg%gSYhZRUBurQMC!kpyK(1IP8J zhybhYJolutZLV%#zHKby9=_%Ik`p?(YzhLiI$LqiXb5YOeXuU-UJ7wUmJ9MrPu8x( zD6kPUzv8vDbi&C2M5iR}W{$Rj99KYCNlxC5;I~*S2-D9_@WbI2ostWz-PxinEvifj z;_tW+CuoT2Zl2C;b*U%0)x97i@Jr)zDC32?LkOLi&@!RFDm>w=Ws26SPmL|>bz+m0 zP>>B6`e~{dG8NluDm2q`yeCA+&oJuOI_Md?lZ}UEYv#{XalOdS7vs`{kz}iG(>(aT zbiAbsX_b~C)U>8m6!e-j>Z6IILle>NsM(!j%!=eRKBJ@P&4|rFi&eujTYTe8F=w{2 zqtZJN2>^;g7^|jAhz=F z+0;(8MBqV!?>Z9FL{;eJjS)692dFIm`=RGs8Iousk7znJ){}+!z8Ii<8Dt=DiC8o8 zbe#snZKIVsX7)`!C&Y(_0SVc>$9x*l=sF&o#b6;jdt@5-ksyx}?%0kzhUQ$diKulPjatPmnDXwll&C!X z-5$|Sr<|fIlP_y6QN|B6$Mie#=ATkfx)x}9gJ9Wu{{XU&JkaDv`_1J^)h%Iuy_{BD zwdN}6=~R=n&6KhiC{8aPOo`0*Gk}%G7^VemXFKjIM_IQnbBSnKhG~Q>(Nmf-yh!JM zSoB3s=HbcB{{T$X58MiJuZe{%SUsLl2r1`LMc|NrmPiRLIRt9gI{vH|0$2(uGE@$l z)f^(PLZwQfhruMKh;0vg>XsJB@oUw4f){z>+hEsJ)P;=wjx-_{ym!6cg#eNe+XU1K z8Q4OtGgAU9Wped->bujg>lG<#j=O~8dd*a1ZaL1WjbOYh%9Z^vl z7G|XJz;iBxu~Z&lapdCYby99@bh#A{w}4wNk(%nya;;uuaW{b;mC?FD`lV`=?Wx`= zYd$Yh>b&b#>&6WDi)6ubtbO-i#Hfb$84kB-qN`aGhcAhiZf0`fB=c3W&{3z61fq+y zu6R{a#Nbgy7E*Fub&Ew|0Wcf^WR#UUY0i{ou4P7NFiKPowC zf+QUwt^uc)CsfhM^HMJR#t)(4Ny|=^sxzkq$kdd2_9%XTxbVY)o<5FZRKHP9QK2W%< zBh1bd$$9zi(UV3QChTXPdI$Em*Y(P{Sy8vl@2GQhzQfE8y1Ig^rJ9a_sG2OTV@J}$ zW3#nQOHDxLYDKIg`FZTZ$|hf;>)iM!A@H&a(h>^Zx+ua6D3S{z(Y@n!9DkqyNGznO z6W(^@ZojLSEi~U3;#0OmTJp!$sjNa8(j_D4x>4pBg^8m^C`z%Lk><1=7lvRt?ve)N zap7n|GPkyQTLU{pV)b-d;IA<;=|`0cRYS6_i=G;w<8qs&?&Nu<*ZHGBPZXSKxnx(X z+LKPDDZLLYPk1?YrT=rs>5ULXUk`5VI+N-N%R#0nc+Hw@HZIB>)e_RvU z*SABPXF0!6LG#9hpJw>w`!UUN_~x6mv(0!0O*?4Na0r~&(`^@9<=NLuD66i-sHpcf z=Jh#8YiQkV5#eP4Ib@VZkeb4(B*m596hR5mq}H$S$Q@+#dB%7gMwOARi#C08Q4WYl zfb40*_ew|vO%GRbiIue!z|@Pu@!jXU!ob+w6PVn>WK6~9IO|g-194tU)-O|))9A;A zDwIL;{*0U)asA2?8`W{9om}Sa2RTx4re#$(ySjtTqpE!iDLF~CO$(wYW+*ZpnlH)1 zgD4=}D$mceDQZei;9CKaE^bob*(FH)_@MqaGE8$-{{UG)bq;f?cD;W^m9Xoo0W@cI zv1bO9P5XJPKF8zJA6}LvA4AxHC&g+a==!DYX#7!g`}Pl9vVa5Di|lQq@<~fY9Yzj&PIB3x4VPBYZ7q`_>bBhNUr);b9(43B zfQdS+H^mox&ue=54}eGrAvk8*Rf4gv)u#%71t~<4HPa0h{c#bH9ALOoWXPLa;ER5S zB!EX+_@{?+hHzyegy^?(60B(@iJ>P#6Mf+Or6+G^uC*J$nY$Y)!Z?M3??H$!tATpO zin1JoK)9$<9i%48 z4<2wb=U!|ArM%sY%y#?4O_IZDXptZ}dO1%XoYn!UGV0CI?>K9Eo`=yg{PP&n@431* zP2q4*6`vE{cU2Qsw-qgvX=>j-vCYT_EsI*W&+RV@gdhV~f~kdsvcOR!5>4@F#cC!a z3h_jiKoX(8?YQ-KTQa>U5V(;b8`~4n`kPX>S4@p&7^FiO3IJMT8ht?1ENw=@(wD&T zz303SHlSsMYV*3o6D>2mK_FSn^)ny5!B*&g|T#aMgiqsr! zKSk)-amq~gMU!4Gstp!*hUh5?icY5~tr;#n@MQ}^%1~x-n^-Dp1lAX-TRlulM4U^y z_O031rIikvX6-9VMs#*%hV2@%nkQsedFeBL&}GgDTZq`f&+x|K%I<3}nx@<4WdIsU zbv;WE`m9|G_M?c^alU{F$q7lptX&}Wduc%=ByfUKC)KhzlmfRv>~X!~G=w*%L!Wo; z%+p-6Gb|F{ccCmz(Kmexafy{Sqhw|yg_th;g_p+G<^&p4&ZTE#DN8~a#lGOWJBxIc zn6Yw^W1AlAT%l3)^)aKQDi1XdL$xjCha7b;PH1^rgUtp*nB8uQI(}`9=m*2pD50zU z5h6V3?rgnfrZLc0bDNIb|e&*31=^ zXeA{O0C6z|du61ASFlFslf)pWAscy3bO+N3vn^uE+U3=J@WfJd5W8yCJESUaM!?XB zVK=@HODHXkky#b$X8|8=Ml>x^%Taojv0QnjtVQb;2OZh>I;BdErD}~7`g!NJWQ$_k zQl#T43X?_2)EsF!4s&%cHA&ftqWyb3^YdaW_1!_VyiV$MI*56ntEHq;D4(V*3y%wj zULPEl642EtqU@AZ9=xdNZ93ze(CEj!=@TnL*>tr;sk-vG=?cw}vD(Db@EQ&>T)eJW zH;rpe!2I^C@{7}Q8kQP}B?T}ED4WLAj8)NYa0$xcWDrc(PqNqb#wWhpIH%8nMw5gN`{aM6hl(%MLRvCcmAI zQaq_Ex||LkTCgmYEm`*Lb&No4l3`OcW}W91PVWxIqdpxa&U%uB9nqa)n#%bsuQtRMQED_dO75!^ebo6;lu%KV^s*0kCsC-q>D%Y&EymdE#5(zCgHEWzE zwr)xSlkl?BkdpYf<4_9uosIZH5D83)FDMJsaFH6SWtPY{Ld<;Dz?+fXqy zCFr_mI$1$V+{*#d@-cIf+P;l~jx8td&7m=))(Gm&S)Lo9_1(%jk zmAN>)Ik^Rib8KEcXlKji1p_<0GlW*wy6B3O6HQZAQ}JUTHVtt|B4K5fYUMOU8b0GC zC%VcxOIMM_Z)bPb4u*-+&7dcMC-rB;@ltb|P5R^(JEU-Ojr`!yEGif1@GiIBT z@pN3heP5Y%6wP9_Etv-Mvy>@Rc5-09oefZQUn*ZVNrxymJ47@a>VwU0$X1oUwL$&K zE*HzGr^j;@{Wv_@MK!3gj-j^|)y|SA!?M)uy=PRD>((_)M>-tn_6CB1MGC7~hjoKK?#hOM`M&BGMZN zr+ql2x|tIul*e6Yj*DV*jPj@QJ{A@<=Xr$q`8#Fid^LY#K?%cvemQDd3#Z)!%0k(- z^WbrHO0i{^M}q60{jIwkbzTLmwcC{nnTuRDP#IJmjy(V6EA?%&J^ojn3H#&r8l?go zDg$>@<<{zEDAjk(zq;TrJP{_l4Bzncsw*VcemmT&Q+-{1FIbnjc`{98u%%-FZpiF@ zU%QHwiN5@`tLz^~Q-kq{mHHJYnV}q;)FdRQi^~jL+EPzu*yh#98xLsGt2CKTx!Y3* zb_Z_N<{D9T)P|vW1Bs^7PZDt*at5NbW#+13gMZzA$Q~}%LqjmyPx0g!gWK`83U@9E zeqmVXk*Kw=>wH|#Or2>UDcL|Pmhj%EK}UU>(WX%Egpi#zkU8N^V9R2BlW_M&v)_t! z>g-HlQr3`Pd{n(jx!ia|$C;1Vj#%<^&iutQeF+Pa(vS)04^iERAV*SZ8#x~;RZJ;HQzi%*x*E^{i5 zQK>#zyEO+L_bpT`Ew_({0&w_m%Ny&ovY@3%+wR^d!GWsU7AH$AEPY;xj*y(+w=`om zpG8liCuz-Zm>0>!{;hcqcPCp3R&QhBOq)w2zixgnf$vkxNtN%U7)($A-G zVxRCNbOvn-`M8brMR4BeTN)c?Kv+5InBtTEc6DePURhSlGJR9gOO-p_ficV$ zR#K!m{O*_$yC65ecV0&+!JxHEyj>7@Q59%=z@O6kz|tyNK=u3Om!TZSGlikw?}KRj z6_s|MdzFXG2ePRz4Yn4YzX`wJF$&`(;IHo9oR&_x`>Y1?h{eki7D#F=K*Cl313G#j z5Ka0ooZ!+d6`A}ax~igBdV-xS1DqEuvgu}4O7$)GIW`BQN;)6v>`sKlixXD8xFr<4 ztD_&6pI6!TzJ*ou_IJv6Udb)Zv?2*An-19vqZS9ve}3FuSQvUyz9sLEZj5E%a1$g6 z9jyH3VXWCzX;jlWxgy}?7KbzPO|j6hA7(bSLId9A7ME!)swJ6rj}s+|lycLLc22<+ zlpFv1$X!TL@gaBB2^r^IF4KIUryS|`Zx4_+K;6uC)LfUOc>~7zM&gDYr*G0%&{tsG zeAG%-@w!Wk;v1#iM}?KFOVlVDRZ7?PI;_5P)09RU*5wIw6byDBL;12gWW0&1mu64p z9i!!~HALt1PRyiKnE9^IFZZ-Z4uc@2O<^ATUA0y-@^5f5W+1K)dLIk^)2F94HxY|2Mj-^z93kk1?r68F1G$_SCRDOx zc3M}NkSDJ!>MBddivrCoNTvCs}e}E6hpQj|RO~|yCkMAw(ys)u}QK*8hTigvl=|XotsS9Ze@xoGPibs#x z5ka-`DksTr3xw446~($&D}kKyu9e^#-Y+2 z5tDXJ^>pil`3mEQCKe(OfQ|fwHkvW=-FFQ|t3oME9y8%Z1XN3_Ox;~9;u&Vu)Ngmz zL_a^`e>Q)cXJT5ukvGNKzbW2uy(IOWOPB^7e>vvGMae6Ex%p|iBKwqCy_=@4`&Gc%es4j}V{{xr{WIq6kMs#p6U#)BhY5;rxX zo;+FgfC-p>!YihaVbSp!b9v87(P9V7{>8|QU#iGCzfvTeDfM&klM5)FQD7=Eo%!(* zlfY)fq>=A=KBCaUjh0^{UFcn$`S2^#X$w`T@5%V@zq;9OR(ds;ySO7f{iQkaa-^yJ zoY(Il=q)w(1WXu&nGo(Pzly!)>!C__oMJO)PYPQnnke)v#iX87G*cC?+$wQ$sk_Kz zyKgLBWq)3w)ABaO*uYfwTUcL$F#GddWjD>9m>wbSEOcrZTg@$a&ph>WHo@a3l%f1| ztwBYMpXl+e^|BLcY4ZgAxDGEN+D^7+m=h4$-`RDp%xtV?joCvi}h5T2*uQaeKO_deIpJ@-pue=~I^nZ^1s zy;?9)CBC9~Bb07wyF3<7llg7_sU#hnNchvcwP~0DEAUwnK3fE19O`DY(vt$hAC-;L zwIlw8V?4j1R>Rq}`M~15I7N~^rS<#ROfy0?U%~9BY-Dt-vGjpO_N`LhmBlsY+!V?C z<-0l%F&PDDLmhE+pAIf}v~+3lviq+F?z$|W_B0CIND3u;^OfojezKa)8Cm=g8e<+$ zJO!diwJ^HObLAFBwNN$v+ev&`J3Nn~-BlQU{(M!PZ7#EOsz>+_LaH6UiijkAi+KFn zK@e5-FtwU2#jX+MoN)B_nOo-eW9UJBn%wrcLp7EAtdEc6Dc4)FSRel^M66e>JBGRW z1ZdWGJdD-&qKgx5FQHzkEq)^@Nymk3h}XomawxOghOgydNJm@iMy^eBgl-}KN&}%` zqoy(4Yv(KzYJ$>a{yF1cGX)8<&qvC1owCuUO{W~n8l zgbVsDVB|G~B{Adb9O?B+if$i%yt7iEu#^~KP2O}(gs!K*mdG)!nSE!d71B@*POBgtf9*aV z<65=`7hq+!dvU-vMHT^`s zdxZ-5RbjUKu*cbYTfd-2-im$@gPwV2PTChwW?#Ot>KJqv2();T(dXDlE$8c_9sja! zhwRrkIfBwcbxVDlw}vf^`w>REcC2e~jjt!QG$;ZEt>D-Tyf1G^At`s%2%?Xc zKk}r+L7)Z+99dt~!vnJ-pNUT6je&eMgw zsn~4lQ=Q(k-)Bs^u^)C@PgxS3N0RW=d}WADUv+zzXr#taZmvG*9ud8(LI1#B<^jB% zP=(fyA*@du`e5O(WyBL^@HRQt;HP(3zvkroJIngIJ`Y&MD=u8u_?}F0eo}qfxjH3% z6xLH-qFr@|p%aHj)ignA;?c=G-j6Oa=uF~ExsCI)voC2zu1}xxYV4ogNsackZr5Q~ z?-Cjmn7f_*qE>piT;W!p=;N`N_(+GH!!y@m&9lI57jW&Lueno`LsDPd>p!iXx^`)r z@fgC3aei|@&%R|R;qk3laQ~YojLzw)K$=>FqLFT_{@0Gl@|%>*NmNF4MX7Y9RSHis zLxjbtQ{;V=`CItTlp91cKhj@`q$dAt(CAHvYI#O2o0)ik>orGn6vcmVaBYzj*08*v zkR|r>Q4GHeh6Md}UWF}IxoF%7Qsz@oteM-9$hz6p&O61lXLut_e4C-Ghllfy@Z*a7 zPS-zFPf76A+=U*|mhS9*ct_W^$Lq`s*%z;B`4MDRt!vL~lT9-ypf@IASpnWLs@xd( zLkf5fN7N#+eyAeZF>JqAZuZ;$o7mHtn1+Xyy?1xa@4CZ#AH05F3ONX)W&T>sV+>W| z$-?YIeuYx$ro%3ejZ}%qWR|PkhZaVb(Xf_;ntWx$z^?~3rt>L=XETj883#E@>nIna zxRbuw*Id(69<)axE1!JY%n5jiy^%?>IAxpOpVQ+R#N;@GbWE1ADQjJmR1TWzf5bV3 z#qxTbxv}>e#NpgaZ7+@M=_yb>meb~zX)#^J`N$mHwb`dUzVzgvAxBAA@Ypq?D(xdRQTSA3-b+;+K*Rm>I;drOp(hom&C$SW^>HujpL zL=MkKb?4_?Ptp_##dXm&f`+P0OLr5i9S)quU`?l7_Nd`L{T-mYo<%TnH5v@3jpuM_&YML7f%bWg&WLbxgA|jMJ(MiJS(q6k#1Ks{&9_TzD z5wza3S?fh*!~4anYNHs%Oh;Mw=r4R2{%SHVsihRUf)|vp_3~fo>e$$ASs9Kwe>0lJ zAi?o8qUz-JxiuDzrkN2bn&+@WslRtH7@EG#*M-t|EO=U!n4Dlu4GbJKBrPd1Cgi$@ zTy9XOsOg)`4ds#(G=?}CF*S4Ga^KwgPoEY(FjTKz-s3>+h*;)cCXvW6Fp#9O+@GTt zlwxMVyT^T5@*XF)T+g*s7|)QiUe=dlnsldm>NF-UPQf&!pZ%?3d@`Px!GP{WvZ3Nb zMq<4O>}+g*;yn+lM|%hTbS?c?59X`xbxbOV*j8)rH7H>`Dx@yx^e75TiTNb+xV!^v zrFLjb7GhNSzu2)~Y-q1c{PB0F9rfpW6^<;wBbY5SHPSi+7;rJ$Na@U?cs!_ytKG=nT0vQ?r4OoX-iWb>k##!4k-x?&U;#;OM_$ixx>DO zf57vY=v$P&q3i3F{GzrR0f^4exjK^fs%ILqws#M6G+k&Fh&njVnpF9kTs5rcatyU^ zTK~GZW2IT?=?gZ>d`}FeOet3h))_f&oT?_KNo#G%Sk|!pHS8^l4d5>n)~HNQQF+UzIAM0pxjO6 z<$fm?__4jqz#k%N+?0e0EK>-;kskA8|!(~?XL!@7e7ePOv@@` zBqCV4!%J}F?Bk^yr0V)!)9{k>D@}Qe)KXD|L|(P9(#zL43fZa*dGG?p#g-3*MRg{&*R zD<3QMLx%&@-Adi2xs8Q^EJORM#9j+aQdK;kF171AV%{oB<1;V{Q}`JJ^Hm?Ozwp)g zCj5rE!Z!V>R6#lp|LAlJw?t_*6=ZHIi{@5rwYfyXJl#>q=emF4@I+4#m47l(nz#4h zxlHD1wUgogRnSYH$4~fK=V0_EWL2h8ldn{9nKNIQ%jALO62|KHv&gSjPN@E5TK7Oty=?ow^{VMieD)mKOaABeT0N8ugX3u1|pT9ZUG&4;N zz1LNN^YXX<>moafJP*_6G49bjzwuGr^ipl=&q~VZs+~qqMN-|&7k}-#AhejCbj##^ z$Kh49dWA6YnXmXjyg`F5r#rGM2-NeKdNkINN-V|PUu>Yr^Y>J89d!LhN`T?z_9fn} z$3&4GqpG>J1rprPcH`7W#n<>64DKWk71DktnE4lu8cjP3VFr z|3UNGyNOs+S=A9*>#(toez(n8cjba4e(vI0z6eCIU}(*kh}YZMaZh2&a*^n`S@o!D z>V}f!V*Nz@`h>ZgyO>vr?0s$(1=Wcbb_a#;f8Vn~n0QXf3#z<#q<(H#y(BP(Dpm2i zgQLwao)O_UYXzeljW7}6jnNJLu4jAsr3E6sq~Xk>&xbz!64LPGrQ*Z2M57}AU$^EA zM;t{e^@9^RB{Z`cujs4#zn2^zI$(;7M-;>ENCnYY#U1^;V5fWQ`6W1*!G&F8_S+z4 zjjmT>G9zEev_dlg`SDmFi$BHj%?h~maniR~w?Bb$&J8=Olc6QE5+7dK@+m5sHKZDv zTE;nxk-~!2Hc8}Am2W#-TNQco9?eki=fBmGo;^yeV)1LK$jWZj@7oU=sF+!iB`13A zk)(Kf_qS$$rQ8awuwkiK`}%i}@&olLetR-x?yYBmh*P(qPIn8noLEEtF=KZZ9)X6s zD7GEMS;*a{#ek;fe9jLKip0j6#kmL8QREu0N!@(=LY^_}5c^~;v&0yA`316bN=SK} z#>$>PJ zE|w6{(o*2!S(j+@$r38jG~YFqvZ`S%XIbW_a&CFJSiMG`g;mAeQ-wBlm zf)hh8ZCgHHa0`w}b@IH_Hw$3iFXB@Ts8M(hG1jsy7UdAMjIXPv;d-!22fMYB9WImi z=pi0j6S1bvyQ(DYtuHML-V@~-2@!`J(WU=Nea$ZlODk0^6&$2bGGo4_+|b8?+WW+V z6!oJ{(llop`uj{>J5cXD`uTm!+2gd&z6vHODdJMTtW&$I?8t%d3=4r)Q8F@38U=yt ze=W@NetDf!9wavB|0US@DXB!-+?mFKX>|#|o+!U*#iKl>uP4@p(C5eZcg=lYzX*Eh z-d0z$8XEGqhuxP2RAbIiAokET8E#u zRvvJ?(^;(mbvFlRs$2E;eU~d9v0B3EIXhvl|Uf7(NB%zFLu1XV(jZ!yZE4(#?s#yy;*m zJs6T)ve$&ncg9s;WeMRrYT-0lmDi?`(a2Ktez4t;UTcrE7jXuxG~d7YbtS zjFX$`Xz?MttCR!tjP-Rd4;@d)1+iPVSDfJzq18y2KnObK*#=*la$)w0gG=-|$#RF{ z?JD=_2a|XX)e{~&FbBDSgzul~%(n4w#-W?6%;d6mQu@?#x{-bV;<@By*dYZ8fQj#ff26ZSb^rfV! z#$43XByvWReh?1%E#pnjx6TV#4Z)BPl^9p z$W0VgcY8~3!ehIkLFF_4eV;K4uT)rzMw99>bvQCBF7<1o1cP^hMza=Ia}$1pJM}M?e?kcynBx_pJAHM}5+(h+)Nr~Vzbm;nGSh)XRp+En)*?3dxdkOsQb>Pn5 zYjlzHchFv#cIftk>Cb-c(Ruc&D_5rvcm@TR_snK9{Phi=^JIhc@4Uq#fkkSEfXkiE z|1B%2CcOBR%q#QRQd4-C{(u010_$_n5}Jd)_DUUGi|Ax61wdEwt6oT1!@y zikhk(K)84`+1(T~ZX5Ld8hR%bH>S&{s5XB@pLEY1?VcA$qjl4j%uCVShOHf6V>3@yFKyB#AKrL_aYJK6&4e9Og#qzjZq4@zlNnOC+6nr8wv zY8hOUG?(v;-}2CDI)}+t|Ez5RBYDZ{j>B6FM2!!5;ltkq%k+rC< z+s+s|q|&>u^smyURlTP6P_uWACReKj$)tU!4P0YM@#Gb+E-xHKua+kJCe}${3+-k} zBgUs65)Q3$v!_kV@a*%WLOv&_l-<+3=s&xinix3Xpdt)lFm@r2P2VNo-$W%*;n8y@ z1{k+)oa0B2+A)?5bij`L74d6GmR8UJGN%os;4gFGXChB54^T;zL_#y}4#q{RZ^^Gn zH0*7C^`a&&IE$**Sjg~Ka0fet-<-Vfses$t`RXDaFpHwh&v|4TXpnM^Sr)k@sgax% zRU52^gN^Z#^e3ddu|=su8w}**(A75THS-=hV!`MX>d#wNOr3Zub0dx5l^YwvR z!*Po8a7t}hOODO?R-x^1`jN&cquNx{yQ7-FU}J5BI2*JyG2vfZ^UOZnL>>zu^qM~o z8?~FP*eK3=dRcYJ03cC#)IqB3?{0!`cHdQQab z7<1I$L{cfsvB%rj>m`O`KE?+TvXM})plJYjo5t?{CwS#p@a(YQQS2a6q6Zc5Wf%5q z8ta5OUQSW+*L!Z}M_a-TmS~N+mitBNlfA2TX^;+xqa*Y5;EcH&lRyz|E z0p6^)!UEBWL|-WeU*JbsSyhU^8?IMC`8v~%G;Zgl7-H%LuI~^ET<+M0xA|G z

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

    l^DGNka`lb=88wb&9F3iV$PDM59Fgj?$4D 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 literal 0 HcmV?d00001 diff --git a/cypress/integration/api.js b/cypress/integration/api.js new file mode 100644 index 0000000..420cea2 --- /dev/null +++ b/cypress/integration/api.js @@ -0,0 +1,44 @@ +context("API Resources", () => { + before(() => { + cy.visit("/login"); + cy.login(); + cy.visit("/app/website"); + }); + + it("Creates two Comments", () => { + cy.insert_doc("Comment", { comment_type: "Comment", content: "hello" }); + cy.insert_doc("Comment", { comment_type: "Comment", content: "world" }); + }); + + it("Lists the Comments", () => { + cy.get_list("Comment") + .its("data") + .then((data) => expect(data.length).to.be.at.least(2)); + + cy.get_list("Comment", ["name", "content"], [["content", "=", "hello"]]).then((body) => { + expect(body).to.have.property("data"); + expect(body.data).to.have.lengthOf(1); + expect(body.data[0]).to.have.property("content"); + expect(body.data[0]).to.have.property("name"); + }); + }); + + it("Gets each Comment", () => { + cy.get_list("Comment").then((body) => + body.data.forEach((comment) => { + cy.get_doc("Comment", comment.name); + }) + ); + }); + + it("Removes the Comments", () => { + cy.get_list("Comment").then((body) => { + let comment_names = []; + body.data.map((comment) => comment_names.push(comment.name)); + comment_names = [...new Set(comment_names)]; // remove duplicates + comment_names.forEach((comment_name) => { + cy.remove_doc("Comment", comment_name); + }); + }); + }); +}); diff --git a/cypress/integration/assignment_rule.js b/cypress/integration/assignment_rule.js new file mode 100644 index 0000000..5431561 --- /dev/null +++ b/cypress/integration/assignment_rule.js @@ -0,0 +1,16 @@ +context("Assignment Rule", () => { + before(() => { + cy.login(); + }); + + it("Custom grid buttons work", () => { + cy.new_form("Assignment Rule"); + cy.findByRole("button", { name: "All Days" }).should("be.visible").click(); + cy.wait(2000); + cy.window() + .its("cur_frm") + .then((frm) => { + expect(frm.doc.assignment_days.length).to.equal(7); + }); + }); +}); diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js new file mode 100644 index 0000000..03ef967 --- /dev/null +++ b/cypress/integration/awesome_bar.js @@ -0,0 +1,48 @@ +context("Awesome Bar", () => { + before(() => { + cy.visit("/login"); + cy.login(); + cy.visit("/app/website"); + }); + + beforeEach(() => { + cy.get(".navbar .navbar-home").click(); + cy.findByPlaceholderText("Search or type a command (Ctrl + G)").as("awesome_bar"); + cy.get("@awesome_bar").type("{selectall}"); + }); + + it("navigates to doctype list", () => { + cy.get("@awesome_bar").type("todo"); + cy.wait(100); + cy.get(".awesomplete").findByRole("listbox").should("be.visible"); + cy.get("@awesome_bar").type("{enter}"); + cy.get(".title-text").should("contain", "To Do"); + cy.location("pathname").should("eq", "/app/todo"); + }); + + it("find text in doctype list", () => { + cy.get("@awesome_bar").type("test in todo"); + cy.wait(100); + cy.get("@awesome_bar").type("{enter}"); + cy.get(".title-text").should("contain", "To Do"); + cy.wait(200); + const name_filter = cy.get('[data-original-title="ID"] > input'); + name_filter.should("have.value", "%test%"); + cy.clear_filters(); + }); + + it("navigates to new form", () => { + cy.get("@awesome_bar").type("new blog post"); + cy.wait(100); + cy.get("@awesome_bar").type("{enter}"); + cy.get(".title-text:visible").should("have.text", "New Blog Post"); + }); + + it("calculates math expressions", () => { + cy.get("@awesome_bar").type("55 + 32"); + cy.wait(100); + cy.get("@awesome_bar").type("{downarrow}{enter}"); + cy.get(".modal-title").should("contain", "Result"); + cy.get(".msgprint").should("contain", "55 + 32 = 87"); + }); +}); diff --git a/cypress/integration/control_attach.js b/cypress/integration/control_attach.js new file mode 100644 index 0000000..fd80d12 --- /dev/null +++ b/cypress/integration/control_attach.js @@ -0,0 +1,251 @@ +context("Attach Control", () => { + before(() => { + cy.login(); + cy.visit("/app/doctype"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { + name: "Test Attach Control", + fields: [ + { + label: "Attach File or Image", + fieldname: "attach", + fieldtype: "Attach", + in_list_view: 1, + }, + ], + }); + }); + }); + it('Checking functionality for "Link" button in the "Attach" fieldtype', () => { + //Navigating to the new form for the newly created doctype + cy.new_form("Test Attach Control"); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Link" button to attach a file using the "Link" button + cy.findByRole("button", { name: "Link" }).click(); + cy.findByPlaceholderText("Attach a web link").type( + "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg", + { force: true } + ); + + //Clicking on the Upload button to upload the file + cy.intercept("POST", "/api/method/upload_file").as("upload_image"); + cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 }); + cy.wait("@upload_image"); + cy.findByRole("button", { name: "Save" }).click(); + + //Checking if the URL of the attached image is getting displayed in the field of the newly created doctype + cy.get(".attached-file > .ellipsis > .attached-file-link") + .should("have.attr", "href") + .and("equal", "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"); + + //Clicking on the "Clear" button + cy.get('[data-action="clear_attachment"]').click(); + + //Checking if clicking on the clear button clears the field of the doctype form and again displays the attach button + cy.get(".control-input > .btn-sm").should("contain", "Attach"); + + //Deleting the doc + cy.go_to_list("Test Attach Control"); + cy.get(".list-row-checkbox").eq(0).click(); + cy.get(".actions-btn-group > .btn").contains("Actions").click(); + cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click(); + cy.click_modal_primary_button("Yes"); + }); + + it('Checking functionality for "Library" button in the "Attach" fieldtype', () => { + //Navigating to the new form for the newly created doctype + cy.new_form("Test Attach Control"); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Link" button to attach a file using the "Link" button + cy.findByRole("button", { name: "Link" }).click(); + cy.findByPlaceholderText("Attach a web link").type( + "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg", + { force: true } + ); + + //Clicking on the Upload button to upload the file + cy.intercept("POST", "/api/method/upload_file").as("upload_image"); + cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 }); + cy.wait("@upload_image"); + cy.findByRole("button", { name: "Save" }).click(); + + //Navigating to the new form for the newly created doctype to check Library button + cy.new_form("Test Attach Control"); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Library" button to attach a file using the "Library" button + cy.findByRole("button", { name: "Library" }).click(); + cy.contains("72402.jpg").click(); + + //Clicking on the Upload button to upload the file + cy.intercept("POST", "/api/method/upload_file").as("upload_image"); + cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 }); + cy.wait("@upload_image"); + cy.findByRole("button", { name: "Save" }).click(); + + //Checking if the URL of the attached image is getting displayed in the field of the newly created doctype + cy.get(".attached-file > .ellipsis > .attached-file-link") + .should("have.attr", "href") + .and("equal", "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"); + + //Clicking on the "Clear" button + cy.get('[data-action="clear_attachment"]').click(); + + //Checking if clicking on the clear button clears the field of the doctype form and again displays the attach button + cy.get(".control-input > .btn-sm").should("contain", "Attach"); + + //Deleting both docs + cy.go_to_list("Test Attach Control"); + cy.get(".list-row-checkbox").eq(0).click(); + cy.get(".list-row-checkbox").eq(1).click(); + cy.get(".actions-btn-group > .btn").contains("Actions").click(); + cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click(); + cy.click_modal_primary_button("Yes"); + }); + + it('Checking that "Camera" button in the "Attach" fieldtype does show if camera is available', () => { + //Navigating to the new form for the newly created doctype + let doctype = "Test Attach Control"; + let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); + cy.visit(`/app/${dt_in_route}/new`, { + onBeforeLoad(win) { + // Mock "window.navigator.mediaDevices" property + // to return mock mediaDevices object + win.navigator.mediaDevices = { + ondevicechange: null, + }; + }, + }); + cy.get("body").should(($body) => { + const dataRoute = $body.attr("data-route"); + expect(dataRoute).to.match(new RegExp(`^Form/${doctype}/new-${dt_in_route}-`)); + }); + cy.get("body").should("have.attr", "data-ajax-state", "complete"); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Camera" button + cy.findByRole("button", { name: "Camera" }).should("exist"); + }); + + it('Checking that "Camera" button in the "Attach" fieldtype does not show if no camera is available', () => { + //Navigating to the new form for the newly created doctype + let doctype = "Test Attach Control"; + let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); + cy.visit(`/app/${dt_in_route}/new`, { + onBeforeLoad(win) { + // Delete "window.navigator.mediaDevices" property + delete win.navigator.mediaDevices; + }, + }); + cy.get("body").should(($body) => { + const dataRoute = $body.attr("data-route"); + expect(dataRoute).to.match(new RegExp(`^Form/${doctype}/new-${dt_in_route}-`)); + }); + cy.get("body").should("have.attr", "data-ajax-state", "complete"); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Camera" button + cy.findByRole("button", { name: "Camera" }).should("not.exist"); + }); +}); +context("Attach Control with Failed Document Save", () => { + before(() => { + cy.login(); + cy.visit("/app/doctype"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { + name: "Test Mandatory Attach Control", + fields: [ + { + label: "Attach File or Image", + fieldname: "attach", + fieldtype: "Attach", + in_list_view: 1, + }, + { + label: "Mandatory Text Field", + fieldname: "text_field", + fieldtype: "Text Editor", + in_list_view: 1, + reqd: 1, + }, + ], + }); + }); + }); + let temp_name = ""; + let docname = ""; + it("Attaching a file on an unsaved document", () => { + //Navigating to the new form for the newly created doctype + cy.new_form("Test Mandatory Attach Control"); + cy.get("body").should(($body) => { + temp_name = $body.attr("data-route").split("/")[2]; + }); + + //Clicking on the attach button which is displayed as part of creating a doctype with "Attach" fieldtype + cy.findByRole("button", { name: "Attach" }).click(); + + //Clicking on "Link" button to attach a file using the "Link" button + cy.findByRole("button", { name: "Link" }).click(); + cy.findByPlaceholderText("Attach a web link").type( + "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg", + { force: true } + ); + + //Clicking on the Upload button to upload the file + cy.intercept("POST", "/api/method/upload_file").as("upload_image"); + cy.get(".modal-footer").findByRole("button", { name: "Upload" }).click({ delay: 500 }); + cy.wait("@upload_image"); + cy.get(".msgprint-dialog .modal-title").contains("Missing Fields").should("be.visible"); + cy.hide_dialog(); + cy.fill_field("text_field", "Random value", "Text Editor").wait(500); + cy.findByRole("button", { name: "Save" }).click().wait(500); + + //Checking if the URL of the attached image is getting displayed in the field of the newly created doctype + cy.get(".attached-file > .ellipsis > .attached-file-link") + .should("have.attr", "href") + .and("equal", "https://wallpaperplay.com/walls/full/8/2/b/72402.jpg"); + + cy.get(".title-text").then(($value) => { + docname = $value.text(); + }); + }); + + it("Check if file was uploaded correctly", () => { + cy.go_to_list("File"); + cy.open_list_filter(); + cy.get(".fieldname-select-area .form-control") + .click() + .type("Attached To Name{enter}") + .blur() + .wait(500); + cy.get('input[data-fieldname="attached_to_name"]').click().type(docname).blur(); + cy.get(".filter-popover .apply-filters").click({ force: true }); + cy.get("header .level-right .list-count").should("contain.text", "1 of 1"); + }); + + it("Check if file exists with temporary name", () => { + cy.open_list_filter(); + cy.get('input[data-fieldname="attached_to_name"]').click().clear().type(temp_name).blur(); + cy.get(".filter-popover .apply-filters").click({ force: true }); + cy.get(".xhiveframework-list > .no-result").should("be.visible"); + }); +}); diff --git a/cypress/integration/control_autocomplete.js b/cypress/integration/control_autocomplete.js new file mode 100644 index 0000000..c685e0c --- /dev/null +++ b/cypress/integration/control_autocomplete.js @@ -0,0 +1,64 @@ +context("Control Autocomplete", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_autocomplete(options) { + cy.visit("/app/website"); + return cy.dialog({ + title: "Autocomplete", + fields: [ + { + label: "Select an option", + fieldname: "autocomplete", + fieldtype: "Autocomplete", + options: options || ["Option 1", "Option 2", "Option 3"], + }, + ], + }); + } + + it("should set the valid value", () => { + get_dialog_with_autocomplete().as("dialog"); + + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").focus().as("input"); + cy.wait(1000); + cy.get("@input").type("2", { delay: 300 }); + cy.get(".xhiveframework-control[data-fieldname=autocomplete]") + .findByRole("listbox") + .should("be.visible"); + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").type("{enter}", { + delay: 300, + }); + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").blur(); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("autocomplete"); + expect(value).to.eq("Option 2"); + dialog.clear(); + }); + }); + + it("should set the valid value with different label", () => { + const options_with_label = [ + { label: "Option 1", value: "option_1" }, + { label: "Option 2", value: "option_2" }, + ]; + get_dialog_with_autocomplete(options_with_label).as("dialog"); + + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").focus().as("input"); + cy.get(".xhiveframework-control[data-fieldname=autocomplete]") + .findByRole("listbox") + .should("be.visible"); + cy.get("@input").type("2", { delay: 300 }); + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").type("{enter}", { + delay: 300, + }); + cy.get(".xhiveframework-control[data-fieldname=autocomplete] input").blur(); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("autocomplete"); + expect(value).to.eq("option_2"); + dialog.clear(); + }); + }); +}); diff --git a/cypress/integration/control_barcode.js b/cypress/integration/control_barcode.js new file mode 100644 index 0000000..a3b9531 --- /dev/null +++ b/cypress/integration/control_barcode.js @@ -0,0 +1,57 @@ +context("Control Barcode", () => { + beforeEach(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_barcode() { + return cy.dialog({ + title: "Barcode", + fields: [ + { + label: "Barcode", + fieldname: "barcode", + fieldtype: "Barcode", + }, + ], + }); + } + + it("should generate barcode on setting a value", () => { + get_dialog_with_barcode().as("dialog"); + + cy.focused().blur(); + cy.get(".xhiveframework-control[data-fieldname=barcode]") + .findByRole("textbox") + .type("123456789") + .blur(); + cy.get( + '.xhiveframework-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]' + ).should("exist"); + + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("barcode"); + expect(value).to.contain(" { + get_dialog_with_barcode().as("dialog"); + + cy.focused().blur(); + cy.get(".xhiveframework-control[data-fieldname=barcode]") + .findByRole("textbox") + .type("123456789") + .blur(); + cy.get(".xhiveframework-control[data-fieldname=barcode]").findByRole("textbox").clear().blur(); + cy.get( + '.xhiveframework-control[data-fieldname=barcode] svg[data-barcode-value="123456789"]' + ).should("not.exist"); + + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("barcode"); + expect(value).to.equal(""); + }); + }); +}); diff --git a/cypress/integration/control_color.js b/cypress/integration/control_color.js new file mode 100644 index 0000000..aa3a45e --- /dev/null +++ b/cypress/integration/control_color.js @@ -0,0 +1,80 @@ +context("Control Color", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_color() { + return cy.dialog({ + title: "Color", + fields: [ + { + label: "Color", + fieldname: "color", + fieldtype: "Color", + }, + ], + }); + } + + it("Verifying if the color control is selecting correct", () => { + get_dialog_with_color().as("dialog"); + cy.findByPlaceholderText("Choose a color").click(); + + ///Selecting a color from the color palette + cy.get('[style="background-color: rgb(79, 157, 217);"]').click(); + + //Checking if the css attribute is correct + cy.get(".color-map").should("have.css", "color", "rgb(79, 157, 217)"); + cy.get(".hue-map").should("have.css", "color", "rgb(0, 145, 255)"); + + //Checking if the correct color is being selected + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("color"); + expect(value).to.equal("#4F9DD9"); + }); + + //Selecting a color + cy.get('[style="background-color: rgb(203, 41, 41);"]').click(); + + //Checking if the correct css is being selected + cy.get(".color-map").should("have.css", "color", "rgb(203, 41, 41)"); + cy.get(".hue-map").should("have.css", "color", "rgb(255, 0, 0)"); + + //Checking if the correct color is being selected + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("color"); + expect(value).to.equal("#CB2929"); + }); + + //Selecting color from the palette + cy.get(".color-map > .color-selector").click(65, 87, { force: true }); + cy.get(".color-map").should("have.css", "color", "rgb(56, 0, 0)"); + + //Checking if the expected color is selected and getting displayed + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("color"); + expect(value).to.equal("#380000"); + }); + + //Selecting the color from the hue map + cy.get(".hue-map > .hue-selector").click(35, -1, { force: true }); + cy.get(".color-map").should("have.css", "color", "rgb(56, 45, 0)"); + cy.get(".hue-map").should("have.css", "color", "rgb(255, 204, 0)"); + cy.get(".color-map > .color-selector").click(55, 12, { force: true }); + cy.get(".color-map").should("have.css", "color", "rgb(46, 37, 0)"); + + //Checking if the correct color is being displayed + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("color"); + expect(value).to.equal("#2e2500"); + }); + + //Clearing the field and checking if the field contains the placeholder "Choose a color" + cy.get(".input-with-feedback").click({ force: true }); + cy.get_field("color", "Color").type("{selectall}").clear(); + cy.get_field("color", "Color") + .invoke("attr", "placeholder") + .should("contain", "Choose a color"); + }); +}); diff --git a/cypress/integration/control_currency.js b/cypress/integration/control_currency.js new file mode 100644 index 0000000..3543dd7 --- /dev/null +++ b/cypress/integration/control_currency.js @@ -0,0 +1,88 @@ +context("Control Currency", () => { + const fieldname = "currency_field"; + + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_currency(df_options = {}) { + return cy.dialog({ + title: "Currency Check", + animate: false, + fields: [ + { + fieldname: fieldname, + fieldtype: "Currency", + Label: "Currency", + ...df_options, + }, + ], + }); + } + + it("check value changes", () => { + const TEST_CASES = [ + { + input: "10.101", + df_options: { precision: 1 }, + blur_expected: "10.1", + }, + { + input: "10.101", + df_options: { precision: "3" }, + blur_expected: "10.101", + }, + { + input: "10.101", + df_options: { precision: "" }, // default assumed to be 2; + blur_expected: "10.10", + }, + { + input: "10.101", + df_options: { precision: "0" }, + blur_expected: "10", + }, + { + input: "10.101", + df_options: { precision: 0 }, + blur_expected: "10", + }, + { + input: "10.000", + number_format: "#.###,##", + df_options: { precision: 0 }, + blur_expected: "10.000", + }, + { + input: "10.000", + number_format: "#.###,##", + blur_expected: "10.000,00", + }, + { + input: "10.101", + df_options: { precision: "" }, + blur_expected: "10.1", + default_precision: 1, + }, + ]; + + TEST_CASES.forEach((test_case) => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.boot.sysdefaults.currency = test_case.currency; + xhiveframework.boot.sysdefaults.currency_precision = test_case.default_precision ?? 2; + xhiveframework.boot.sysdefaults.number_format = test_case.number_format ?? "#,###.##"; + }); + + get_dialog_with_currency(test_case.df_options).as("dialog"); + cy.wait(300); + cy.get_field(fieldname, "Currency").clear(); + cy.wait(300); + cy.fill_field(fieldname, test_case.input, "Currency").blur(); + cy.get_field(fieldname, "Currency").should("have.value", test_case.blur_expected); + cy.hide_dialog(); + }); + }); +}); diff --git a/cypress/integration/control_data.js b/cypress/integration/control_data.js new file mode 100644 index 0000000..7132c61 --- /dev/null +++ b/cypress/integration/control_data.js @@ -0,0 +1,148 @@ +context("Data Control", () => { + before(() => { + cy.login(); + cy.visit("/app/doctype"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { + name: "Test Data Control", + fields: [ + { + label: "Name", + fieldname: "name1", + fieldtype: "Data", + options: "Name", + in_list_view: 1, + reqd: 1, + }, + { + label: "Email-ID", + fieldname: "email", + fieldtype: "Data", + options: "Email", + in_list_view: 1, + reqd: 1, + }, + { + label: "Phone No.", + fieldname: "phone", + fieldtype: "Data", + options: "Phone", + in_list_view: 1, + reqd: 1, + }, + ], + }); + }); + }); + + it("check custom formatters", () => { + cy.visit(`/app/doctype/User`); + cy.get( + '[data-fieldname="fields"] .grid-row[data-idx="3"] [data-fieldname="fieldtype"] .static-area' + ).should("have.text", "Section Break"); + }); + + it('Verifying data control by inputting different patterns for "Name" field', () => { + cy.new_form("Test Data Control"); + + //Checking the URL for the new form of the doctype + cy.location("pathname").should("contains", "/app/test-data-control/new-test-data-control"); + cy.get(".title-text").should("have.text", "New Test Data Control"); + cy.get('.xhiveframework-control[data-fieldname="name1"]') + .find("label") + .should("have.class", "reqd"); + cy.get('.xhiveframework-control[data-fieldname="email"]') + .find("label") + .should("have.class", "reqd"); + cy.get('.xhiveframework-control[data-fieldname="phone"]') + .find("label") + .should("have.class", "reqd"); + + //Checking if the status is "Not Saved" initially + cy.get(".indicator-pill").should("have.text", "Not Saved"); + + //Inputting data in the field + cy.fill_field("name1", "@@###", "Data"); + cy.fill_field("email", "test@example.com", "Data"); + cy.fill_field("phone", "9834280031", "Data"); + + //Checking if the border color of the field changes to red + cy.get('.xhiveframework-control[data-fieldname="name1"]').should("have.class", "has-error"); + cy.save(); + + //Checking for the error message + cy.get(".modal-title").should("have.text", "Message"); + cy.get(".msgprint").should("have.text", "@@### is not a valid Name"); + cy.hide_dialog(); + + cy.get_field("name1", "Data").clear({ force: true }); + cy.fill_field("name1", "Komal{}/!", "Data"); + cy.get('.xhiveframework-control[data-fieldname="name1"]').should("have.class", "has-error"); + cy.save(); + cy.get(".modal-title").should("have.text", "Message"); + cy.get(".msgprint").should("have.text", "Komal{}/! is not a valid Name"); + cy.hide_dialog(); + }); + + it('Verifying data control by inputting different patterns for "Email" field', () => { + cy.get_field("name1", "Data").clear({ force: true }); + cy.fill_field("name1", "Komal", "Data"); + cy.get_field("email", "Data").clear({ force: true }); + cy.fill_field("email", "komal", "Data"); + cy.get('.xhiveframework-control[data-fieldname="email"]').should("have.class", "has-error"); + cy.save(); + cy.get(".modal-title").should("have.text", "Message"); + cy.get(".msgprint").should("have.text", "komal is not a valid Email Address"); + cy.hide_dialog(); + cy.get_field("email", "Data").clear({ force: true }); + cy.fill_field("email", "komal@test", "Data"); + cy.get('.xhiveframework-control[data-fieldname="email"]').should("have.class", "has-error"); + cy.save(); + cy.get(".modal-title").should("have.text", "Message"); + cy.get(".msgprint").should("have.text", "komal@test is not a valid Email Address"); + cy.hide_dialog(); + }); + + it('Verifying data control by inputting different patterns for "Phone" field', () => { + cy.get_field("email", "Data").clear({ force: true }); + cy.fill_field("email", "komal@test.com", "Data"); + cy.get_field("phone", "Data").clear({ force: true }); + cy.fill_field("phone", "komal", "Data"); + cy.get('.xhiveframework-control[data-fieldname="phone"]').should("have.class", "has-error"); + cy.findByRole("button", { name: "Save" }).click({ force: true }); + cy.get(".modal-title").should("have.text", "Message"); + cy.get(".msgprint").should("have.text", "komal is not a valid Phone Number"); + cy.hide_dialog(); + }); + + it("Inputting correct data and saving the doc", () => { + //Inputting the data as expected and saving the document + cy.get_field("name1", "Data").clear({ force: true }); + cy.get_field("email", "Data").clear({ force: true }); + cy.get_field("phone", "Data").clear({ force: true }); + cy.fill_field("name1", "Komal", "Data"); + cy.fill_field("email", "komal@test.com", "Data"); + cy.fill_field("phone", "9432380001", "Data"); + cy.findByRole("button", { name: "Save" }).click({ force: true }); + //Checking if the fields contains the data which has been filled in + cy.location("pathname").should( + "not.contains", + "/app/test-data-control/new-test-data-control" + ); + cy.get_field("name1").should("have.value", "Komal"); + cy.get_field("email").should("have.value", "komal@test.com"); + cy.get_field("phone").should("have.value", "9432380001"); + }); + + it("Deleting the doc", () => { + //Deleting the inserted document + cy.go_to_list("Test Data Control"); + cy.get(".list-row-checkbox").eq(0).click({ force: true }); + cy.get(".actions-btn-group > .btn").contains("Actions").click(); + cy.get('.actions-btn-group > .dropdown-menu [data-label="Delete"]').click(); + cy.click_modal_primary_button("Yes"); + }); +}); diff --git a/cypress/integration/control_date.js b/cypress/integration/control_date.js new file mode 100644 index 0000000..b8b86dc --- /dev/null +++ b/cypress/integration/control_date.js @@ -0,0 +1,86 @@ +context("Date Control", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + function get_dialog(date_field_options) { + return cy.dialog({ + title: "Date", + animate: false, + fields: [ + { + label: "Date", + fieldname: "date", + fieldtype: "Date", + in_list_view: 1, + ...date_field_options, + }, + ], + }); + } + + it("Selecting a date from the datepicker & check prev & next button", () => { + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog().as("dialog"); + cy.get_field("date", "Date").click(); + cy.get(".datepicker--nav-title").click(); + cy.get(".datepicker--nav-title").click({ force: true }); + + //Inputing values in the date field + cy.get( + ".datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]" + ).click(); + cy.get( + ".datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]" + ).click(); + cy.get(".datepicker--days > .datepicker--cells > .datepicker--cell[data-date=15]").click(); + + // Verify if the selected date is set the date field + cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-01-15"); + + cy.get_field("date", "Date").click(); + + //Clicking on the next button in the datepicker + cy.get(".datepicker--nav-action[data-action=next]").click(); + + //Selecting a date from the datepicker + cy.get(".datepicker--cell[data-date=15]").click({ force: true }); + + //Verifying if the selected date has been displayed in the date field + cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-02-15"); + cy.wait(500); + cy.get_field("date", "Date").click(); + + //Clicking on the previous button in the datepicker + cy.get(".datepicker--nav-action[data-action=prev]").click(); + + //Selecting a date from the datepicker + cy.get(".datepicker--cell[data-date=15]").click({ force: true }); + + //Verifying if the selected date has been displayed in the date field + cy.window().its("cur_dialog.fields_dict.date.value").should("be.equal", "2020-01-15"); + }); + + it('Clicking on "Today" button gives todays date', () => { + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog().as("dialog"); + cy.get_field("date", "Date").click(); + + //Clicking on "Today" button + cy.get(".datepicker--button").click(); + + //Verifying if clicking on "Today" button matches today's date + cy.window().then((win) => { + // `expect` can not wait like `should` + cy.wait(500); + expect(win.cur_dialog.fields_dict.date.value).to.be.equal( + win.xhiveframework.datetime.get_today() + ); + }); + }); +}); diff --git a/cypress/integration/control_date_range.js b/cypress/integration/control_date_range.js new file mode 100644 index 0000000..f95a382 --- /dev/null +++ b/cypress/integration/control_date_range.js @@ -0,0 +1,48 @@ +context("Date Range Control", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + function get_dialog() { + return cy.dialog({ + title: "Date Range", + fields: [ + { + label: "Date Range", + fieldname: "date_range", + fieldtype: "Date Range", + }, + ], + }); + } + + it("Selecting a date range from the datepicker", () => { + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog().as("dialog"); + cy.get_field("date_range", "Date Range").click(); + cy.get(".datepicker--nav-title").click(); + cy.get(".datepicker--nav-title").click({ force: true }); + + //Inputing date range values in the date range field + cy.get( + ".datepicker--years > .datepicker--cells > .datepicker--cell[data-year=2020]" + ).click(); + cy.get( + ".datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]" + ).click(); + cy.get(".datepicker--cell[data-date=1]:first").click({ force: true }); + cy.get(".datepicker--cell[data-date=15]:first").click({ force: true }); + + // Verify if the selected date range values is set in the date range field + cy.window() + .its("cur_dialog") + .then((dialog) => { + let date_range = dialog.get_value("date_range"); + expect(date_range[0]).to.equal("2020-01-01"); + expect(date_range[1]).to.equal("2020-01-15"); + }); + }); +}); diff --git a/cypress/integration/control_duration.js b/cypress/integration/control_duration.js new file mode 100644 index 0000000..b7c0db0 --- /dev/null +++ b/cypress/integration/control_duration.js @@ -0,0 +1,46 @@ +context("Control Duration", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_duration(hide_days = 0, hide_seconds = 0) { + return cy.dialog({ + title: "Duration", + fields: [ + { + fieldname: "duration", + fieldtype: "Duration", + hide_days: hide_days, + hide_seconds: hide_seconds, + }, + ], + }); + } + + it("should set duration", () => { + get_dialog_with_duration().as("dialog"); + cy.get(".xhiveframework-control[data-fieldname=duration] input").first().click(); + cy.get(".duration-input[data-duration=days]") + .type(45, { force: true }) + .blur({ force: true }); + cy.get(".duration-input[data-duration=minutes]").type(30).blur({ force: true }); + cy.get(".xhiveframework-control[data-fieldname=duration] input") + .first() + .should("have.value", "45d 30m"); + cy.get(".xhiveframework-control[data-fieldname=duration] input").first().blur(); + cy.get(".duration-picker").should("not.be.visible"); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("duration"); + expect(value).to.equal(3889800); + cy.hide_dialog(); + }); + }); + + it("should hide days or seconds according to duration options", () => { + get_dialog_with_duration(1, 1).as("dialog"); + cy.get(".xhiveframework-control[data-fieldname=duration] input").first(); + cy.get(".duration-input[data-duration=days]").should("not.be.visible"); + cy.get(".duration-input[data-duration=seconds]").should("not.be.visible"); + }); +}); diff --git a/cypress/integration/control_dynamic_link.js b/cypress/integration/control_dynamic_link.js new file mode 100644 index 0000000..7adca75 --- /dev/null +++ b/cypress/integration/control_dynamic_link.js @@ -0,0 +1,159 @@ +context("Dynamic Link", () => { + before(() => { + cy.login(); + cy.visit("/app/doctype"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { + name: "Test Dynamic Link", + fields: [ + { + label: "Document Type", + fieldname: "doc_type", + fieldtype: "Link", + options: "DocType", + in_list_view: 1, + in_standard_filter: 1, + }, + { + label: "Document ID", + fieldname: "doc_id", + fieldtype: "Dynamic Link", + options: "doc_type", + in_list_view: 1, + in_standard_filter: 1, + }, + ], + }); + }); + }); + + function get_dialog_with_dynamic_link() { + return cy.dialog({ + title: "Dynamic Link", + fields: [ + { + label: "Document Type", + fieldname: "doc_type", + fieldtype: "Link", + options: "DocType", + in_list_view: 1, + }, + { + label: "Document ID", + fieldname: "doc_id", + fieldtype: "Dynamic Link", + options: "doc_type", + in_list_view: 1, + }, + ], + }); + } + + function get_dialog_with_dynamic_link_option() { + return cy.dialog({ + title: "Dynamic Link", + fields: [ + { + label: "Document Type", + fieldname: "doc_type", + fieldtype: "Link", + options: "DocType", + in_list_view: 1, + }, + { + label: "Document ID", + fieldname: "doc_id", + fieldtype: "Dynamic Link", + get_options: () => { + return "User"; + }, + in_list_view: 1, + }, + ], + }); + } + + it("Creating a dynamic link by passing option as function and verifying it in a dialog", () => { + get_dialog_with_dynamic_link_option().as("dialog"); + cy.get_field("doc_type").clear(); + cy.fill_field("doc_type", "User", "Link"); + cy.get_field("doc_id").click(); + + //Checking if the listbox have length greater than 0 + cy.get('[data-fieldname="doc_id"]') + .find(".awesomplete") + .find("li") + .its("length") + .should("be.gte", 0); + cy.get(".btn-modal-close").click({ force: true }); + }); + + it("Creating a dynamic link and verifying it in a dialog", () => { + get_dialog_with_dynamic_link().as("dialog"); + cy.get_field("doc_type").clear(); + cy.fill_field("doc_type", "User", "Link"); + cy.get_field("doc_id").click(); + + //Checking if the listbox have length greater than 0 + cy.get('[data-fieldname="doc_id"]') + .find(".awesomplete") + .find("li") + .its("length") + .should("be.gte", 0); + cy.get(".btn-modal-close").click({ force: true, multiple: true }); + }); + + it("Creating a dynamic link and verifying it", () => { + cy.visit("/app/test-dynamic-link"); + + //Clicking on the Document ID field + cy.get_field("doc_type").clear(); + + //Entering User in the Doctype field + cy.fill_field("doc_type", "User", "Link", { delay: 500 }); + cy.get_field("doc_id").click(); + + //Checking if the listbox have length greater than 0 + cy.get('[data-fieldname="doc_id"]') + .find(".awesomplete") + .find("li") + .its("length") + .should("be.gte", 0); + + //Opening a new form for dynamic link doctype + cy.new_form("Test Dynamic Link"); + cy.get_field("doc_type").clear(); + + //Entering User in the Doctype field + cy.fill_field("doc_type", "User", "Link", { delay: 500 }); + cy.get_field("doc_id").click(); + + //Checking if the listbox have length greater than 0 + cy.get('[data-fieldname="doc_id"]') + .find(".awesomplete") + .find("li") + .its("length") + .should("be.gte", 0); + cy.get_field("doc_type").clear(); + + //Entering System Settings in the Doctype field + cy.intercept("/api/method/xhiveframework.desk.search.search_link").as("search_query"); + cy.fill_field("doc_type", "System Settings", "Link", { delay: 500 }); + cy.wait("@search_query"); + cy.get(`[data-fieldname="doc_type"] ul:visible li:first-child`).click({ + scrollBehavior: false, + }); + + cy.get_field("doc_id").click(); + + //Checking if the system throws error + cy.get(".modal-title").should("have.text", "Error"); + cy.get(".msgprint").should( + "have.text", + "System Settings is not a valid DocType for Dynamic Link" + ); + }); +}); diff --git a/cypress/integration/control_float.js b/cypress/integration/control_float.js new file mode 100644 index 0000000..6676f82 --- /dev/null +++ b/cypress/integration/control_float.js @@ -0,0 +1,110 @@ +context("Control Float", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_float() { + return cy.dialog({ + title: "Float Check", + animate: false, + fields: [ + { + fieldname: "float_number", + fieldtype: "Float", + Label: "Float", + }, + ], + }); + } + + it("check value changes", () => { + get_dialog_with_float().as("dialog"); + cy.wait(300); + + let data = get_data(); + data.forEach((x) => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.boot.sysdefaults.number_format = x.number_format; + }); + x.values.forEach((d) => { + cy.get_field("float_number", "Float").clear(); + cy.wait(200); + cy.fill_field("float_number", d.input, "Float").blur(); + cy.get_field("float_number", "Float").should("have.value", d.blur_expected); + cy.wait(100); + cy.get_field("float_number", "Float").focus(); + cy.wait(100); + cy.get_field("float_number", "Float").blur(); + cy.wait(100); + cy.get_field("float_number", "Float").focus(); + cy.wait(100); + cy.get_field("float_number", "Float").should("have.value", d.focus_expected); + }); + }); + }); + + function get_data() { + return [ + { + number_format: "#.###,##", + values: [ + { + input: "364.87,334", + blur_expected: "36.487,334", + focus_expected: "36.487,334", + }, + { + input: "36487,335", + blur_expected: "36.487,335", + focus_expected: "36.487,335", + }, + { + input: "2*(2+47)+1,5+1", + blur_expected: "100,500", + focus_expected: "100,500", + }, + ], + }, + { + number_format: "#,###.##", + values: [ + { + input: "464,87.334", + blur_expected: "46,487.334", + focus_expected: "46,487.334", + }, + { + input: "46487.335", + blur_expected: "46,487.335", + focus_expected: "46,487.335", + }, + { + input: "3*(2+47)+1.5+1", + blur_expected: "149.500", + focus_expected: "149.500", + }, + ], + }, + { + // '.' is the parseFloat's decimal separator + number_format: "#.###,##", + values: [ + { + input: "12.345", + blur_expected: "12.345,000", + focus_expected: "12.345,000", + }, + { + // parseFloat would reduce 12,340 to 12,34 if this string was ever to be parsed + input: "12.340", + blur_expected: "12.340,000", + focus_expected: "12.340,000", + }, + ], + }, + ]; + } +}); diff --git a/cypress/integration/control_icon.js b/cypress/integration/control_icon.js new file mode 100644 index 0000000..af8cefd --- /dev/null +++ b/cypress/integration/control_icon.js @@ -0,0 +1,55 @@ +context("Control Icon", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_icon() { + return cy.dialog({ + title: "Icon", + fields: [ + { + label: "Icon", + fieldname: "icon", + fieldtype: "Icon", + }, + ], + }); + } + + it("should set icon", () => { + get_dialog_with_icon().as("dialog"); + cy.get(".xhiveframework-control[data-fieldname=icon]").findByRole("textbox").click(); + + cy.get(".icon-picker .icon-wrapper[id=heart-active]").first().click(); + cy.get(".xhiveframework-control[data-fieldname=icon]") + .findByRole("textbox") + .should("have.value", "heart-active"); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("icon"); + expect(value).to.equal("heart-active"); + }); + + cy.get(".icon-picker .icon-wrapper[id=heart]").first().click(); + cy.get(".xhiveframework-control[data-fieldname=icon]") + .findByRole("textbox") + .should("have.value", "heart"); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("icon"); + expect(value).to.equal("heart"); + }); + }); + + it("search for icon and clear search input", () => { + let search_text = "ed"; + cy.get(".icon-picker").get(".search-icons > input").click().type(search_text); + cy.get(".icon-section .icon-wrapper:not(.hidden)").then((i) => { + cy.get(`.icon-section .icon-wrapper[id*='${search_text}']`).then((icons) => { + expect(i.length).to.equal(icons.length); + }); + }); + + cy.get(".icon-picker").get(".search-icons > input").clear().blur(); + cy.get(".icon-section .icon-wrapper").should("not.have.class", "hidden"); + }); +}); diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js new file mode 100644 index 0000000..8e8683c --- /dev/null +++ b/cypress/integration/control_link.js @@ -0,0 +1,334 @@ +context("Control Link", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + beforeEach(() => { + cy.visit("/app/website"); + cy.create_records({ + doctype: "ToDo", + description: "this is a test todo for link", + }).as("todos"); + }); + + function get_dialog_with_link() { + return cy.dialog({ + title: "Link", + fields: [ + { + label: "Select ToDo", + fieldname: "link", + fieldtype: "Link", + options: "ToDo", + }, + ], + }); + } + + function get_dialog_with_gender_link() { + return cy.dialog({ + title: "Link", + fields: [ + { + label: "Select Gender", + fieldname: "link", + fieldtype: "Link", + options: "Gender", + }, + ], + }); + } + + it("should set the valid value", () => { + get_dialog_with_link().as("dialog"); + + cy.insert_doc( + "Property Setter", + { + doctype: "Property Setter", + doc_type: "ToDo", + property: "show_title_field_in_link", + property_type: "Check", + doctype_or_field: "DocType", + value: "0", + }, + true + ); + + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("todo for link", { delay: 200 }); + cy.wait("@search_link"); + cy.get(".xhiveframework-control[data-fieldname=link]").findByRole("listbox").should("be.visible"); + cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".xhiveframework-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + cy.get("@todos").then((todos) => { + let value = dialog.get_value("link"); + expect(value).to.eq(todos[0]); + }); + }); + }); + + it("should unset invalid value", () => { + get_dialog_with_link().as("dialog"); + + cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input") + .type("invalid value", { delay: 100 }) + .blur(); + cy.wait("@validate_link"); + cy.get(".xhiveframework-control[data-fieldname=link] input").should("have.value", ""); + }); + + it("should be possible set empty value explicitly", () => { + get_dialog_with_link().as("dialog"); + + cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input").type(" ", { delay: 100 }).blur(); + cy.wait("@validate_link"); + cy.get(".xhiveframework-control[data-fieldname=link] input").should("have.value", ""); + cy.window() + .its("cur_dialog") + .then((dialog) => { + expect(dialog.get_value("link")).to.equal(""); + }); + }); + + it("should route to form on arrow click", () => { + get_dialog_with_link().as("dialog"); + + cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.get("@todos").then((todos) => { + cy.get(".xhiveframework-control[data-fieldname=link] input").as("input"); + cy.get("@input").focus(); + cy.wait("@search_link"); + cy.get("@input").type(todos[0]).blur(); + cy.wait("@validate_link"); + cy.get("@input").focus(); + cy.wait(500); // wait for arrow to show + cy.get(".xhiveframework-control[data-fieldname=link] .btn-open").should("be.visible").click(); + cy.location("pathname").should("eq", `/app/todo/${todos[0]}`); + }); + }); + + it("show title field in link", () => { + cy.insert_doc( + "Property Setter", + { + doctype: "Property Setter", + doc_type: "ToDo", + property: "show_title_field_in_link", + property_type: "Check", + doctype_or_field: "DocType", + value: "1", + }, + true + ); + + cy.reload(); + + get_dialog_with_link().as("dialog"); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + if (!xhiveframework.boot) { + xhiveframework.boot = { + link_title_doctypes: ["ToDo"], + }; + } else { + xhiveframework.boot.link_title_doctypes = ["ToDo"]; + } + }); + + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("todo for link", { delay: 200 }); + cy.wait("@search_link"); + cy.get(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".xhiveframework-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + cy.get("@todos").then((todos) => { + let field = dialog.get_field("link"); + let value = field.get_value(); + let label = field.get_label_value(); + + expect(value).to.eq(todos[0]); + expect(label).to.eq("this is a test todo for link"); + }); + }); + }); + + it("should update dependant fields (via fetch_from)", () => { + cy.get("@todos").then((todos) => { + cy.visit(`/app/todo/${todos[0]}`); + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + cy.intercept("POST", "/api/method/xhiveframework.client.validate_link").as("validate_link"); + + cy.get(".xhiveframework-control[data-fieldname=assigned_by] input").focus().as("input"); + cy.get("@input").clear().type(cy.config("testUser"), { delay: 300 }).blur(); + cy.wait("@validate_link"); + cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( + "contain", + "XhiveFramework" + ); + + cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser")); + + // invalid input + cy.get("@input").clear().type("invalid input", { delay: 100 }).blur(); + cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( + "contain", + "" + ); + + cy.window().its("cur_frm.doc.assigned_by").should("eq", null); + + // set valid value again + cy.get("@input").clear().focus(); + cy.wait("@search_link"); + cy.get("@input").type(cy.config("testUser"), { delay: 100 }).blur(); + cy.wait("@validate_link"); + + cy.window().its("cur_frm.doc.assigned_by").should("eq", cy.config("testUser")); + + // clear input + cy.get("@input").clear().blur(); + cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( + "contain", + "" + ); + + cy.window().its("cur_frm.doc.assigned_by").should("eq", ""); + }); + }); + + it("should set default values", () => { + cy.insert_doc( + "Property Setter", + { + doctype_or_field: "DocField", + doc_type: "ToDo", + field_name: "assigned_by", + property: "default", + property_type: "Text", + value: "Administrator", + }, + true + ); + cy.reload(); + cy.new_form("ToDo"); + cy.fill_field("description", "new", "Text Editor").wait(200); + cy.save(); + cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( + "contain", + "Administrator" + ); + // if user clears default value explicitly, system should not reset default again + cy.get_field("assigned_by").clear().blur(); + cy.save(); + cy.get_field("assigned_by").should("have.value", ""); + cy.get(".xhiveframework-control[data-fieldname=assigned_by_full_name] .control-value").should( + "contain", + "" + ); + }); + + it("show translated text for Gender link field with language de with input in de", () => { + cy.call("xhiveframework.tests.ui_test_helpers.insert_translations").then(() => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + cy.set_value("User", xhiveframework.user.name, { language: "de" }); + }); + + cy.clear_cache(); + cy.wait(500); + + get_dialog_with_gender_link().as("dialog"); + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("Sonstiges", { delay: 200 }); + cy.wait("@search_link"); + cy.get(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".xhiveframework-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + let field = dialog.get_field("link"); + let value = field.get_value(); + let label = field.get_label_value(); + + expect(value).to.eq("Other"); + expect(label).to.eq("Sonstiges"); + }); + }); + }); + + it("show text for Gender link field with language en", () => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + cy.set_value("User", xhiveframework.user.name, { language: "en" }); + }); + + cy.clear_cache(); + cy.wait(500); + + get_dialog_with_gender_link().as("dialog"); + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); + cy.wait("@search_link"); + cy.get("@input").type("Non-Conforming", { delay: 200 }); + cy.wait("@search_link"); + cy.get(".xhiveframework-control[data-fieldname=link] ul").should("be.visible"); + cy.get(".xhiveframework-control[data-fieldname=link] input").type("{enter}", { delay: 100 }); + cy.get(".xhiveframework-control[data-fieldname=link] input").blur(); + cy.get("@dialog").then((dialog) => { + let field = dialog.get_field("link"); + let value = field.get_value(); + let label = field.get_label_value(); + + expect(value).to.eq("Non-Conforming"); + expect(label).to.eq("Non-Conforming"); + }); + }); + + it("show custom link option", () => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.ui.form.ControlLink.link_options = (link) => { + return [ + { + html: + "" + + " " + + "Custom Link Option" + + "", + label: "Custom Link Option", + value: "custom__link_option", + action: () => {}, + }, + ]; + }; + + get_dialog_with_link().as("dialog"); + cy.get(".xhiveframework-control[data-fieldname=link] input").focus().as("input"); + cy.get("@input").type("custom", { delay: 100 }); + cy.get(".custom-link-option").should("be.visible"); + }); + }); +}); diff --git a/cypress/integration/control_markdown_editor.js b/cypress/integration/control_markdown_editor.js new file mode 100644 index 0000000..24ab32f --- /dev/null +++ b/cypress/integration/control_markdown_editor.js @@ -0,0 +1,22 @@ +context("Control Markdown Editor", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + it("should allow inserting images by drag and drop", () => { + cy.visit("/app/web-page/new"); + cy.fill_field("content_type", "Markdown", "Select"); + cy.get_field("main_section_md", "Markdown Editor").selectFile( + "cypress/fixtures/sample_image.jpg", + { + action: "drag-drop", + } + ); + cy.click_modal_primary_button("Upload"); + cy.get_field("main_section_md", "Markdown Editor").should( + "contain", + "![](/private/files/sample_image" + ); + }); +}); diff --git a/cypress/integration/control_phone.js b/cypress/integration/control_phone.js new file mode 100644 index 0000000..3b64657 --- /dev/null +++ b/cypress/integration/control_phone.js @@ -0,0 +1,99 @@ +import doctype_with_phone from "../fixtures/doctype_with_phone"; + +context("Control Phone", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + afterEach(() => { + cy.clear_dialogs(); + }); + + function get_dialog_with_phone() { + return cy.dialog({ + title: "Phone", + fields: [ + { + fieldname: "phone", + fieldtype: "Phone", + }, + ], + }); + } + + it("should set flag and data", () => { + get_dialog_with_phone().as("dialog"); + + cy.get(".selected-phone").click(); + cy.wait(100); + cy.get(".phone-picker .phone-wrapper[id='afghanistan']").click(); + cy.wait(100); + cy.get(".selected-phone .country").should("have.text", "+93"); + cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/af.svg"); + + cy.get(".selected-phone").click(); + cy.wait(100); + cy.get(".phone-picker .phone-wrapper[id='india']").click(); + cy.wait(100); + cy.get(".selected-phone .country").should("have.text", "+91"); + cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/in.svg"); + + let phone_number = "9312672712"; + cy.get(".selected-phone > img").click().first(); + cy.get_field("phone").first().click(); + cy.get(".xhiveframework-control[data-fieldname=phone]") + .findByRole("textbox") + .first() + .type(phone_number); + + cy.get_field("phone").first().should("have.value", phone_number); + cy.get_field("phone").first().blur(); + cy.wait(100); + cy.get("@dialog").then((dialog) => { + let value = dialog.get_value("phone"); + expect(value).to.equal("+91-" + phone_number); + }); + + let search_text = "india"; + cy.get(".selected-phone").click().first(); + cy.get(".phone-picker").get(".search-phones").click().type(search_text); + cy.get(".phone-section .phone-wrapper:not(.hidden)").then((i) => { + cy.get(`.phone-section .phone-wrapper[id*="${search_text.toLowerCase()}"]`).then( + (countries) => { + expect(i.length).to.equal(countries.length); + } + ); + }); + }); + + it("existing document should render phone field with data", () => { + cy.visit("/app/doctype"); + cy.insert_doc("DocType", doctype_with_phone, true); + cy.clear_cache(); + + // Creating custom doctype + cy.insert_doc("DocType", doctype_with_phone, true); + cy.visit("/app/doctype-with-phone"); + cy.click_listview_primary_button("Add Doctype With Phone"); + + // create a record + cy.fill_field("title", "Test Phone 1"); + cy.fill_field("phone", "+91-9823341234"); + cy.get_field("phone").should("have.value", "9823341234"); + cy.click_doc_primary_button("Save"); + cy.get_doc("Doctype With Phone", "Test Phone 1").then((doc) => { + let value = doc.data.phone; + expect(value).to.equal("+91-9823341234"); + }); + + // open the doc from list view + cy.go_to_list("Doctype With Phone"); + cy.clear_cache(); + cy.click_listview_row_item(0); + cy.title().should("eq", "Test Phone 1"); + cy.get(".selected-phone .country").should("have.text", "+91"); + cy.get(".selected-phone > img").should("have.attr", "src").and("include", "/in.svg"); + cy.get_field("phone").should("have.value", "9823341234"); + }); +}); diff --git a/cypress/integration/control_rating.js b/cypress/integration/control_rating.js new file mode 100644 index 0000000..613a6e9 --- /dev/null +++ b/cypress/integration/control_rating.js @@ -0,0 +1,54 @@ +context("Control Rating", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_rating() { + return cy.dialog({ + title: "Rating", + fields: [ + { + fieldname: "rate", + fieldtype: "Rating", + options: 7, + }, + ], + }); + } + + it("click on the star rating to record value", () => { + get_dialog_with_rating().as("dialog"); + + cy.get("div.rating") + .children("svg") + .find(".right-half") + .first() + .click() + .should("have.class", "star-click"); + cy.get("@dialog").then((dialog) => { + var value = dialog.get_value("rate"); + expect(value).to.equal(1 / 7); + dialog.hide(); + }); + }); + + it("hover on the star", () => { + get_dialog_with_rating(); + + cy.get("div.rating") + .children("svg") + .find(".right-half") + .first() + .invoke("trigger", "mouseenter") + .should("have.class", "star-hover") + .invoke("trigger", "mouseleave") + .should("not.have.class", "star-hover"); + }); + + it("check number of stars in rating", () => { + get_dialog_with_rating(); + + cy.get("div.rating").first().children("svg").should("have.length", 7); + }); +}); diff --git a/cypress/integration/control_select.js b/cypress/integration/control_select.js new file mode 100644 index 0000000..eae8bc6 --- /dev/null +++ b/cypress/integration/control_select.js @@ -0,0 +1,41 @@ +context("Control Select", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + function get_dialog_with_select() { + return cy.dialog({ + title: "Select", + fields: [ + { + fieldname: "select_control", + fieldtype: "Select", + placeholder: "Select an Option", + options: ["", "Option 1", "Option 2", "Option 2"], + }, + ], + }); + } + + it("toggles placholder on clicking an option", () => { + get_dialog_with_select().as("dialog"); + + cy.get(".xhiveframework-control[data-fieldname=select_control] .control-input").as("control"); + cy.get(".xhiveframework-control[data-fieldname=select_control] .control-input select").as( + "select" + ); + cy.get("@control").get(".select-icon").should("exist"); + cy.get("@control").get(".placeholder").should("have.css", "display", "block"); + cy.get("@select").select("Option 1"); + cy.findByDisplayValue("Option 1").should("exist"); + cy.get("@control").get(".placeholder").should("have.css", "display", "none"); + cy.get("@select").invoke("val", ""); + cy.findByDisplayValue("Option 1").should("not.exist"); + cy.get("@control").get(".placeholder").should("have.css", "display", "block"); + + cy.get("@dialog").then((dialog) => { + dialog.hide(); + }); + }); +}); diff --git a/cypress/integration/custom_buttons.js b/cypress/integration/custom_buttons.js new file mode 100644 index 0000000..ddbd197 --- /dev/null +++ b/cypress/integration/custom_buttons.js @@ -0,0 +1,57 @@ +const test_button_names = [ + "Metallica", + "Pink Floyd", + "Porcupine Tree (the GOAT)", + "AC / DC", + `Electronic Dance "music"`, + "l'imperatrice", +]; + +const add_button = (label, group = "TestGroup") => { + cy.window() + .its("cur_frm") + .then((frm) => { + frm.add_custom_button(label, () => {}, group); + }); +}; + +const check_button_count = (label, group = "TestGroup") => { + // Verify main buttons + cy.findByRole("button", { name: group }).click(); + cy.get(`[data-label="${encodeURIComponent(label)}"]`) + .should("have.length", 1) + .should("be.visible"); + + // Verify dropdown buttons in mobile view + cy.viewport(420, 900); + const dropdown_btn_label = `${group} > ${label}`; + cy.get(".menu-btn-group > .btn").click(); + cy.get(`[data-label="${encodeURIComponent(dropdown_btn_label)}"]`) + .should("have.length", 1) + .should("be.visible"); + + //reset viewport + cy.viewport(Cypress.config("viewportWidth"), Cypress.config("viewportHeight")); +}; + +describe( + "Custom group button behaviour on desk", + { scrollBehavior: false }, // speeds up the test + () => { + before(() => { + cy.login(); + cy.visit(`/app/note/new`); + }); + + test_button_names.forEach((button_name) => { + it(`Custom button works with name '${button_name}'`, () => { + add_button(button_name); + check_button_count(button_name); + + // duplicate button shouldn't be added + add_button(button_name); + check_button_count(button_name); + }); + }); + } +); diff --git a/cypress/integration/customize_form.js b/cypress/integration/customize_form.js new file mode 100644 index 0000000..bd8ca1d --- /dev/null +++ b/cypress/integration/customize_form.js @@ -0,0 +1,24 @@ +context("Customize Form", () => { + before(() => { + cy.login(); + cy.visit("/app/customize-form"); + }); + it("Changing to naming rule should update autoname", () => { + cy.fill_field("doc_type", "ToDo", "Link").blur(); + cy.wait(2000); + cy.findByRole("tab", { name: "Details" }).click(); + cy.click_form_section("Naming"); + const naming_rule_default_autoname_map = { + "Set by user": "prompt", + "By fieldname": "field:", + Expression: "format:", + "Expression (old style)": "", + Random: "hash", + "By script": "", + }; + Cypress._.forOwn(naming_rule_default_autoname_map, (value, naming_rule) => { + cy.fill_field("naming_rule", naming_rule, "Select"); + cy.get_field("autoname", "Data").should("have.value", value); + }); + }); +}); diff --git a/cypress/integration/dashboard.js b/cypress/integration/dashboard.js new file mode 100644 index 0000000..6eb2856 --- /dev/null +++ b/cypress/integration/dashboard.js @@ -0,0 +1,50 @@ +describe("Dashboard view", { scrollBehavior: false }, () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + it("should load", () => { + const chart = "TODO-YEARLY-TRENDS"; + const dashboard = "TODO-TEST-DASHBOARD"; // check slash in name intentionally. + + cy.insert_doc( + "Dashboard Chart", + { + is_standard: 0, + chart_name: chart, + chart_type: "Count", + document_type: "ToDo", + parent_document_type: "", + based_on: "creation", + group_by_type: "Count", + timespan: "Last Year", + time_interval: "Yearly", + timeseries: 1, + type: "Line", + filters_json: "[]", + }, + true + ); + + cy.insert_doc( + "Dashboard", + { + name: dashboard, + dashboard_name: dashboard, + is_standard: 0, + charts: [ + { + chart: chart, + }, + ], + }, + true + ); + + cy.visit(`/app/dashboard-view/${dashboard}`); + + // expect chart to be loaded + cy.findByText(chart).should("be.visible"); + }); +}); diff --git a/cypress/integration/dashboard_chart.js b/cypress/integration/dashboard_chart.js new file mode 100644 index 0000000..f2a837e --- /dev/null +++ b/cypress/integration/dashboard_chart.js @@ -0,0 +1,22 @@ +context("Dashboard Chart", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + it("Check filter populate for child table doctype", () => { + cy.new_form("Dashboard Chart"); + cy.get('[data-fieldname="parent_document_type"]').should("have.css", "display", "none"); + + cy.get_field("document_type", "Link"); + cy.fill_field("document_type", "Workspace Link", "Link").focus().blur(); + cy.get_field("document_type", "Link").should("have.value", "Workspace Link"); + + cy.fill_field("chart_name", "Test Chart", "Data"); + + cy.get('[data-fieldname="filters_json"]').click().wait(200); + cy.get(".modal-body .filter-action-buttons .add-filter").click(); + cy.get(".modal-body .fieldname-select-area").click(); + cy.get(".modal-actions .btn-modal-close").click(); + }); +}); diff --git a/cypress/integration/dashboard_links.js b/cypress/integration/dashboard_links.js new file mode 100644 index 0000000..d758150 --- /dev/null +++ b/cypress/integration/dashboard_links.js @@ -0,0 +1,94 @@ +import doctype_with_child_table from "../fixtures/doctype_with_child_table"; +import child_table_doctype from "../fixtures/child_table_doctype"; +import child_table_doctype_1 from "../fixtures/child_table_doctype_1"; +import doctype_to_link from "../fixtures/doctype_to_link"; +const doctype_to_link_name = doctype_to_link.name; +const child_table_doctype_name = child_table_doctype.name; + +context("Dashboard links", () => { + before(() => { + cy.visit("/login"); + cy.login("Administrator"); + cy.insert_doc("DocType", child_table_doctype, true); + cy.insert_doc("DocType", child_table_doctype_1, true); + cy.insert_doc("DocType", doctype_with_child_table, true); + cy.insert_doc("DocType", doctype_to_link, true); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.call("xhiveframework.tests.ui_test_helpers.update_child_table", { + name: child_table_doctype_name, + }); + }); + }); + + it("Adding a new contact, checking for the counter on the dashboard and deleting the created contact", () => { + cy.visit("/app/contact"); + cy.clear_filters(); + + cy.visit(`/app/user/${cy.config("testUser")}`); + + //To check if initially the dashboard contains only the "Contact" link and there is no counter + cy.select_form_tab("Connections"); + cy.get('[data-doctype="Contact"]').should("contain", "Contact"); + + //Adding a new contact + cy.get('.document-link-badge[data-doctype="Contact"]').click(); + cy.wait(300); + cy.findByRole("button", { name: "Add Contact" }).should("be.visible"); + cy.findByRole("button", { name: "Add Contact" }).click(); + cy.get('[data-doctype="Contact"][data-fieldname="first_name"]').type("Admin"); + cy.findByRole("button", { name: "Save" }).click(); + cy.visit(`/app/user/${cy.config("testUser")}`); + + //To check if the counter for contact doc is "2" after adding additional contact + cy.select_form_tab("Connections"); + cy.get('[data-doctype="Contact"] > .count').should("contain", "2"); + cy.get('[data-doctype="Contact"]').contains("Contact").click(); + + //Deleting the newly created contact + cy.visit("/app/contact"); + cy.get(".list-subject > .select-like > .list-row-checkbox").eq(0).click({ force: true }); + cy.findByRole("button", { name: "Actions" }).click(); + cy.get('.actions-btn-group [data-label="Delete"]').click(); + cy.findByRole("button", { name: "Yes" }).click({ delay: 700 }); + + //To check if the counter from the "Contact" doc link is removed + cy.wait(700); + cy.visit("/app/user"); + cy.get(".list-row-col > .level-item > .ellipsis").eq(0).click({ force: true }); + cy.get('[data-doctype="Contact"]').should("contain", "Contact"); + }); + + it("Report link in dashboard", () => { + cy.visit(`/app/user/${cy.config("testUser")}`); + cy.select_form_tab("Connections"); + cy.get('.document-link[data-doctype="Contact"]').contains("Contact"); + cy.window() + .its("cur_frm") + .then((cur_frm) => { + cur_frm.dashboard.data.reports = [ + { + label: "Reports", + items: ["Website Analytics"], + }, + ]; + cur_frm.dashboard.render_report_links(); + cy.get('.document-link[data-report="Website Analytics"]') + .contains("Website Analytics") + .click(); + }); + }); + + it("check if child table is populated with linked field on creation from dashboard link", () => { + cy.new_form(doctype_to_link_name); + cy.fill_field("title", "Test Linking"); + cy.findByRole("button", { name: "Save" }).click(); + + cy.get(".document-link .btn-new").click(); + cy.get( + '.xhiveframework-control[data-fieldname="child_table"] .rows .data-row .col[data-fieldname="doctype_to_link"]' + ).should("contain.text", "Test Linking"); + }); +}); diff --git a/cypress/integration/data_field_form_validation.js b/cypress/integration/data_field_form_validation.js new file mode 100644 index 0000000..4797ed2 --- /dev/null +++ b/cypress/integration/data_field_form_validation.js @@ -0,0 +1,45 @@ +import data_field_validation_doctype from "../fixtures/data_field_validation_doctype"; +const doctype_name = data_field_validation_doctype.name; + +context("Data Field Input Validation in New Form", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy.insert_doc("DocType", data_field_validation_doctype, true); + }); + + function validateField(fieldname, invalid_value, valid_value) { + // Invalid, should have has-error class + cy.get_field(fieldname).clear().type(invalid_value).blur(); + cy.get(`.xhiveframework-control[data-fieldname="${fieldname}"]`).should("have.class", "has-error"); + // Valid value, should not have has-error class + cy.get_field(fieldname).clear().type(valid_value); + cy.get(`.xhiveframework-control[data-fieldname="${fieldname}"]`).should( + "not.have.class", + "has-error" + ); + } + + describe("Data Field Options", () => { + it("should validate email address", () => { + cy.new_form(doctype_name); + validateField("email", "captian", "hello@test.com"); + }); + + it("should validate URL", () => { + validateField("url", "jkl", "https://xhiveframework.io"); + validateField("url", "abcd.com", "http://google.com/home"); + validateField("url", "&&http://google.uae", "gopher://xhiveframework.io"); + validateField("url", "ftt2:://google.in?q=news", "ftps2://xhiveframework.io/__/#home"); + validateField("url", "ftt2://", "ntps://localhost"); // For intranet URLs + }); + + it("should validate phone number", () => { + validateField("phone", "america", "89787878"); + }); + + it("should validate name", () => { + validateField("person_name", " 777Hello", "James Bond"); + }); + }); +}); diff --git a/cypress/integration/datetime.js b/cypress/integration/datetime.js new file mode 100644 index 0000000..f55d9b4 --- /dev/null +++ b/cypress/integration/datetime.js @@ -0,0 +1,126 @@ +import datetime_doctype from "../fixtures/datetime_doctype"; +const doctype_name = datetime_doctype.name; + +context("Control Date, Time and DateTime", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy.insert_doc("DocType", datetime_doctype, true); + }); + + describe("Date formats", () => { + let date_formats = [ + { + date_format: "dd-mm-yyyy", + part: 2, + length: 4, + separator: "-", + }, + { + date_format: "mm/dd/yyyy", + part: 0, + length: 2, + separator: "/", + }, + ]; + + date_formats.forEach((d) => { + it("test date format " + d.date_format, () => { + cy.set_value("System Settings", "System Settings", { + date_format: d.date_format, + }); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + // update sys_defaults value to avoid a reload + xhiveframework.sys_defaults.date_format = d.date_format; + }); + + cy.new_form(doctype_name); + cy.get(".form-control[data-fieldname=date]").focus(); + cy.get(".datepickers-container .datepicker.active").should("be.visible"); + cy.get( + ".datepickers-container .datepicker.active .datepicker--cell-day.-current-" + ).click({ force: true }); + + cy.window() + .its("cur_frm") + .then((cur_frm) => { + let formatted_value = cur_frm.get_field("date").input.value; + let parts = formatted_value.split(d.separator); + expect(parts[d.part].length).to.equal(d.length); + }); + }); + }); + }); + + describe("Time formats", () => { + let time_formats = [ + { + time_format: "HH:mm:ss", + value: " 11:00:12", + match_value: "11:00:12", + }, + { + time_format: "HH:mm", + value: " 11:00:12", + match_value: "11:00", + }, + ]; + + time_formats.forEach((d) => { + it("test time format " + d.time_format, () => { + cy.set_value("System Settings", "System Settings", { + time_format: d.time_format, + }); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.sys_defaults.time_format = d.time_format; + }); + cy.new_form(doctype_name); + cy.fill_field("time", d.value, "Time").blur(); + cy.get_field("time").should("have.value", d.match_value); + }); + }); + }); + + describe("DateTime formats", () => { + let datetime_formats = [ + { + date_format: "dd.mm.yyyy", + time_format: "HH:mm:ss", + value: " 02.12.2019 11:00:12", + doc_value: "2019-12-02 00:30:12", // system timezone (America/New_York) + input_value: "02.12.2019 11:00:12", // admin timezone (Asia/Kolkata) + }, + { + date_format: "mm-dd-yyyy", + time_format: "HH:mm", + value: " 12-02-2019 11:00:00", + doc_value: "2019-12-02 00:30:00", // system timezone (America/New_York) + input_value: "12-02-2019 11:00", // admin timezone (Asia/Kolkata) + }, + ]; + + datetime_formats.forEach((d) => { + it(`test datetime format ${d.date_format} ${d.time_format}`, () => { + cy.set_value("System Settings", "System Settings", { + date_format: d.date_format, + time_format: d.time_format, + }); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.sys_defaults.date_format = d.date_format; + xhiveframework.sys_defaults.time_format = d.time_format; + }); + cy.new_form(doctype_name); + cy.fill_field("datetime", d.value, "Datetime").blur(); + cy.get_field("datetime").should("have.value", d.input_value); + + cy.window().its("cur_frm.doc.datetime").should("eq", d.doc_value); + }); + }); + }); +}); diff --git a/cypress/integration/datetime_field_form_validation.js b/cypress/integration/datetime_field_form_validation.js new file mode 100644 index 0000000..b3081f8 --- /dev/null +++ b/cypress/integration/datetime_field_form_validation.js @@ -0,0 +1,19 @@ +// TODO: Enable this again +// currently this is flaky possibly because of different timezone in CI + +// context('Datetime Field Validation', () => { +// before(() => { +// cy.login(); +// cy.visit('/app/communication'); +// }); + +// it('datetime field form validation', () => { +// // validating datetime field value when value is set from backend and get validated on form load. +// cy.window().its('xhiveframework').then(xhiveframework => { +// return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_communication_record"); +// }).then(doc => { +// cy.visit(`/app/communication/${doc.name}`); +// cy.get('.indicator-pill').should('contain', 'Open').should('have.class', 'red'); +// }); +// }); +// }); diff --git a/cypress/integration/depends_on.js b/cypress/integration/depends_on.js new file mode 100644 index 0000000..88e13cd --- /dev/null +++ b/cypress/integration/depends_on.js @@ -0,0 +1,152 @@ +context("Depends On", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_child_doctype", { + name: "Child Test Depends On", + fields: [ + { + label: "Child Test Field", + fieldname: "child_test_field", + fieldtype: "Data", + in_list_view: 1, + }, + { + label: "Child Dependant Field", + fieldname: "child_dependant_field", + fieldtype: "Data", + in_list_view: 1, + }, + { + label: "Child Display Dependant Field", + fieldname: "child_display_dependant_field", + fieldtype: "Data", + in_list_view: 1, + }, + ], + }); + }) + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.create_doctype", { + name: "Test Depends On", + fields: [ + { + label: "Test Field", + fieldname: "test_field", + fieldtype: "Data", + }, + { + label: "Dependant Field", + fieldname: "dependant_field", + fieldtype: "Data", + mandatory_depends_on: "eval:doc.test_field=='Some Value'", + read_only_depends_on: "eval:doc.test_field=='Some Other Value'", + }, + { + label: "Display Dependant Field", + fieldname: "display_dependant_field", + fieldtype: "Data", + depends_on: "eval:doc.test_field=='Value'", + }, + { + label: "Child Test Depends On Field", + fieldname: "child_test_depends_on_field", + fieldtype: "Table", + read_only_depends_on: "eval:doc.test_field=='Some Other Value'", + options: "Child Test Depends On", + }, + { + label: "Dependent Tab", + fieldname: "dependent_tab", + fieldtype: "Tab Break", + depends_on: "eval:doc.test_field=='Show Tab'", + }, + { + fieldname: "tab_section", + fieldtype: "Section Break", + }, + { + label: "Field in Tab", + fieldname: "field_in_tab", + fieldtype: "Data", + }, + ], + }); + }); + }); + it("should show the tab on other setting field value", () => { + cy.new_form("Test Depends On"); + cy.fill_field("test_field", "Show Tab"); + cy.get("body").click(); + cy.findByRole("tab", { name: "Dependent Tab" }).should("be.visible"); + }); + it("should set the field as mandatory depending on other fields value", () => { + cy.new_form("Test Depends On"); + cy.fill_field("test_field", "Some Value"); + cy.findByRole("button", { name: "Save" }).click(); + cy.get(".msgprint-dialog .modal-title").contains("Missing Fields").should("be.visible"); + cy.hide_dialog(); + cy.fill_field("test_field", "Random value"); + cy.findByRole("button", { name: "Save" }).click(); + cy.get(".msgprint-dialog .modal-title") + .contains("Missing Fields") + .should("not.be.visible"); + }); + it("should set the field as read only depending on other fields value", () => { + cy.new_form("Test Depends On"); + cy.fill_field("dependant_field", "Some Value"); + cy.fill_field("test_field", "Some Other Value"); + cy.get("body").click(); + cy.get('.control-input [data-fieldname="dependant_field"]').should("be.disabled"); + cy.fill_field("test_field", "Random Value"); + cy.get("body").click(); + cy.get('.control-input [data-fieldname="dependant_field"]').should("not.be.disabled"); + }); + it("should set the table and its fields as read only depending on other fields value", () => { + cy.new_form("Test Depends On"); + cy.fill_field("dependant_field", "Some Value"); + //cy.fill_field('test_field', 'Some Other Value'); + cy.get('.xhiveframework-control[data-fieldname="child_test_depends_on_field"]').as("table"); + cy.get("@table").findByRole("button", { name: "Add Row" }).click(); + cy.get("@table").find('[data-idx="1"]').as("row1"); + cy.get("@row1").find(".btn-open-row").click(); + cy.get("@row1").find(".form-in-grid").as("row1-form_in_grid"); + //cy.get('@row1-form_in_grid').find('') + cy.fill_table_field("child_test_depends_on_field", "1", "child_test_field", "Some Value"); + cy.fill_table_field( + "child_test_depends_on_field", + "1", + "child_dependant_field", + "Some Other Value" + ); + + cy.get("@row1-form_in_grid").find(".grid-collapse-row").click(); + + // set the table to read-only + cy.fill_field("test_field", "Some Other Value"); + + // grid row form fields should be read-only + cy.get("@row1").find(".btn-open-row").click(); + + cy.get("@row1-form_in_grid") + .find('.control-input [data-fieldname="child_test_field"]') + .should("be.disabled"); + cy.get("@row1-form_in_grid") + .find('.control-input [data-fieldname="child_dependant_field"]') + .should("be.disabled"); + }); + it("should display the field depending on other fields value", () => { + cy.new_form("Test Depends On"); + cy.get('.control-input [data-fieldname="display_dependant_field"]').should( + "not.be.visible" + ); + cy.get('.control-input [data-fieldname="test_field"]').clear(); + cy.fill_field("test_field", "Value"); + cy.get("body").click(); + cy.get('.control-input [data-fieldname="display_dependant_field"]').should("be.visible"); + }); +}); diff --git a/cypress/integration/discussions.js b/cypress/integration/discussions.js new file mode 100644 index 0000000..f7d73b3 --- /dev/null +++ b/cypress/integration/discussions.js @@ -0,0 +1,85 @@ +context("Discussions", () => { + before(() => { + cy.login(); + cy.visit("/app"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_data_for_discussions"); + }); + }); + + const reply_through_modal = () => { + cy.visit("/test-page-discussions"); + + // Open the modal + cy.get(".reply").click(); + cy.wait(500); + cy.get(".discussion-modal").should("be.visible"); + + // Enter title + cy.get(".modal .topic-title") + .type("Discussion from tests") + .should("have.value", "Discussion from tests"); + + // Enter comment + cy.get(".modal .discussions-comment").type( + "This is a discussion from the cypress ui tests." + ); + + // Submit + cy.get(".modal .submit-discussion").click(); + cy.wait(2000); + + // Check if discussion is added to page and content is visible + cy.get(".sidebar-parent:first .discussion-topic-title").should( + "have.text", + "Discussion from tests" + ); + cy.get(".discussion-on-page:visible").should("have.class", "show"); + cy.get(".discussion-on-page:visible .reply-card .reply-text .ql-editor p").should( + "have.text", + "This is a discussion from the cypress ui tests." + ); + }; + + const reply_through_comment_box = () => { + cy.get(".discussion-form:visible .discussions-comment").type( + "This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page." + ); + + cy.get(".discussion-form:visible .submit-discussion").click(); + cy.wait(3000); + cy.get(".discussion-on-page:visible").should("have.class", "show"); + cy.get(".discussion-on-page:visible") + .children(".reply-card") + .eq(1) + .find(".reply-text") + .should( + "have.text", + "This is a discussion from the cypress ui tests. This comment was entered through the commentbox on the page.\n" + ); + }; + + const single_thread_discussion = () => { + cy.visit("/test-single-thread"); + cy.get(".discussions-sidebar").should("have.length", 0); + cy.get(".reply").should("have.length", 0); + + cy.get(".discussion-form:visible .discussions-comment").type( + "This comment is being made on a single thread discussion." + ); + cy.get(".discussion-form:visible .submit-discussion").click(); + cy.wait(3000); + cy.get(".discussion-on-page") + .children(".reply-card") + .eq(-1) + .find(".reply-text") + .should("have.text", "This comment is being made on a single thread discussion.\n"); + }; + + it("reply through modal", reply_through_modal); + it("reply through comment box", reply_through_comment_box); + it("single thread discussion", single_thread_discussion); +}); diff --git a/cypress/integration/file_uploader.js b/cypress/integration/file_uploader.js new file mode 100644 index 0000000..9f4ba23 --- /dev/null +++ b/cypress/integration/file_uploader.js @@ -0,0 +1,86 @@ +context("FileUploader", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + function open_upload_dialog() { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + new xhiveframework.ui.FileUploader(); + }); + } + + it("upload dialog api works", () => { + open_upload_dialog(); + cy.get_open_dialog().should("contain", "Drag and drop files"); + cy.hide_dialog(); + }); + + it("should accept dropped files", () => { + open_upload_dialog(); + + cy.get_open_dialog() + .find(".file-upload-area") + .selectFile("cypress/fixtures/example.json", { + action: "drag-drop", + }); + + cy.get_open_dialog().find(".file-name").should("contain", "example.json"); + cy.intercept("POST", "/api/method/upload_file").as("upload_file"); + cy.get_open_dialog().findByRole("button", { name: "Upload" }).click(); + cy.wait("@upload_file").its("response.statusCode").should("eq", 200); + cy.get(".modal:visible").should("not.exist"); + }); + + it("should accept uploaded files", () => { + open_upload_dialog(); + + cy.get_open_dialog().findByRole("button", { name: "Library" }).click(); + cy.findByPlaceholderText("Search by filename or extension").type("example.json"); + cy.get_open_dialog().findAllByText("example.json").first().click(); + cy.intercept("POST", "/api/method/upload_file").as("upload_file"); + cy.get_open_dialog().findByRole("button", { name: "Upload" }).click(); + cy.wait("@upload_file") + .its("response.body.message") + .should("have.property", "file_name", "example.json"); + cy.get(".modal:visible").should("not.exist"); + }); + + it("should accept web links", () => { + open_upload_dialog(); + + cy.get_open_dialog().findByRole("button", { name: "Link" }).click(); + cy.get_open_dialog() + .findByPlaceholderText("Attach a web link") + .type("https://github.com", { delay: 100, force: true }); + cy.intercept("POST", "/api/method/upload_file").as("upload_file"); + cy.get_open_dialog().findByRole("button", { name: "Upload" }).click(); + cy.wait("@upload_file") + .its("response.body.message") + .should("have.property", "file_url", "https://github.com"); + cy.get(".modal:visible").should("not.exist"); + }); + + it("should allow cropping and optimization for valid images", () => { + open_upload_dialog(); + + cy.get_open_dialog() + .find(".file-upload-area") + .selectFile("cypress/fixtures/sample_image.jpg", { + action: "drag-drop", + }); + + cy.get_open_dialog().findAllByText("sample_image.jpg").should("exist"); + cy.get_open_dialog().find(".btn-crop").first().click(); + cy.get_open_dialog().findByRole("button", { name: "Crop" }).click(); + cy.get_open_dialog().findAllByRole("checkbox", { name: "Optimize" }).should("exist"); + cy.get_open_dialog().findAllByLabelText("Optimize").first().click(); + + cy.intercept("POST", "/api/method/upload_file").as("upload_file"); + cy.get_open_dialog().findByRole("button", { name: "Upload" }).click(); + cy.wait("@upload_file").its("response.statusCode").should("eq", 200); + cy.get(".modal:visible").should("not.exist"); + }); +}); diff --git a/cypress/integration/first_day_of_the_week.js b/cypress/integration/first_day_of_the_week.js new file mode 100644 index 0000000..9222778 --- /dev/null +++ b/cypress/integration/first_day_of_the_week.js @@ -0,0 +1,51 @@ +context("First Day of the Week", () => { + before(() => { + cy.login(); + }); + + beforeEach(() => { + cy.visit("/app/system-settings"); + cy.findByText("Date and Number Format").click(); + }); + + it("Date control starts with same day as selected in System Settings", () => { + cy.intercept( + "POST", + "/api/method/xhiveframework.core.doctype.system_settings.system_settings.load" + ).as("load_settings"); + cy.fill_field("first_day_of_the_week", "Tuesday", "Select"); + cy.findByRole("button", { name: "Save" }).click(); + cy.wait("@load_settings"); + cy.dialog({ + title: "Date", + fields: [ + { + label: "Date", + fieldname: "date", + fieldtype: "Date", + }, + ], + }); + cy.get_field("date").click(); + cy.get(".datepicker--day-name").eq(0).should("have.text", "Tu"); + }); + + it("Calendar view starts with same day as selected in System Settings", () => { + cy.intercept( + "POST", + "/api/method/xhiveframework.core.doctype.system_settings.system_settings.load" + ).as("load_settings"); + cy.fill_field("first_day_of_the_week", "Monday", "Select"); + cy.findByRole("button", { name: "Save" }).click(); + cy.wait("@load_settings"); + cy.visit("app/todo/view/calendar/default"); + cy.get(".fc-day-header > span").eq(0).should("have.text", "Mon"); + }); + + after(() => { + cy.visit("/app/system-settings"); + cy.findByText("Date and Number Format").click(); + cy.fill_field("first_day_of_the_week", "Sunday", "Select"); + cy.findByRole("button", { name: "Save" }).click(); + }); +}); diff --git a/cypress/integration/folder_navigation.js b/cypress/integration/folder_navigation.js new file mode 100644 index 0000000..ab32722 --- /dev/null +++ b/cypress/integration/folder_navigation.js @@ -0,0 +1,97 @@ +context("Folder Navigation", () => { + before(() => { + cy.visit("/login"); + cy.login(); + cy.visit("/app/file"); + }); + + it("Adding Folders", () => { + //Adding filter to go into the home folder + cy.get(".filter-x-button").click(); + cy.click_filter_button(); + cy.get(".filter-action-buttons > .text-muted").findByText("+ Add a Filter").click(); + cy.get(".fieldname-select-area > .awesomplete > .form-control:last").type("Fol{enter}"); + cy.get(".filter-field > .form-group > .link-field > .awesomplete > .input-with-feedback") + .first() + .type("Home{enter}"); + cy.get(".filter-action-buttons > div > .btn-primary").findByText("Apply Filters").click(); + + //Adding folder (Test Folder) + cy.click_menu_button("New Folder"); + cy.fill_field("value", "Test Folder"); + cy.click_modal_primary_button("Create"); + }); + + it("Navigating the nested folders, checking if the URL formed is correct, checking if the added content in the child folder is correct", () => { + //Navigating inside the Attachments folder + cy.clear_filters(); + cy.wait(500); + cy.get('[title="Attachments"] > span').click(); + + //To check if the URL formed after visiting the attachments folder is correct + cy.location("pathname").should("eq", "/app/file/view/home/Attachments"); + cy.visit("/app/file/view/home/Attachments"); + + //Adding folder inside the attachments folder + cy.click_menu_button("New Folder"); + cy.fill_field("value", "Test Folder"); + cy.click_modal_primary_button("Create"); + + //Navigating inside the added folder in the Attachments folder + cy.wait(500); + cy.get('[title="Test Folder"] > span').click(); + + //To check if the URL is correct after visiting the Test Folder + cy.location("pathname").should("eq", "/app/file/view/home/Attachments/Test%20Folder"); + cy.visit("/app/file/view/home/Attachments/Test%20Folder"); + + //Adding a file inside the Test Folder + cy.findByRole("button", { name: "Add File" }).eq(0).click({ force: true }); + cy.get(".file-uploader").findByText("Link").click(); + cy.get(".input-group > input.form-control:visible").as("upload_input"); + cy.get("@upload_input").type("https://wallpaperplay.com/walls/full/8/2/b/72402.jpg", { + waitForAnimations: false, + parseSpecialCharSequences: false, + force: true, + delay: 100, + }); + cy.click_modal_primary_button("Upload"); + + //To check if the added file is present in the Test Folder + cy.visit("/app/file/view/home/Attachments"); + cy.wait(500); + cy.get("span.level-item > a > span").should("contain", "Test Folder"); + cy.visit("/app/file/view/home/Attachments/Test%20Folder"); + + cy.wait(500); + cy.get(".list-row-container").eq(0).should("contain.text", "72402.jpg"); + cy.get(".list-row-checkbox").eq(0).click(); + + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.reportview.delete_items", + }).as("file_deleted"); + + //Deleting the added file from the Test folder + cy.click_action_button("Delete"); + cy.click_modal_primary_button("Yes"); + cy.wait("@file_deleted"); + + //Deleting the Test Folder + cy.visit("/app/file/view/home/Attachments"); + cy.get(".list-row-checkbox").eq(0).click(); + cy.click_action_button("Delete"); + cy.click_modal_primary_button("Yes"); + cy.wait("@file_deleted"); + }); + + it("Deleting Test Folder from the home", () => { + //Deleting the Test Folder added in the home directory + cy.visit("/app/file/view/home"); + cy.get(".level-left > .list-subject > .file-select >.list-row-checkbox") + .eq(0) + .click({ force: true, delay: 500 }); + cy.click_action_button("Delete"); + cy.click_modal_primary_button("Yes"); + }); +}); diff --git a/cypress/integration/form.js b/cypress/integration/form.js new file mode 100644 index 0000000..56bca59 --- /dev/null +++ b/cypress/integration/form.js @@ -0,0 +1,171 @@ +const jump_to_field = (field_label) => { + cy.get("body") + .type("{esc}") // lose focus if any + .type("{ctrl+j}") // jump to field + .type(field_label) + .wait(500) + .type("{enter}") + .wait(200) + .type("{enter}") + .wait(500); +}; + +const type_value = (value) => { + cy.focused().clear().type(value).type("{esc}"); +}; + +context("Form", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_contact_records"); + }); + }); + + beforeEach(() => { + cy.login(); + cy.visit("/app/website"); + }); + + it("create a new form", () => { + cy.visit("/app/todo/new"); + cy.get_field("description", "Text Editor") + .type("this is a test todo", { force: true }) + .wait(1000); + cy.get(".page-title").should("contain", "Not Saved"); + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.form.save.savedocs", + }).as("form_save"); + cy.get(".primary-action").click(); + cy.wait("@form_save").its("response.statusCode").should("eq", 200); + + cy.go_to_list("ToDo"); + cy.clear_filters(); + cy.get(".page-head").findByTitle("To Do").should("exist"); + cy.get(".list-row").should("contain", "this is a test todo"); + }); + + it("navigates between documents with child table list filters applied", () => { + cy.visit("/app/contact"); + + cy.clear_filters(); + cy.get('.standard-filter-section [data-fieldname="name"] input') + .type("Test Form Contact 3") + .blur(); + cy.click_listview_row_item_with_text("Test Form Contact 3"); + + cy.scrollTo(0); + cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist"); + cy.get(".prev-doc").should("be.visible").click(); + cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible"); + cy.hide_dialog(); + + cy.scrollTo(0); + cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist"); + cy.get(".next-doc").should("be.visible").click(); + cy.get(".msgprint-dialog .modal-body").contains("No further records").should("be.visible"); + cy.hide_dialog(); + + cy.get("#page-Contact .page-head").findByTitle("Test Form Contact 3").should("exist"); + + // clear filters + cy.visit("/app/contact"); + cy.clear_filters(); + }); + + it("validates behaviour of Data options validations in child table", () => { + // test email validations for set_invalid controller + let website_input = "website.in"; + let valid_email = "user@email.com"; + let expectBackgroundColor = "rgb(255, 245, 245)"; + + cy.visit("/app/contact/new"); + cy.get('.xhiveframework-control[data-fieldname="email_ids"]').as("table"); + cy.get("@table").find("button.grid-add-row").click(); + cy.get("@table").find("button.grid-add-row").click(); + cy.get("@table").find('[data-idx="1"]').as("row1"); + cy.get("@table").find('[data-idx="2"]').as("row2"); + cy.get("@row1").click(); + cy.get("@row1").find("input.input-with-feedback.form-control").as("email_input1"); + + cy.get("@email_input1").type(website_input, { waitForAnimations: false }); + cy.fill_field("company_name", "Test Company"); + + cy.get("@row2").click(); + cy.get("@row2").find("input.input-with-feedback.form-control").as("email_input2"); + cy.get("@email_input2").type(valid_email, { waitForAnimations: false }); + + cy.get("@row1").click(); + cy.get("@email_input1").should("have.class", "invalid"); + + cy.get("@row2").click(); + cy.get("@email_input2").should("not.have.class", "invalid"); + }); + + it("Jump to field in collapsed section", { scrollBehavior: false }, () => { + cy.new_form("User"); + + jump_to_field("Location"); // this is in collapsed section + type_value("Bermuda"); + + cy.get_field("location").should("have.value", "Bermuda"); + }); + + it("update docfield property using set_df_property in child table", () => { + cy.visit("/app/contact/Test Form Contact 1"); + cy.window() + .its("cur_frm") + .then((frm) => { + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + + // set property before form_render event of child table + cy.get("@table") + .find('[data-idx="1"]') + .invoke("attr", "data-name") + .then((cdn) => { + frm.set_df_property( + "phone_nos", + "hidden", + 1, + "Contact Phone", + "is_primary_phone", + cdn + ); + }); + + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_phone"]') + .should("be.hidden"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + + // set property on form_render event of child table + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get("@table") + .find('[data-idx="1"]') + .invoke("attr", "data-name") + .then((cdn) => { + frm.set_df_property( + "phone_nos", + "hidden", + 0, + "Contact Phone", + "is_primary_phone", + cdn + ); + }); + + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_phone"]') + .should("be.visible"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + }); + }); +}); diff --git a/cypress/integration/form_builder.js b/cypress/integration/form_builder.js new file mode 100644 index 0000000..e7972c6 --- /dev/null +++ b/cypress/integration/form_builder.js @@ -0,0 +1,282 @@ +import form_builder_doctype from "../fixtures/form_builder_doctype"; +const doctype_name = form_builder_doctype.name; +context("Form Builder", () => { + before(() => { + cy.login(); + cy.visit("/app"); + return cy.insert_doc("DocType", form_builder_doctype, true); + }); + + it("Open Form Builder for Web Form Doctype/Customize Form", () => { + // doctype + cy.visit("/app/doctype/Web Form"); + cy.findByRole("tab", { name: "Form" }).click(); + cy.get(".form-builder-container").should("exist"); + + // customize form + cy.visit("/app/customize-form?doc_type=Web%20Form"); + cy.findByRole("tab", { name: "Form" }).click(); + cy.get(".form-builder-container").should("exist"); + }); + + it("Save without change, check form dirty", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + // Save without change + cy.click_doc_primary_button("Save"); + cy.get(".desk-alert.orange .alert-message").should("have.text", "No changes in document"); + + // Check form dirty + cy.get(".tab-content.active .section-columns-container:first .column:first .field:first") + .find("div[title='Double click to edit label']") + .dblclick() + .type("Dirty"); + cy.get(".title-area .indicator-pill.orange").should("have.text", "Not Saved"); + }); + + it("Add empty section and save", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_section = ".tab-content.active .form-section-container:first"; + + // add new section + cy.get(first_section).click(15, 10); + cy.get(first_section).find(".dropdown-btn:first").click(); + cy.get(".dropdown-options:visible .dropdown-item:first").click(); + + // save + cy.click_doc_primary_button("Save"); + cy.get(".tab-content.active .form-section-container").should("have.length", 1); + }); + + it("Add Table field and check if columns are rendered", () => { + cy.intercept("POST", "/api/method/xhiveframework.desk.search.search_link").as("search_link"); + + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_column = ".tab-content.active .section-columns-container:first .column:first"; + + let last_field = first_column + " .field:last"; + + let add_new_field_btn = first_column + " .add-new-field-btn button"; + + // add new field + cy.get(add_new_field_btn).click(); + + // type table and press enter + cy.get(".combo-box-options:visible .search-box > input").type("table{enter}"); + + // save + cy.click_doc_primary_button("Save"); + + // Validate if options is not set + cy.get_open_dialog().find(".msgprint").should("contain", "Options is required"); + cy.hide_dialog(); + + cy.get(last_field).click({ force: true }); + + cy.get(".sidebar-container .xhiveframework-control[data-fieldname='options'] input") + .click() + .as("input"); + cy.get("@input").clear({ force: true }).type("Web Form Field", { delay: 200 }); + cy.wait("@search_link"); + + cy.get(last_field).click({ force: true }); + + cy.get(last_field).find(".table-controls .table-column").contains("Field").should("exist"); + cy.get(last_field) + .find(".table-controls .table-column") + .contains("Fieldtype") + .should("exist"); + + // validate In List View + cy.get(".sidebar-container .field label .label-area").contains("In List View").click(); + + // save + cy.click_doc_primary_button("Save"); + + cy.get_open_dialog().find(".msgprint").should("contain", "In List View"); + cy.hide_dialog(); + + cy.get(last_field).click({ force: true }); + cy.get(".sidebar-container .field label .label-area").contains("In List View").click(); + + // validate In Global Search + cy.get(".sidebar-container .field label .label-area").contains("In Global Search").click(); + // save + cy.click_doc_primary_button("Save"); + + cy.get_open_dialog().find(".msgprint").should("contain", "In Global Search"); + }); + // not important and was flaky on CI + it.skip("Drag Field/Column/Section & Tab", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_column = ".tab-content.active .section-columns-container:first .column:first"; + let first_field = first_column + " .field:first"; + let label = "div[title='Double click to edit label'] span:first"; + + cy.get(".tab-header .tabs .tab:first").click(); + + // drag first tab to second position + cy.get(".tab-header .tabs .tab:first").drag(".tab-header .tabs .tab:nth-child(2)", { + target: { x: 10, y: 10 }, + force: true, + }); + cy.get(".tab-header .tabs .tab:first").find(label).should("have.text", "Tab 2"); + + cy.get(".tab-header .tabs .tab:first").click(); + cy.get(".sidebar-container .tab:first").click(); + + // drag check field to first column + cy.get(".fields-container .field[title='Check']").drag(first_field, { + target: { x: 100, y: 10 }, + }); + cy.get(first_column).find(".field").should("have.length", 3); + + cy.get(first_field) + .find("div[title='Double click to edit label']") + .dblclick() + .type("Test Check"); + cy.get(first_field).find(label).should("have.text", "Test Check"); + + // drag the first field to second position + cy.get(first_field).drag(first_column + " .field:nth-child(2)", { + target: { x: 100, y: 10 }, + }); + cy.get(first_field).find(label).should("have.text", "Data"); + + // drag first column to second position + cy.get(first_column).click().wait(200); + cy.get(first_column) + .find(".column-actions") + .drag(".section-columns-container:first .column:last", { + target: { x: 100, y: 10 }, + force: true, + }); + cy.get(first_field).find(label).should("have.text", "Data 1"); + + let first_section = ".tab-content.active .form-section-container:first"; + + // drag first section to second position + cy.get(first_section).click().wait(200); + cy.get(first_section) + .find(".section-header") + .drag(".form-section-container:nth-child(2)", { + target: { x: 100, y: 10 }, + force: true, + }); + cy.get(first_field).find(label).should("have.text", "Data 2"); + }); + + it("Add New Tab/Section/Column to Form", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_section = ".tab-content.active .form-section-container:first"; + + // add new tab + cy.get(".tab-header").realHover().find(".tab-actions .new-tab-btn").click(); + cy.get(".tab-header .tabs .tab").should("have.length", 3); + + // add new section + cy.get(first_section).click(15, 10); + cy.get(first_section).find(".dropdown-btn:first").click(); + cy.get(".dropdown-options:visible .dropdown-item:first").click(); + cy.get(".tab-content.active .form-section-container").should("have.length", 2); + + // add new column + cy.get(first_section).click(15, 10); + cy.get(first_section).find(".dropdown-btn:first").click(); + cy.get(".dropdown-options:visible .dropdown-item:last").click(); + cy.get(first_section).find(".column").should("have.length", 2); + }); + + it("Remove Tab/Section/Column", () => { + let first_section = ".tab-content.active .form-section-container:first"; + + // remove column + cy.get(first_section).click(15, 10); + cy.get(first_section).find(".dropdown-btn:first").click(); + cy.get(".dropdown-options:visible .dropdown-item:last").click(); + cy.get(first_section).find(".column").should("have.length", 1); + + // remove section + cy.get(first_section).click(15, 10); + cy.get(first_section).find(".dropdown-btn:first").click(); + cy.get(".dropdown-options:visible .dropdown-item").eq(1).click(); + cy.get(".tab-content.active .form-section-container").should("have.length", 1); + + // remove tab + cy.get(".tab-header .tab:last").realHover().find(".remove-tab-btn").click(); + cy.get(".tab-header .tabs .tab").should("have.length", 2); + }); + + it("Update Title field Label to New Title through Customize Form", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_field = + ".tab-content.active .section-columns-container:first .column:first .field:first"; + + cy.get(first_field) + .find("div[title='Double click to edit label']") + .dblclick() + .type("{selectall}New Title"); + + cy.findByRole("button", { name: "Save" }).click({ force: true }); + + cy.visit("/app/form-builder-doctype/new"); + cy.get("[data-fieldname='data3'] .clearfix label").should("have.text", "New Title"); + }); + + it("Validate Duplicate Name & reqd + hidden without default logic", () => { + cy.visit(`/app/doctype/${doctype_name}`); + cy.findByRole("tab", { name: "Form" }).click(); + + let first_column = ".tab-content.active .section-columns-container:first .column:first"; + + let last_field = first_column + " .field:last"; + + let add_new_field_btn = first_column + " .add-new-field-btn button"; + + // add new field + cy.get(add_new_field_btn).click(); + + // type data and press enter + cy.get(".combo-box-options:visible .search-box > input").type("data{enter}"); + + cy.get(last_field).click(); + + // validate duplicate name + cy.get(".sidebar-container .xhiveframework-control[data-fieldname='fieldname'] input") + .click() + .as("input"); + cy.get(".sidebar-container .xhiveframework-control[data-fieldname='fieldname'] input") + .clear({ force: true }) + .type("data3"); + + cy.click_doc_primary_button("Save"); + cy.get_open_dialog().find(".msgprint").should("contain", "appears multiple times"); + cy.hide_dialog(); + cy.get(last_field).click(); + cy.get(".sidebar-container .xhiveframework-control[data-fieldname='fieldname'] input").clear({ + force: true, + }); + + // validate reqd + hidden without default + cy.get(".sidebar-container .field label .label-area").contains("Mandatory").click(); + cy.get(".sidebar-container .field label .label-area").contains("Hidden").click(); + + // save + cy.click_doc_primary_button("Save"); + + cy.get_open_dialog() + .find(".msgprint") + .should("contain", "cannot be hidden and mandatory without any default value"); + }); +}); diff --git a/cypress/integration/form_tab_break.js b/cypress/integration/form_tab_break.js new file mode 100644 index 0000000..91695cb --- /dev/null +++ b/cypress/integration/form_tab_break.js @@ -0,0 +1,30 @@ +import doctype_with_tab_break from "../fixtures/doctype_with_tab_break"; +const doctype_name = doctype_with_tab_break.name; +context("Form Tab Break", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy.insert_doc("DocType", doctype_with_tab_break, true); + }); + it("Should switch tab and open correct tabs on validation error", () => { + cy.new_form(doctype_name); + // test tab switch + cy.findByRole("tab", { name: "Tab 2" }).click(); + cy.findByText("Phone"); + cy.findByRole("tab", { name: "Details" }).click(); + cy.findByText("Name"); + + // form should switch to the tab with un-filled mandatory field + cy.fill_field("username", "Test"); + cy.findByRole("button", { name: "Save" }).click(); + cy.findByText("Missing Fields"); + cy.hide_dialog(); + cy.findByText("Phone"); + cy.fill_field("phone", "12345678"); + cy.findByRole("button", { name: "Save" }).click(); + + // After save, first tab should have dashboard + cy.get(".form-tabs > .nav-item").eq(0).click(); + cy.findByText("Connections"); + }); +}); diff --git a/cypress/integration/form_tour.js b/cypress/integration/form_tour.js new file mode 100644 index 0000000..a187c85 --- /dev/null +++ b/cypress/integration/form_tour.js @@ -0,0 +1,94 @@ +context.skip("Form Tour", () => { + before(() => { + cy.login(); + cy.visit("/app"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_form_tour"); + }); + }); + + const open_test_form_tour = () => { + cy.visit("/app/form-tour/Test Form Tour"); + cy.findByRole("button", { name: "Show Tour" }).should("be.visible").as("show_tour"); + cy.get("@show_tour").click(); + cy.wait(500); + cy.url().should("include", "/app/contact"); + }; + + it("jump to a form tour", open_test_form_tour); + + it("navigates a form tour", () => { + open_test_form_tour(); + + cy.get(".xhiveframework-driver").should("be.visible"); + cy.get('.xhiveframework-control[data-fieldname="first_name"]').as("first_name"); + cy.get("@first_name").should("have.class", "driver-highlighted-element"); + cy.get(".xhiveframework-driver").findByRole("button", { name: "Next" }).as("next_btn"); + + // next btn shouldn't move to next step, if first name is not entered + cy.get("@next_btn").click(); + cy.wait(500); + cy.get("@first_name").should("have.class", "driver-highlighted-element"); + + // after filling the field, next step should be highlighted + cy.fill_field("first_name", "Test Name", "Data"); + cy.wait(500); + cy.get("@next_btn").click(); + cy.wait(500); + + // assert field is highlighted + cy.get('.xhiveframework-control[data-fieldname="last_name"]').as("last_name"); + cy.get("@last_name").should("have.class", "driver-highlighted-element"); + + // after filling the field, next step should be highlighted + cy.fill_field("last_name", "Test Last Name", "Data"); + cy.wait(500); + cy.get("@next_btn").click(); + cy.wait(500); + + // assert field is highlighted + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("phone_nos"); + cy.get("@phone_nos").should("have.class", "driver-highlighted-element"); + + // move to next step + cy.wait(500); + cy.get("@next_btn").click(); + cy.wait(500); + + // assert add row btn is highlighted + cy.get("@phone_nos").find(".grid-add-row").as("add_row"); + cy.get("@add_row").should("have.class", "driver-highlighted-element"); + + // add a row & move to next step + cy.wait(500); + cy.get("@add_row").click(); + cy.wait(500); + + // assert table field is highlighted + cy.get('.grid-row-open .xhiveframework-control[data-fieldname="phone"]').as("phone"); + cy.get("@phone").should("have.class", "driver-highlighted-element"); + // enter value in a table field + let field = cy.fill_table_field("phone_nos", "1", "phone", "1234567890"); + field.blur(); + + // move to collapse row step + cy.wait(500); + cy.get(".driver-popover-title") + .contains("Test Title 4") + .siblings() + .get("@next_btn") + .click(); + cy.wait(500); + // collapse row + cy.get(".grid-row-open .grid-collapse-row").click(); + cy.wait(500); + + // assert save btn is highlighted + cy.get(".primary-action").should("have.class", "driver-highlighted-element"); + cy.wait(500); + cy.get(".xhiveframework-driver").findByRole("button", { name: "Save" }).should("be.visible"); + }); +}); diff --git a/cypress/integration/grid.js b/cypress/integration/grid.js new file mode 100644 index 0000000..3afac95 --- /dev/null +++ b/cypress/integration/grid.js @@ -0,0 +1,114 @@ +context("Grid", () => { + beforeEach(() => { + cy.login(); + cy.visit("/app/website"); + }); + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call( + "xhiveframework.tests.ui_test_helpers.create_contact_phone_nos_records" + ); + }); + }); + it("update docfield property using update_docfield_property", () => { + cy.visit("/app/contact/Test Contact"); + cy.window() + .its("cur_frm") + .then((frm) => { + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + let field = frm.get_field("phone_nos"); + field.grid.update_docfield_property("is_primary_phone", "hidden", true); + + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_phone"]') + .should("be.hidden"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + + cy.get("@table").find('[data-idx="2"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_phone"]') + .should("be.hidden"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + }); + }); + it("update docfield property using toggle_display", () => { + cy.visit("/app/contact/Test Contact"); + cy.window() + .its("cur_frm") + .then((frm) => { + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + let field = frm.get_field("phone_nos"); + field.grid.toggle_display("is_primary_mobile_no", false); + + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_mobile_no"]') + .should("be.hidden"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + + cy.get("@table").find('[data-idx="2"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="is_primary_mobile_no"]') + .should("be.hidden"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + }); + }); + it("update docfield property using toggle_enable", () => { + cy.visit("/app/contact/Test Contact"); + cy.window() + .its("cur_frm") + .then((frm) => { + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + let field = frm.get_field("phone_nos"); + field.grid.toggle_enable("phone", false); + + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="phone"] .control-value') + .should("have.class", "like-disabled-input"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + + cy.get("@table").find('[data-idx="2"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get("@table-form") + .find('.xhiveframework-control[data-fieldname="phone"] .control-value') + .should("have.class", "like-disabled-input"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + }); + }); + it("update docfield property using toggle_reqd", () => { + cy.visit("/app/contact/Test Contact"); + cy.window() + .its("cur_frm") + .then((frm) => { + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + let field = frm.get_field("phone_nos"); + field.grid.toggle_reqd("phone", false); + + cy.get("@table").find('[data-idx="1"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get_field("phone").as("phone-field"); + cy.get("@phone-field").focus().clear().wait(500).blur(); + cy.get("@phone-field").should("not.have.class", "has-error"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + + cy.get("@table").find('[data-idx="2"] .btn-open-row').click(); + cy.get(".grid-row-open").as("table-form"); + cy.get_field("phone").as("phone-field"); + cy.get("@phone-field").focus().clear().wait(500).blur(); + cy.get("@phone-field").should("not.have.class", "has-error"); + cy.get("@table-form").find(".grid-footer-toolbar").click(); + }); + }); +}); diff --git a/cypress/integration/grid_configuration.js b/cypress/integration/grid_configuration.js new file mode 100644 index 0000000..d130005 --- /dev/null +++ b/cypress/integration/grid_configuration.js @@ -0,0 +1,25 @@ +context("Grid Configuration", () => { + beforeEach(() => { + cy.login(); + cy.visit("/app/doctype/User"); + }); + it("Set user wise grid settings", () => { + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get('.form-section[data-fieldname="fields_section"]').click(); + cy.wait(100); + cy.get('.xhiveframework-control[data-fieldname="fields"]').as("table"); + cy.get("@table").find(".icon-sm").click(); + cy.wait(100); + cy.get('.xhiveframework-control[data-fieldname="fields_html"]').as("modal"); + cy.get("@modal").find(".add-new-fields").click(); + cy.wait(100); + cy.get('[type="checkbox"][data-unit="read_only"]').check(); + cy.findByRole("button", { name: "Add" }).click(); + cy.wait(100); + cy.get('[data-fieldname="options"]').invoke("attr", "value", "1"); + cy.get('.form-control.column-width[data-fieldname="options"]').trigger("change"); + cy.findByRole("button", { name: "Update" }).click(); + cy.wait(200); + cy.get('[title="Read Only"').should("be.visible"); + }); +}); diff --git a/cypress/integration/grid_keyboard_shortcut.js b/cypress/integration/grid_keyboard_shortcut.js new file mode 100644 index 0000000..45f6dc6 --- /dev/null +++ b/cypress/integration/grid_keyboard_shortcut.js @@ -0,0 +1,54 @@ +context("Grid Keyboard Shortcut", () => { + let total_count = 0; + let contact_email_name = null; + before(() => { + cy.login(); + }); + beforeEach(() => { + cy.reload(); + cy.new_form("Contact"); + cy.get('.xhiveframework-control[data-fieldname="email_ids"]').find(".grid-add-row").click(); + // as new names uses hash instead of numbers get row's data-name dynamically. + cy.get('.xhiveframework-control[data-fieldname="email_ids"]') + .find(".grid-body .grid-row") + .should(($row) => { + contact_email_name = $row.attr("data-name"); + }); + }); + it("Insert new row at the end", () => { + cy.add_new_row_in_grid( + "{ctrl}{shift}{downarrow}", + (cy, total_count) => { + cy.get(`[data-name="${contact_email_name}"]`).should( + "have.attr", + "data-idx", + `${total_count + 1}` + ); + }, + total_count + ); + }); + it("Insert new row at the top", () => { + cy.add_new_row_in_grid("{ctrl}{shift}{uparrow}", (cy) => { + cy.get(`[data-name="${contact_email_name}"]`).should("have.attr", "data-idx", "2"); + }); + }); + it("Insert new row below", () => { + cy.add_new_row_in_grid("{ctrl}{downarrow}", (cy) => { + cy.get(`[data-name^="${contact_email_name}"]`).should("have.attr", "data-idx", "1"); + }); + }); + it("Insert new row above", () => { + cy.add_new_row_in_grid("{ctrl}{uparrow}", (cy) => { + cy.get(`[data-name^="${contact_email_name}"]`).should("have.attr", "data-idx", "2"); + }); + }); +}); + +Cypress.Commands.add("add_new_row_in_grid", (shortcut_keys, callbackFn, total_count) => { + cy.get('.xhiveframework-control[data-fieldname="email_ids"]').as("table"); + cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().click(); + cy.get("@table").find('.grid-body [data-fieldname="email_id"]').first().type(shortcut_keys); + + callbackFn(cy, total_count); +}); diff --git a/cypress/integration/grid_pagination.js b/cypress/integration/grid_pagination.js new file mode 100644 index 0000000..efab8aa --- /dev/null +++ b/cypress/integration/grid_pagination.js @@ -0,0 +1,80 @@ +context("Grid Pagination", () => { + beforeEach(() => { + cy.login(); + cy.visit("/app/website"); + }); + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call( + "xhiveframework.tests.ui_test_helpers.create_contact_phone_nos_records" + ); + }); + }); + it("creates pages for child table", () => { + cy.visit("/app/contact/Test Contact"); + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + cy.get("@table").find(".current-page-number").should("have.value", "1"); + cy.get("@table").find(".total-page-number").should("contain", "20"); + cy.get("@table").find(".grid-body .grid-row").should("have.length", 50); + }); + it("goes to the next and previous page", () => { + cy.visit("/app/contact/Test Contact"); + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + cy.get("@table").find(".next-page").click(); + cy.get("@table").find(".current-page-number").should("have.value", "2"); + cy.get("@table") + .find(".grid-body .grid-row") + .first() + .should("have.attr", "data-idx", "51"); + cy.get("@table").find(".prev-page").click(); + cy.get("@table").find(".current-page-number").should("have.value", "1"); + cy.get("@table").find(".grid-body .grid-row").first().should("have.attr", "data-idx", "1"); + }); + it("adds and deletes rows and changes page", () => { + cy.visit("/app/contact/Test Contact"); + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + cy.get("@table").findByRole("button", { name: "Add Row" }).click(); + cy.get("@table").find(".grid-body .row-index").should("contain", 1001); + cy.get("@table").find(".current-page-number").should("have.value", "21"); + cy.get("@table").find(".total-page-number").should("contain", "21"); + cy.get("@table").find(".grid-body .grid-row .grid-row-check").click({ force: true }); + cy.get("@table").findByRole("button", { name: "Delete" }).click(); + cy.get("@table").find(".grid-body .row-index").last().should("contain", 1000); + cy.get("@table").find(".current-page-number").should("have.value", "20"); + cy.get("@table").find(".total-page-number").should("contain", "20"); + }); + it("go to specific page, use up and down arrow, type characters, 0 page and more than existing page", () => { + cy.visit("/app/contact/Test Contact"); + cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as("table"); + cy.get("@table").find(".current-page-number").focus().clear().type("17").blur(); + cy.get("@table").find(".grid-body .row-index").should("contain", 801); + + cy.get("@table").find(".current-page-number").focus().type("{uparrow}{uparrow}"); + cy.get("@table").find(".current-page-number").should("have.value", "19"); + + cy.get("@table").find(".current-page-number").focus().type("{downarrow}{downarrow}"); + cy.get("@table").find(".current-page-number").should("have.value", "17"); + + cy.get("@table").find(".current-page-number").focus().clear().type("700").blur(); + cy.get("@table").find(".current-page-number").should("have.value", "20"); + + cy.get("@table").find(".current-page-number").focus().clear().type("0").blur(); + cy.get("@table").find(".current-page-number").should("have.value", "1"); + + cy.get("@table").find(".current-page-number").focus().clear().type("abc").blur(); + cy.get("@table").find(".current-page-number").should("have.value", "1"); + }); + // it('deletes all rows', ()=> { + // cy.visit('/app/contact/Test Contact'); + // cy.get('.xhiveframework-control[data-fieldname="phone_nos"]').as('table'); + // cy.get('@table').find('.grid-heading-row .grid-row-check').click({force: true}); + // cy.get('@table').find('button.grid-remove-all-rows').click(); + // cy.get('.modal-dialog .btn-primary').contains('Yes').click(); + // cy.get('@table').find('.grid-body .grid-row').should('have.length', 0); + // }); +}); diff --git a/cypress/integration/grid_search.js b/cypress/integration/grid_search.js new file mode 100644 index 0000000..82774f3 --- /dev/null +++ b/cypress/integration/grid_search.js @@ -0,0 +1,133 @@ +import doctype_with_child_table from "../fixtures/doctype_with_child_table"; +import child_table_doctype from "../fixtures/child_table_doctype"; +import child_table_doctype_1 from "../fixtures/child_table_doctype_1"; +const doctype_with_child_table_name = doctype_with_child_table.name; + +context("Grid Search", () => { + before(() => { + cy.visit("/login"); + cy.login(); + cy.visit("/app/website"); + cy.insert_doc("DocType", child_table_doctype, true); + cy.insert_doc("DocType", child_table_doctype_1, true); + cy.insert_doc("DocType", doctype_with_child_table, true); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall( + "xhiveframework.tests.ui_test_helpers.insert_doctype_with_child_table_record", + { + name: doctype_with_child_table_name, + } + ); + }); + }); + + it("Test search row visibility", () => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.model.user_settings.save("Doctype With Child Table", "GridView", { + "Child Table Doctype 1": [ + { fieldname: "data", columns: 2 }, + { fieldname: "barcode", columns: 1 }, + { fieldname: "check", columns: 1 }, + { fieldname: "rating", columns: 2 }, + { fieldname: "duration", columns: 2 }, + { fieldname: "date", columns: 2 }, + ], + }); + }); + + cy.visit(`/app/doctype-with-child-table/Test Grid Search`); + + cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table"); + cy.get("@table").find(".grid-row-check:last").click(); + cy.get("@table").find(".grid-footer").contains("Delete").click(); + cy.get(".grid-heading-row .grid-row .search").should("not.exist"); + }); + + it("test search field for different fieldtypes", () => { + cy.visit(`/app/doctype-with-child-table/Test Grid Search`); + + cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table"); + + // Index Column + cy.get("@table").find(".grid-heading-row .row-index.search input").type("3"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2); + cy.get("@table").find(".grid-heading-row .row-index.search input").clear(); + + // Data Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Data"]') + .type("Data"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 1); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').clear(); + + // Barcode Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Barcode"]') + .type("092"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Barcode"]').clear(); + + // Check Column + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("1"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 9); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear(); + + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').type("0"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 11); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Check"]').clear(); + + // Rating Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Rating"]') + .type("3"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Rating"]').clear(); + + // Duration Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Duration"]') + .type("3d"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 3); + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Duration"]') + .clear(); + + // Date Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Date"]') + .type("2022"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 4); + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Date"]').clear(); + }); + + it("test with multiple filter", () => { + cy.get('.xhiveframework-control[data-fieldname="child_table_1"]').as("table"); + + // Data Column + cy.get("@table").find('.grid-heading-row .search input[data-fieldtype="Data"]').type("a"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 10); + + // Barcode Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Barcode"]') + .type("0"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 8); + + // Duration Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Duration"]') + .type("d"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 5); + + // Date Column + cy.get("@table") + .find('.grid-heading-row .search input[data-fieldtype="Date"]') + .type("02-"); + cy.get("@table").find(".grid-body .rows .grid-row").should("have.length", 2); + }); +}); diff --git a/cypress/integration/kanban.js b/cypress/integration/kanban.js new file mode 100644 index 0000000..45f76d5 --- /dev/null +++ b/cypress/integration/kanban.js @@ -0,0 +1,134 @@ +context("Kanban Board", () => { + before(() => { + cy.login("xhiveframework@example.com"); + cy.visit("/app"); + }); + + it("Create ToDo Kanban", () => { + cy.visit("/app/todo"); + + cy.get(".page-actions .custom-btn-group button").click(); + cy.get(".page-actions .custom-btn-group ul.dropdown-menu li").contains("Kanban").click(); + + cy.focused().blur(); + cy.fill_field("board_name", "ToDo Kanban", "Data"); + cy.fill_field("field_name", "Status", "Select"); + cy.click_modal_primary_button("Save"); + + cy.get(".title-text").should("contain", "ToDo Kanban"); + }); + + it("Create ToDo from kanban", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.client.save", + }).as("save-todo"); + + cy.click_listview_primary_button("Add ToDo"); + + cy.fill_field("description", "Test Kanban ToDo", "Text Editor").wait(300); + cy.get(".modal-footer .btn-primary").last().click(); + + cy.wait("@save-todo"); + }); + + it("Add and Remove fields", () => { + cy.visit("/app/todo/view/kanban/ToDo Kanban"); + + cy.intercept( + "POST", + "/api/method/xhiveframework.desk.doctype.kanban_board.kanban_board.save_settings" + ).as("save-kanban"); + cy.intercept( + "POST", + "/api/method/xhiveframework.desk.doctype.kanban_board.kanban_board.update_order" + ).as("update-order"); + + cy.get(".page-actions .menu-btn-group > .btn").click(); + cy.get(".page-actions .menu-btn-group .dropdown-menu li") + .contains("Kanban Settings") + .click(); + cy.get(".add-new-fields").click(); + + cy.get(".checkbox-options .checkbox").contains("ID").click(); + cy.get(".checkbox-options .checkbox").contains("Status").first().click(); + cy.get(".checkbox-options .checkbox").contains("Priority").click(); + + cy.get(".modal-footer .btn-primary").last().click(); + + cy.get(".xhiveframework-control .label-area").contains("Show Labels").click(); + cy.click_modal_primary_button("Save"); + + cy.wait("@save-kanban"); + + cy.get('.kanban-column[data-column-value="Open"] .kanban-cards').as("open-cards"); + cy.get("@open-cards") + .find(".kanban-card .kanban-card-doc") + .first() + .should("contain", "ID:"); + cy.get("@open-cards") + .find(".kanban-card .kanban-card-doc") + .first() + .should("contain", "Status:"); + cy.get("@open-cards") + .find(".kanban-card .kanban-card-doc") + .first() + .should("contain", "Priority:"); + + cy.get(".page-actions .menu-btn-group > .btn").click(); + cy.get(".page-actions .menu-btn-group .dropdown-menu li") + .contains("Kanban Settings") + .click(); + cy.get_open_dialog() + .find( + '.xhiveframework-control[data-fieldname="fields_html"] div[data-label="ID"] .remove-field' + ) + .click(); + + cy.wait("@update-order"); + cy.get_open_dialog().find(".xhiveframework-control .label-area").contains("Show Labels").click(); + cy.get(".modal-footer .btn-primary").last().click(); + + cy.wait("@save-kanban"); + + cy.get("@open-cards") + .find(".kanban-card .kanban-card-doc") + .first() + .should("not.contain", "ID:"); + }); + + it("Checks if Kanban Board edits are blocked for non-System Manager and non-owner of the Board", () => { + cy.switch_to_user("Administrator"); + + const not_system_manager = "nosysmanager@example.com"; + cy.call("xhiveframework.tests.ui_test_helpers.create_test_user", { + username: not_system_manager, + }); + cy.remove_role(not_system_manager, "System Manager"); + cy.call("xhiveframework.tests.ui_test_helpers.create_todo", { description: "XhiveFramework User ToDo" }); + cy.call("xhiveframework.tests.ui_test_helpers.create_admin_kanban"); + + cy.switch_to_user(not_system_manager); + + cy.visit("/app/todo/view/kanban/Admin Kanban"); + + // Menu button should be hidden (dropdown for 'Save Filters' and 'Delete Kanban Board') + cy.get(".no-list-sidebar .menu-btn-group .btn-default[data-original-title='Menu']").should( + "have.length", + 0 + ); + // Kanban Columns should be visible (read-only) + cy.get(".kanban .kanban-column").should("have.length", 2); + // User should be able to add card (has access to ToDo) + cy.get(".kanban .add-card").should("have.length", 2); + // Column actions should be hidden (dropdown for 'Archive' and indicators) + cy.get(".kanban .column-options").should("have.length", 0); + + cy.switch_to_user("Administrator"); + cy.call("xhiveframework.client.delete", { doctype: "User", name: not_system_manager }); + }); + + after(() => { + cy.call("logout"); + }); +}); diff --git a/cypress/integration/list_paging.js b/cypress/integration/list_paging.js new file mode 100644 index 0000000..187bb93 --- /dev/null +++ b/cypress/integration/list_paging.js @@ -0,0 +1,42 @@ +context("List Paging", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_multiple_todo_records"); + }); + }); + + it("test load more with count selection buttons", () => { + cy.visit("/app/todo/view/report"); + cy.get(".filter-x-button").click(); + + cy.get(".list-paging-area .list-count").should("contain.text", "20 of"); + cy.get(".list-paging-area .btn-more").click(); + cy.get(".list-paging-area .list-count").should("contain.text", "40 of"); + cy.get(".list-paging-area .btn-more").click(); + cy.get(".list-paging-area .list-count").should("contain.text", "60 of"); + + cy.get('.list-paging-area .btn-group .btn-paging[data-value="100"]').click(); + + cy.get(".list-paging-area .list-count").should("contain.text", "100 of"); + cy.get(".list-paging-area .btn-more").click(); + cy.get(".list-paging-area .list-count").should("contain.text", "200 of"); + cy.get(".list-paging-area .btn-more").click(); + cy.get(".list-paging-area .list-count").should("contain.text", "300 of"); + + // check if refresh works after load more + cy.get('.page-head .standard-actions [data-original-title="Reload List"]').click(); + cy.get(".list-paging-area .list-count").should("contain.text", "300 of"); + + cy.get('.list-paging-area .btn-group .btn-paging[data-value="500"]').click(); + + cy.get(".list-paging-area .list-count").should("contain.text", "500 of"); + cy.get(".list-paging-area .btn-more").click(); + + cy.get(".list-paging-area .list-count").should("contain.text", "1,000 of"); + }); +}); diff --git a/cypress/integration/list_view.js b/cypress/integration/list_view.js new file mode 100644 index 0000000..874b197 --- /dev/null +++ b/cypress/integration/list_view.js @@ -0,0 +1,55 @@ +context("List View", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.setup_workflow"); + }); + }); + + it("Keep checkbox checked after Refresh", { scrollBehavior: false }, () => { + cy.go_to_list("ToDo"); + cy.clear_filters(); + cy.get(".list-header-subject > .list-subject > .list-check-all").click(); + cy.get("button[data-original-title='Reload List']").click(); + cy.get(".list-row-container .list-row-checkbox:checked").should("be.visible"); + }); + + it('enables "Actions" button', { scrollBehavior: false }, () => { + const actions = [ + "Approve", + "Reject", + "Edit", + "Export", + "Assign To", + "Apply Assignment Rule", + "Add Tags", + "Print", + "Delete", + ]; + cy.go_to_list("ToDo"); + cy.clear_filters(); + cy.get(".list-header-subject > .list-subject > .list-check-all").click(); + cy.findByRole("button", { name: "Actions" }).click(); + cy.get(".dropdown-menu li:visible .dropdown-item") + .should("have.length", 9) + .each((el, index) => { + cy.wrap(el).contains(actions[index]); + }) + .then((elements) => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.model.workflow.bulk_workflow_approval", + }).as("bulk-approval"); + cy.wrap(elements).contains("Approve").click(); + cy.wait("@bulk-approval"); + cy.hide_dialog(); + cy.reload(); + cy.clear_filters(); + cy.get(".list-row-container:visible").should("contain", "Approved"); + }); + }); +}); diff --git a/cypress/integration/list_view_drag_select.js b/cypress/integration/list_view_drag_select.js new file mode 100644 index 0000000..2dcb313 --- /dev/null +++ b/cypress/integration/list_view_drag_select.js @@ -0,0 +1,49 @@ +context("List View", () => { + before(() => { + cy.login(); + cy.go_to_list("DocType"); + }); + + it("List view check rows on drag", () => { + cy.get(".filter-x-button").click(); + cy.get(".list-row-checkbox").then(($checkbox) => { + cy.wrap($checkbox).first().trigger("mousedown"); + cy.get(".level.list-row").each(($ele) => { + cy.wrap($ele).trigger("mousemove"); + }); + cy.document().trigger("mouseup"); + }); + + cy.get(".level.list-row .list-row-checkbox").each(($checkbox) => { + cy.wrap($checkbox).should("be.checked"); + }); + }); + + it("Check all rows are checked", () => { + cy.get(".level.list-row .list-row-checkbox") + .its("length") + .then((len) => { + cy.get(".level-item.list-header-meta") + .should("be.visible") + .should("contain.text", `${len} items selected`); + }); + }); + + it("List view uncheck rows on drag", () => { + cy.get(".list-row-checkbox").then(($checkbox) => { + cy.wrap($checkbox).first().trigger("mousedown"); + cy.get(".level.list-row").each(($ele) => { + cy.wrap($ele).trigger("mousemove"); + }); + cy.document().trigger("mouseup"); + }); + + cy.get(".level.list-row .list-row-checkbox").each(($checkbox) => { + cy.wrap($checkbox).should("not.be.checked"); + }); + }); + + it("Check all rows are unchecked", () => { + cy.get(".level-item.list-header-meta").should("not.be.visible"); + }); +}); diff --git a/cypress/integration/list_view_settings.js b/cypress/integration/list_view_settings.js new file mode 100644 index 0000000..9c66edb --- /dev/null +++ b/cypress/integration/list_view_settings.js @@ -0,0 +1,42 @@ +context("List View Settings", () => { + beforeEach(() => { + cy.login(); + cy.visit("/app/website"); + }); + it("Default settings", () => { + cy.visit("/app/List/DocType/List"); + cy.clear_filters(); + cy.get(".list-count").should("contain", "20 of"); + cy.get(".list-stats").should("contain", "Tags"); + }); + it("disable count and sidebar stats then verify", () => { + cy.wait(300); + cy.visit("/app/List/DocType/List"); + cy.clear_filters(); + cy.wait(300); + cy.get(".list-count").should("contain", "20 of"); + cy.get("[href='#es-line-chat-alt']").should("be.visible"); + cy.get(".menu-btn-group button").click(); + cy.get(".dropdown-menu li").filter(":visible").contains("List Settings").click(); + cy.get(".modal-dialog").should("contain", "DocType Settings"); + + cy.findByLabelText("Disable Count").check({ force: true }); + cy.findByLabelText("Disable Comment Count").check({ force: true }); + cy.findByLabelText("Disable Sidebar Stats").check({ force: true }); + cy.findByRole("button", { name: "Save" }).click(); + + cy.reload({ force: true }); + + cy.get(".list-count").should("be.empty"); + cy.get(".list-sidebar .list-tags").should("not.exist"); + cy.get("[href='#es-line-chat-alt']").should("not.be.visible"); + + cy.get(".menu-btn-group button").click({ force: true }); + cy.get(".dropdown-menu li").filter(":visible").contains("List Settings").click(); + cy.get(".modal-dialog").should("contain", "DocType Settings"); + cy.findByLabelText("Disable Count").uncheck({ force: true }); + cy.findByLabelText("Disable Comment Count").uncheck({ force: true }); + cy.findByLabelText("Disable Sidebar Stats").uncheck({ force: true }); + cy.findByRole("button", { name: "Save" }).click(); + }); +}); diff --git a/cypress/integration/login.js b/cypress/integration/login.js new file mode 100644 index 0000000..aed9b49 --- /dev/null +++ b/cypress/integration/login.js @@ -0,0 +1,67 @@ +context("Login", () => { + beforeEach(() => { + cy.visit("/"); + cy.call("logout"); + cy.visit("/login"); + cy.location("pathname").should("eq", "/login"); + }); + + it("greets with login screen", () => { + cy.get(".page-card-head").contains("Login"); + }); + + it("validates password", () => { + cy.get("#login_email").type("Administrator"); + cy.findByRole("button", { name: "Login" }).click(); + cy.location("pathname").should("eq", "/login"); + }); + + it("validates email", () => { + cy.get("#login_password").type("qwe"); + cy.findByRole("button", { name: "Login" }).click(); + cy.location("pathname").should("eq", "/login"); + }); + + it("shows invalid login if incorrect credentials", () => { + cy.get("#login_email").type("Administrator"); + cy.get("#login_password").type("qwer"); + + cy.findByRole("button", { name: "Login" }).click(); + cy.findByRole("button", { name: "Invalid Login. Try again." }).should("exist"); + cy.location("pathname").should("eq", "/login"); + }); + + it("logs in using correct credentials", () => { + cy.get("#login_email").type("Administrator"); + cy.get("#login_password").type(Cypress.env("adminPassword")); + + cy.findByRole("button", { name: "Login" }).click(); + cy.location("pathname").should("match", /^\/app/); + cy.window().its("xhiveframework.session.user").should("eq", "Administrator"); + }); + + it("check redirect after login", () => { + // mock for OAuth 2.0 client_id, redirect_uri, scope and state + const payload = new URLSearchParams({ + uuid: "6fed1519-cfd8-4a2d-84a6-9a1799c7c741", + encoded_string: "hello all", + encoded_url: "http://test.localhost/callback", + base64_string: "aGVsbG8gYWxs", + }); + + cy.call("logout"); + + // redirect-to /me page with params to mock OAuth 2.0 like request + cy.visit( + "/login?redirect-to=/me?" + encodeURIComponent(payload.toString().replace("+", " ")) + ); + + cy.get("#login_email").type("Administrator"); + cy.get("#login_password").type(Cypress.env("adminPassword")); + + cy.findByRole("button", { name: "Login" }).click(); + + // verify redirected location and url params after login + cy.url().should("include", "/me?" + payload.toString().replace("+", "%20")); + }); +}); diff --git a/cypress/integration/multi_select_dialog.js b/cypress/integration/multi_select_dialog.js new file mode 100644 index 0000000..34632a1 --- /dev/null +++ b/cypress/integration/multi_select_dialog.js @@ -0,0 +1,107 @@ +context("MultiSelectDialog", () => { + before(() => { + cy.login(); + cy.visit("/app"); + const contact_template = { + doctype: "Contact", + first_name: "Test", + status: "Passive", + email_ids: [ + { + doctype: "Contact Email", + email_id: "test@example.com", + is_primary: 0, + }, + ], + }; + const promises = Array.from({ length: 25 }).map(() => + cy.insert_doc("Contact", contact_template, true) + ); + Promise.all(promises); + }); + + function open_multi_select_dialog() { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + new xhiveframework.ui.form.MultiSelectDialog({ + doctype: "Contact", + target: {}, + setters: { + status: null, + gender: null, + }, + add_filters_group: 1, + allow_child_item_selection: 1, + child_fieldname: "email_ids", + child_columns: ["email_id", "is_primary"], + }); + }); + } + + it("checks multi select dialog api works", () => { + open_multi_select_dialog(); + cy.get_open_dialog().should("contain", "Select Contacts"); + }); + + it("checks for filters", () => { + ["search_term", "status", "gender"].forEach((fieldname) => { + cy.get_open_dialog() + .get(`.xhiveframework-control[data-fieldname="${fieldname}"]`) + .should("exist"); + }); + + // add_filters_group: 1 should add a filter group + cy.get_open_dialog().get(`.xhiveframework-control[data-fieldname="filter_area"]`).should("exist"); + }); + + it("checks for child item selection", () => { + cy.get_open_dialog().get(`.dt-row-header`).should("not.exist"); + + cy.get_open_dialog() + .get(`.xhiveframework-control[data-fieldname="allow_child_item_selection"]`) + .find('input[data-fieldname="allow_child_item_selection"]') + .should("exist") + .click({ force: true }); + + cy.get_open_dialog() + .get(`.xhiveframework-control[data-fieldname="child_selection_area"]`) + .should("exist"); + + cy.get_open_dialog().get(`.dt-row-header`).should("contain", "Contact"); + + cy.get_open_dialog().get(`.dt-row-header`).should("contain", "Email Id"); + + cy.get_open_dialog().get(`.dt-row-header`).should("contain", "Is Primary"); + }); + + it("tests more button", () => { + cy.get_open_dialog() + .get(`.xhiveframework-control[data-fieldname="search_term"]`) + .find('input[data-fieldname="search_term"]') + .should("exist") + .type("Test", { delay: 200 }); + cy.get_open_dialog() + .get(`.xhiveframework-control[data-fieldname="more_child_btn"]`) + .should("exist") + .as("more-btn"); + + cy.get_open_dialog() + .get(".datatable .dt-scrollable .dt-row") + .should(($rows) => { + expect($rows).to.have.length(20); + }); + + cy.intercept("POST", "api/method/xhiveframework.client.get_list").as("get-more-records"); + cy.get("@more-btn").find("button").click({ force: true }); + cy.wait("@get-more-records"); + + cy.get_open_dialog() + .get(".datatable .dt-scrollable .dt-row") + .should(($rows) => { + if ($rows.length <= 20) { + throw new Error("More button doesn't work"); + } + }); + }); +}); diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js new file mode 100644 index 0000000..d93adc1 --- /dev/null +++ b/cypress/integration/navigation.js @@ -0,0 +1,36 @@ +context("Navigation", () => { + before(() => { + cy.visit("/login"); + cy.login(); + cy.visit("/app/website"); + }); + it("Navigate to route with hash in document name", () => { + cy.insert_doc( + "Client Script", + { + __newname: "ABC#123", + dt: "User", + script: "console.log('ran')", + enabled: 0, + }, + true + ); + cy.visit(`/app/client-script/${encodeURIComponent("ABC#123")}`); + cy.title().should("eq", "ABC#123"); + cy.go("back"); + cy.title().should("eq", "Website"); + }); + + it("Navigate to previous page after login", () => { + cy.visit("/app/todo"); + cy.get(".page-head").findByTitle("To Do").should("be.visible"); + cy.clear_filters(); + cy.call("logout"); + cy.reload().as("reload"); + cy.get("@reload").get(".page-card .btn-primary").contains("Login").click(); + cy.location("pathname").should("eq", "/login"); + cy.login(); + cy.visit("/app"); + cy.location("pathname").should("eq", "/app/todo"); + }); +}); diff --git a/cypress/integration/number_card.js b/cypress/integration/number_card.js new file mode 100644 index 0000000..24227fe --- /dev/null +++ b/cypress/integration/number_card.js @@ -0,0 +1,22 @@ +context("Number Card", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + it("Check filter populate for child table doctype", () => { + cy.new_form("Number Card"); + cy.get('[data-fieldname="parent_document_type"]').should("have.css", "display", "none"); + + cy.get_field("document_type", "Link"); + cy.fill_field("document_type", "Workspace Link", "Link").focus().blur(); + cy.get_field("document_type", "Link").should("have.value", "Workspace Link"); + + cy.fill_field("label", "Test Number Card", "Data"); + + cy.get('[data-fieldname="filters_json"]').click().wait(200); + cy.get(".modal-body .filter-action-buttons .add-filter").click(); + cy.get(".modal-body .fieldname-select-area").click(); + cy.get(".modal-actions .btn-modal-close").click(); + }); +}); diff --git a/cypress/integration/permissions.js b/cypress/integration/permissions.js new file mode 100644 index 0000000..2da4f81 --- /dev/null +++ b/cypress/integration/permissions.js @@ -0,0 +1,41 @@ +context.skip("Permissions API", () => { + before(() => { + cy.visit("/login"); + cy.remove_role("xhiveframework@example.com", "System Manager"); + cy.visit("/app"); + }); + + it("Checks permissions via `has_perm` for Kanban Board DocType", () => { + cy.visit("/app/kanban-board/view/list"); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.model.with_doctype("Kanban Board", function () { + // needed to make sure doc meta is loaded + expect(xhiveframework.perm.has_perm("Kanban Board", 0, "read")).to.equal(true); + expect(xhiveframework.perm.has_perm("Kanban Board", 0, "write")).to.equal(true); + expect(xhiveframework.perm.has_perm("Kanban Board", 0, "print")).to.equal(false); + }); + }); + }); + + it("Checks permissions via `get_perm` for Kanban Board DocType", () => { + cy.visit("/app/kanban-board/view/list"); + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.model.with_doctype("Kanban Board", function () { + // needed to make sure doc meta is loaded + const perms = xhiveframework.perm.get_perm("Kanban Board"); + expect(perms.read).to.equal(true); + expect(perms.write).to.equal(true); + expect(perms.rights_without_if_owner).to.include("read"); + }); + }); + }); + + after(() => { + cy.add_role("xhiveframework@example.com", "System Manager"); + cy.call("logout"); + }); +}); diff --git a/cypress/integration/query_report.js b/cypress/integration/query_report.js new file mode 100644 index 0000000..67dd576 --- /dev/null +++ b/cypress/integration/query_report.js @@ -0,0 +1,91 @@ +context("Query Report", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + cy.insert_doc( + "Report", + { + report_name: "Test ToDo Report", + ref_doctype: "ToDo", + report_type: "Query Report", + query: "select * from tabToDo", + }, + true + ).as("doc"); + cy.create_records({ + doctype: "ToDo", + description: "this is a test todo for query report", + }).as("todos"); + }); + + it("add custom column in report", () => { + cy.visit("/app/query-report/Permitted Documents For User"); + + cy.get(".page-form.flex", { timeout: 60000 }) + .should("have.length", 1) + .then(() => { + cy.get('#page-query-report input[data-fieldname="user"]').as("input-user"); + cy.get("@input-user").focus().type("test@xhiveerp.com", { delay: 100 }).blur(); + cy.wait(300); + cy.get('#page-query-report input[data-fieldname="doctype"]').as("input-role"); + cy.get("@input-role").focus().type("Role", { delay: 100 }).blur(); + + cy.get(".datatable").should("exist"); + cy.get("#page-query-report .page-actions .menu-btn-group button").click({ + force: true, + }); + cy.get("#page-query-report .menu-btn-group .dropdown-menu") + .contains("Add Column") + .click({ force: true }); + cy.get_open_dialog().get(".modal-title").should("contain", "Add Column"); + cy.get('select[data-fieldname="doctype"]').select("Role", { force: true }); + cy.get('select[data-fieldname="field"]').select("Role Name", { force: true }); + cy.get('select[data-fieldname="insert_after"]').select("Name", { force: true }); + cy.get_open_dialog() + .findByRole("button", { name: "Submit" }) + .click({ force: true }); + cy.get("#page-query-report .page-actions .menu-btn-group button").click({ + force: true, + }); + cy.get("#page-query-report .menu-btn-group .dropdown-menu") + .contains("Save") + .click({ timeout: 100, force: true }); + cy.get_open_dialog().get(".modal-title").should("contain", "Save Report"); + + cy.get('input[data-fieldname="report_name"]').type("Test Report", { + delay: 100, + force: true, + }); + cy.get_open_dialog() + .findByRole("button", { name: "Submit" }) + .click({ timeout: 1000, force: true }); + }); + }); + + let save_report_and_open = (report, update_name) => { + cy.get("#page-query-report .page-actions .menu-btn-group button").click({ force: true }); + cy.get("#page-query-report .menu-btn-group .dropdown-menu") + .contains("Save") + .click({ timeout: 100, force: true }); + cy.get_open_dialog().get(".modal-title").should("contain", "Save Report"); + + cy.get('input[data-fieldname="report_name"]').type(update_name, { + delay: 100, + force: true, + }); + cy.get_open_dialog() + .findByRole("button", { name: "Submit" }) + .click({ timeout: 1000, force: true }); + + cy.visit("/app/query-report/" + report); + cy.get(".datatable").should("exist"); + }; + + it("test multi level query report", () => { + cy.visit("/app/query-report/Test ToDo Report"); + cy.get(".datatable").should("exist"); + + save_report_and_open("Test ToDo Report 1", " 1"); + save_report_and_open("Test ToDo Report 11", "1"); + }); +}); diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js new file mode 100644 index 0000000..938f23b --- /dev/null +++ b/cypress/integration/recorder.js @@ -0,0 +1,72 @@ +context.skip("Recorder", () => { + before(() => { + cy.login(); + }); + + beforeEach(() => { + cy.visit("/app/recorder"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + // reset recorder + return xhiveframework.xcall("xhiveframework.recorder.stop").then(() => { + return xhiveframework.xcall("xhiveframework.recorder.delete"); + }); + }); + }); + + it("Recorder Empty State", () => { + cy.get(".page-head").findByTitle("Recorder").should("exist"); + + cy.get(".indicator-pill").should("contain", "Inactive").should("have.class", "red"); + + cy.get(".page-actions").findByRole("button", { name: "Start" }).should("exist"); + cy.get(".page-actions").findByRole("button", { name: "Clear" }).should("exist"); + + cy.get(".msg-box").should("contain", "Recorder is Inactive"); + cy.get(".msg-box").findByRole("button", { name: "Start Recording" }).should("exist"); + }); + + it("Recorder Start", () => { + cy.get(".page-actions").findByRole("button", { name: "Start" }).click(); + cy.get(".indicator-pill").should("contain", "Active").should("have.class", "green"); + + cy.get(".msg-box").should("contain", "No Requests found"); + + cy.visit("/app/List/DocType/List"); + cy.intercept("POST", "/api/method/xhiveframework.desk.reportview.get").as("list_refresh"); + cy.wait("@list_refresh"); + + cy.get(".page-head").findByTitle("DocType").should("exist"); + cy.get(".list-count").should("contain", "20 of "); + + cy.visit("/app/recorder"); + cy.get(".page-head").findByTitle("Recorder").should("exist"); + cy.get(".xhiveframework-list .result-list").should( + "contain", + "/api/method/xhiveframework.desk.reportview.get" + ); + }); + + it("Recorder View Request", () => { + cy.get(".page-actions").findByRole("button", { name: "Start" }).click(); + + cy.visit("/app/List/DocType/List"); + cy.intercept("POST", "/api/method/xhiveframework.desk.reportview.get").as("list_refresh"); + cy.wait("@list_refresh"); + + cy.get(".page-head").findByTitle("DocType").should("exist"); + cy.get(".list-count").should("contain", "20 of "); + + cy.visit("/app/recorder"); + + cy.get(".xhiveframework-list .list-row-container span") + .contains("/api/method/xhiveframework") + .should("be.visible") + .click({ force: true }); + + cy.url().should("include", "/recorder/request"); + cy.get("form").should("contain", "/api/method/xhiveframework"); + }); +}); diff --git a/cypress/integration/relative_time_filters.js b/cypress/integration/relative_time_filters.js new file mode 100644 index 0000000..3778ead --- /dev/null +++ b/cypress/integration/relative_time_filters.js @@ -0,0 +1,47 @@ +// TODO: Enable this again +// currently this is flaky possibly because of different timezone in CI + +// context('Relative Timeframe', () => { +// before(() => { +// cy.login(); +// cy.visit('/app/website'); +// cy.window().its('xhiveframework').then(xhiveframework => { +// xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_todo_records"); +// }); +// }); +// it('sets relative timespan filter for last week and filters list', () => { +// cy.visit('/app/List/ToDo/List'); +// cy.clear_filters(); +// cy.get('.list-row:contains("this is fourth todo")').should('exist'); +// cy.add_filter(); +// cy.get('.fieldname-select-area').should('exist'); +// cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 }); +// cy.get('select.condition.form-control').select("Timespan"); +// cy.get('.filter-field select.input-with-feedback.form-control').select("last week"); +// cy.intercept('POST', '/api/method/xhiveframework.desk.reportview.get').as('list_refresh'); +// cy.get('.filter-popover .apply-filters').click({ force: true }); +// cy.wait('@list_refresh'); +// cy.get('.list-row-container').its('length').should('eq', 1); +// cy.get('.list-row-container').should('contain', 'this is second todo'); +// cy.intercept('POST', '/api/method/xhiveframework.model.utils.user_settings.save') +// .as('save_user_settings'); +// cy.clear_filters(); +// cy.wait('@save_user_settings'); +// }); +// it('sets relative timespan filter for next week and filters list', () => { +// cy.visit('/app/List/ToDo/List'); +// cy.clear_filters(); +// cy.get('.list-row:contains("this is fourth todo")').should('exist'); +// cy.add_filter(); +// cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 }); +// cy.get('select.condition.form-control').select("Timespan"); +// cy.get('.filter-field select.input-with-feedback.form-control').select("next week"); +// cy.intercept('POST', '/api/method/xhiveframework.desk.reportview.get').as('list_refresh'); +// cy.get('.filter-popover .apply-filters').click({ force: true }); +// cy.wait('@list_refresh'); +// cy.intercept('POST', '/api/method/xhiveframework.model.utils.user_settings.save') +// .as('save_user_settings'); +// cy.clear_filters(); +// cy.wait('@save_user_settings'); +// }); +// }); diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js new file mode 100644 index 0000000..38980b2 --- /dev/null +++ b/cypress/integration/report_view.js @@ -0,0 +1,47 @@ +import custom_submittable_doctype from "../fixtures/custom_submittable_doctype"; +const doctype_name = custom_submittable_doctype.name; + +context("Report View", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + cy.insert_doc("DocType", custom_submittable_doctype, true); + cy.clear_cache(); + cy.insert_doc( + doctype_name, + { + title: "Doc 1", + description: "Random Text", + enabled: 0, + docstatus: 1, // submit document + }, + true + ); + }); + + it("Field with enabled allow_on_submit should be editable.", () => { + cy.intercept("POST", "api/method/xhiveframework.client.set_value").as("value-update"); + cy.visit(`/app/List/${doctype_name}/Report`); + + // check status column added from docstatus + cy.get(".dt-row-0 > .dt-cell--col-3").should("contain", "Submitted"); + let cell = cy.get(".dt-row-0 > .dt-cell--col-4"); + + // select the cell + cell.dblclick(); + cell.get(".dt-cell__edit--col-4").findByRole("checkbox").check({ force: true }); + cy.get(".dt-row-0 > .dt-cell--col-3").click(); // click outside + + cy.wait("@value-update"); + + cy.call("xhiveframework.client.get_value", { + doctype: doctype_name, + filters: { + title: "Doc 1", + }, + fieldname: "enabled", + }).then((r) => { + expect(r.message.enabled).to.equals(1); + }); + }); +}); diff --git a/cypress/integration/rounding.js b/cypress/integration/rounding.js new file mode 100644 index 0000000..f778e00 --- /dev/null +++ b/cypress/integration/rounding.js @@ -0,0 +1,104 @@ +context("Rounding behaviour", () => { + before(() => { + cy.login(); + cy.visit("/app/"); + }); + + it("Commercial Rounding", () => { + cy.window() + .its("flt") + .then((flt) => { + let rounding_method = "Commercial Rounding"; + + expect(flt("0.5", 0, null, rounding_method)).eq(1); + expect(flt("0.3", null, null, rounding_method)).eq(0.3); + + expect(flt("1.5", 0, null, rounding_method)).eq(2); + + // positive rounding to integers + expect(flt(0.4, 0, null, rounding_method)).eq(0); + expect(flt(0.5, 0, null, rounding_method)).eq(1); + expect(flt(1.455, 0, null, rounding_method)).eq(1); + expect(flt(1.5, 0, null, rounding_method)).eq(2); + + // negative rounding to integers + expect(flt(-0.5, 0, null, rounding_method)).eq(-1); + expect(flt(-1.5, 0, null, rounding_method)).eq(-2); + + // negative precision i.e. round to nearest 10th + expect(flt(123, -1, null, rounding_method)).eq(120); + expect(flt(125, -1, null, rounding_method)).eq(130); + expect(flt(134.45, -1, null, rounding_method)).eq(130); + expect(flt(135, -1, null, rounding_method)).eq(140); + + // positive multiple digit rounding + expect(flt(1.25, 1, null, rounding_method)).eq(1.3); + expect(flt(0.15, 1, null, rounding_method)).eq(0.2); + expect(flt(2.675, 2, null, rounding_method)).eq(2.68); + + // negative multiple digit rounding + expect(flt(-1.25, 1, null, rounding_method)).eq(-1.3); + expect(flt(-0.15, 1, null, rounding_method)).eq(-0.2); + }); + }); + + it("Banker's Rounding", () => { + cy.window() + .its("flt") + .then((flt) => { + let rounding_method = "Banker's Rounding"; + + expect(flt("0.5", 0, null, rounding_method)).eq(0); + expect(flt("0.3", null, null, rounding_method)).eq(0.3); + + expect(flt("1.5", 0, null, rounding_method)).eq(2); + + // positive rounding to integers + expect(flt(0.4, 0, null, rounding_method)).eq(0); + expect(flt(0.5, 0, null, rounding_method)).eq(0); + expect(flt(1.455, 0, null, rounding_method)).eq(1); + expect(flt(1.5, 0, null, rounding_method)).eq(2); + + // negative rounding to integers + expect(flt(-0.5, 0, null, rounding_method)).eq(0); + expect(flt(-1.5, 0, null, rounding_method)).eq(-2); + + // negative precision i.e. round to nearest 10th + expect(flt(123, -1, null, rounding_method)).eq(120); + expect(flt(125, -1, null, rounding_method)).eq(120); + expect(flt(134.45, -1, null, rounding_method)).eq(130); + expect(flt(135, -1, null, rounding_method)).eq(140); + + // positive multiple digit rounding + expect(flt(1.25, 1, null, rounding_method)).eq(1.2); + expect(flt(0.15, 1, null, rounding_method)).eq(0.2); + expect(flt(2.675, 2, null, rounding_method)).eq(2.68); + expect(flt(-2.675, 2, null, rounding_method)).eq(-2.68); + + // negative multiple digit rounding + expect(flt(-1.25, 1, null, rounding_method)).eq(-1.2); + expect(flt(-0.15, 1, null, rounding_method)).eq(-0.2); + + // Nearest number and not even (the default behaviour) + expect(flt(0.5, 0, null, rounding_method)).eq(0); + expect(flt(1.5, 0, null, rounding_method)).eq(2); + expect(flt(2.5, 0, null, rounding_method)).eq(2); + expect(flt(3.5, 0, null, rounding_method)).eq(4); + + expect(flt(0.05, 1, null, rounding_method)).eq(0.0); + expect(flt(1.15, 1, null, rounding_method)).eq(1.2); + expect(flt(2.25, 1, null, rounding_method)).eq(2.2); + expect(flt(3.35, 1, null, rounding_method)).eq(3.4); + + expect(flt(-0.5, 0, null, rounding_method)).eq(0); + expect(flt(-1.5, 0, null, rounding_method)).eq(-2); + expect(flt(-2.5, 0, null, rounding_method)).eq(-2); + expect(flt(-3.5, 0, null, rounding_method)).eq(-4); + + expect(flt(-0.05, 1, null, rounding_method)).eq(0.0); + expect(flt(-1.15, 1, null, rounding_method)).eq(-1.2); + expect(flt(-2.25, 1, null, rounding_method)).eq(-2.2); + expect(flt(-3.35, 1, null, rounding_method)).eq(-3.4); + }); + }); +}); diff --git a/cypress/integration/routing.js b/cypress/integration/routing.js new file mode 100644 index 0000000..0822dd9 --- /dev/null +++ b/cypress/integration/routing.js @@ -0,0 +1,40 @@ +const list_view = "/app/todo"; + +// test round trip with filter types + +const test_queries = [ + "?status=Open", + `?date=%5B"Between"%2C%5B"2022-06-01"%2C"2022-06-30"%5D%5D`, + `?date=%5B">"%2C"2022-06-01"%5D`, + `?name=%5B"like"%2C"%2542%25"%5D`, + `?status=%5B"not%20in"%2C%5B"Open"%2C"Closed"%5D%5D`, +]; + +describe("SPA Routing", { scrollBehavior: false }, () => { + before(() => { + cy.login(); + cy.go_to_list("ToDo"); + }); + + after(() => { + cy.clear_filters(); // avoid flake in future tests + }); + + it("should apply filter on list view from route", () => { + test_queries.forEach((query) => { + const full_url = `${list_view}${query}`; + cy.visit(full_url); + cy.findByTitle("To Do").should("exist"); + + const expected = new URLSearchParams(query); + cy.location().then((loc) => { + const actual = new URLSearchParams(loc.search); + // This might appear like a dumb test checking visited URL to itself + // but it's actually doing a round trip + // URL with params -> parsed filters -> new URL + // if it's same that means everything worked in between. + expect(actual.toString()).to.eq(expected.toString()); + }); + }); + }); +}); diff --git a/cypress/integration/sidebar.js b/cypress/integration/sidebar.js new file mode 100644 index 0000000..6a54da4 --- /dev/null +++ b/cypress/integration/sidebar.js @@ -0,0 +1,144 @@ +const verify_attachment_visibility = (document, is_private) => { + cy.visit(`/app/${document}`); + + const assertion = is_private ? "be.checked" : "not.be.checked"; + cy.get(".add-attachment-btn").click(); + + cy.get_open_dialog() + .find(".file-upload-area") + .selectFile("cypress/fixtures/sample_image.jpg", { + action: "drag-drop", + }); + + cy.get_open_dialog().findByRole("checkbox", { name: "Private" }).should(assertion); +}; + +const attach_file = (file, no_of_files = 1) => { + let files = []; + if (file) { + files = [file]; + } else if (no_of_files > 1) { + // attach n files + files = [...Array(no_of_files)].map( + (el, idx) => + "cypress/fixtures/sample_attachments/attachment-" + + (idx + 1) + + (idx == 0 ? ".jpg" : ".txt") + ); + } + + cy.get(".add-attachment-btn").click(); + cy.get_open_dialog().find(".file-upload-area").selectFile(files, { + action: "drag-drop", + }); + cy.get_open_dialog().findByRole("button", { name: "Upload" }).click(); +}; + +context("Sidebar", () => { + before(() => { + cy.visit("/"); + cy.login(); + cy.visit("/app"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.call("xhiveframework.tests.ui_test_helpers.create_blog_post"); + }); + }); + + it("Verify attachment visibility config", () => { + cy.call("xhiveframework.tests.ui_test_helpers.create_todo", { + description: "Sidebar Attachment ToDo", + }).then((todo) => { + verify_attachment_visibility(`todo/${todo.message.name}`, true); + }); + verify_attachment_visibility("blog-post/test-blog-attachment-post", false); + }); + + it("Verify attachment accessibility UX", () => { + cy.call("xhiveframework.tests.ui_test_helpers.create_todo_with_attachment_limit", { + description: "Sidebar Attachment Access Test ToDo", + }).then((todo) => { + cy.visit(`/app/todo/${todo.message.name}`); + + attach_file("cypress/fixtures/sample_image.jpg"); + cy.get(".explore-link").should("be.visible"); + cy.get(".show-all-btn").should("be.hidden"); + + // attach 10 images + attach_file(null, 10); + cy.get(".show-all-btn").should("be.visible"); + + // attach 1 more image to reach attachment limit + attach_file("cypress/fixtures/sample_attachments/attachment-11.txt"); + cy.get(".add-attachment-btn").should("be.hidden"); + cy.get(".explore-link").should("be.visible"); + + // test "Show All" button + cy.get(".attachment-row").should("have.length", 10); + cy.get(".show-all-btn").click({ force: true }); + cy.get(".attachment-row").should("have.length", 12); + }); + }); + + it('Test for checking "Assigned To" counter value, adding filter and adding & removing an assignment', () => { + cy.call("xhiveframework.tests.ui_test_helpers.create_todo", { + description: "Sidebar Attachment ToDo", + }).then((todo) => { + let todo_name = todo.message.name; + cy.visit("/app/todo"); + cy.click_sidebar_button("Assigned To"); + + //To check if no filter is available in "Assigned To" dropdown + cy.get(".empty-state").should("contain", "No filters found"); + + //Assigning a doctype to a user + cy.visit(`/app/todo/${todo_name}`); + cy.get(".add-assignment-btn").click(); + cy.get_field("assign_to_me", "Check").click(); + cy.wait(1000); + cy.get(".modal-footer > .standard-actions > .btn-primary").click(); + cy.visit("/app/todo"); + cy.click_sidebar_button("Assigned To"); + + //To check if filter is added in "Assigned To" dropdown after assignment + cy.get( + ".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item" + ).should("contain", "1"); + + //To check if there is no filter added to the listview + cy.get(".filter-button").should("contain", "Filter"); + + //To add a filter to display data into the listview + cy.get( + ".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item" + ).click(); + + //To check if filter is applied + cy.click_filter_button().get(".filter-label").should("contain", "1"); + cy.get(".fieldname-select-area > .awesomplete > .form-control").should( + "have.value", + "Assigned To" + ); + cy.get(".condition").should("have.value", "like"); + cy.get(".filter-field > .form-group > .input-with-feedback").should( + "have.value", + `%${cy.config("testUser")}%` + ); + cy.click_filter_button(); + + //To remove the applied filter + cy.clear_filters(); + + //To remove the assignment + cy.visit(`/app/todo/${todo_name}`); + cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click(); + cy.get(".remove-btn").click({ force: true }); + cy.hide_dialog(); + cy.visit("/app/todo"); + cy.click_sidebar_button("Assigned To"); + cy.get(".empty-state").should("contain", "No filters found"); + }); + }); +}); diff --git a/cypress/integration/socket_updates.js b/cypress/integration/socket_updates.js new file mode 100644 index 0000000..7055ab0 --- /dev/null +++ b/cypress/integration/socket_updates.js @@ -0,0 +1,80 @@ +context("Realtime updates", () => { + before(() => { + cy.login(); + }); + + beforeEach(() => { + cy.visit("/app/todo"); + // required because immediately after load socket is still connecting. + // Not a huge deal breaker in prod. + cy.wait(500); + cy.clear_filters(); + }); + + it("Shows version conflict warning", { scrollBehavior: false }, () => { + cy.insert_doc("ToDo", { description: "old" }).then((doc) => { + cy.visit(`/app/todo/${doc.name}`); + // make form dirty + cy.fill_field("status", "Cancelled", "Select"); + + // update doc using api - simulating parallel change by another user + cy.update_doc("ToDo", doc.name, { status: "Closed" }).then(() => { + cy.findByRole("button", { name: "Refresh" }).click(); + cy.get_field("status", "Select").should("have.value", "Closed"); + }); + }); + }); + + it("List view updates in realtime on insert", { scrollBehavior: false }, () => { + const original = "Added for realtime update"; + const updated = "Updated for realtime update"; + cy.insert_doc("ToDo", { description: original }).then((doc) => { + cy.contains(original).should("be.visible"); + + // update doc using api - simulating parallel change by another user + cy.update_doc("ToDo", doc.name, { description: updated }).then(() => { + cy.contains(updated).should("be.visible"); + }); + }); + }); + + it("Receives msgprint from server", { scrollBehavior: false }, () => { + // required because immediately after load socket is still connecting. + // Not a deal breaker in prod + const msg = "msgprint sent via realtime"; + publish_realtime({ event: "msgprint", message: msg }).then(() => { + cy.contains(msg).should("be.visible"); + }); + }); + + it("Recieves custom messages from server", { scrollBehavior: false }, () => { + const event = "cypress_event"; + let handler = { + handle() { + console.log("clear"); + }, + }; + cy.spy(handler, "handle").as("callback"); + + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.realtime.on(event, () => handler.handle()); + }); + + publish_realtime({ event }).then(() => { + cy.get("@callback").should("be.called"); + }); + }); + + it("Progress bar", { scrollBehavior: false }, () => { + const title = "RealTime Progress"; + cy.call("xhiveframework.tests.ui_test_helpers.publish_progress", { title }).then(() => { + cy.contains(title).should("be.visible"); + }); + }); +}); + +function publish_realtime(args) { + return cy.call("xhiveframework.tests.ui_test_helpers.publish_realtime", args); +} diff --git a/cypress/integration/table_multiselect.js b/cypress/integration/table_multiselect.js new file mode 100644 index 0000000..b691ff0 --- /dev/null +++ b/cypress/integration/table_multiselect.js @@ -0,0 +1,59 @@ +context("Table MultiSelect", () => { + before(() => { + cy.login(); + }); + + let name = "table multiselect" + Math.random().toString().slice(2, 8); + + it("select value from multiselect dropdown", () => { + cy.new_form("Assignment Rule"); + cy.fill_field("__newname", name); + cy.fill_field("document_type", "Blog Post"); + cy.get(".section-head").contains("Assignment Rules").scrollIntoView(); + cy.fill_field("assign_condition", 'status=="Open"', "Code"); + cy.get('input[data-fieldname="users"]').focus().as("input"); + cy.get('input[data-fieldname="users"] + ul').should("be.visible"); + cy.get("@input").type("test@xhiveerp", { delay: 100 }); + cy.wait(500); + cy.get("@input").type("{enter}"); + cy.get( + '.xhiveframework-control[data-fieldname="users"] .form-control .tb-selected-value .btn-link-to-form' + ).as("selected-value"); + cy.get("@selected-value").should("contain", "test@xhiveerp.com"); + + cy.intercept("POST", "/api/method/xhiveframework.desk.form.save.savedocs").as("save_form"); + // trigger save + cy.get(".primary-action").click(); + cy.wait("@save_form").its("response.statusCode").should("eq", 200); + cy.get("@selected-value").should("contain", "test@xhiveerp.com"); + }); + + it("delete value using backspace", () => { + cy.go_to_list("Assignment Rule"); + cy.get(`.list-subject:contains("table multiselect")`).last().find("a").click(); + cy.get('input[data-fieldname="users"]').focus().type("{backspace}"); + cy.get('.xhiveframework-control[data-fieldname="users"] .form-control .tb-selected-value').should( + "not.exist" + ); + }); + + it("delete value using x", () => { + cy.go_to_list("Assignment Rule"); + cy.get(`.list-subject:contains("table multiselect")`).last().find("a").click(); + cy.get('.xhiveframework-control[data-fieldname="users"] .form-control .tb-selected-value').as( + "existing_value" + ); + cy.get("@existing_value").find(".btn-remove").click(); + cy.get("@existing_value").should("not.exist"); + }); + + it("navigate to selected value", () => { + cy.go_to_list("Assignment Rule"); + cy.get(`.list-subject:contains("table multiselect")`).last().find("a").click(); + cy.get('.xhiveframework-control[data-fieldname="users"] .form-control .tb-selected-value').as( + "existing_value" + ); + cy.get("@existing_value").find(".btn-link-to-form").click(); + cy.location("pathname").should("contain", "/user/test%40xhiveerp.com"); + }); +}); diff --git a/cypress/integration/url_data_field.js b/cypress/integration/url_data_field.js new file mode 100644 index 0000000..b26f0b4 --- /dev/null +++ b/cypress/integration/url_data_field.js @@ -0,0 +1,42 @@ +import data_field_validation_doctype from "../fixtures/data_field_validation_doctype"; + +const doctype_name = data_field_validation_doctype.name; + +context("URL Data Field Input", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + return cy.insert_doc("DocType", data_field_validation_doctype, true); + }); + + describe("URL Data Field Input ", () => { + it("should not show URL link button without focus", () => { + cy.new_form(doctype_name); + cy.get_field("url").clear().type("https://xhiveframework.io"); + cy.get_field("url").blur().wait(500); + cy.get(".link-btn").should("not.be.visible"); + }); + + it("should show URL link button on focus", () => { + cy.get_field("url").focus().wait(500); + cy.get(".link-btn").should("be.visible"); + }); + + it("should not show URL link button for invalid URL", () => { + cy.get_field("url").clear().type("fuzzbuzz"); + cy.get(".link-btn").should("not.be.visible"); + }); + + it("should have valid URL link with target _blank", () => { + cy.get_field("url").clear().type("https://xhiveframework.io"); + cy.get(".link-btn .btn-open").should("have.attr", "href", "https://xhiveframework.io"); + cy.get(".link-btn .btn-open").should("have.attr", "target", "_blank"); + }); + + it("should inject anchor tag in read-only URL data field", () => { + cy.get('[data-fieldname="read_only_url"]') + .find("a") + .should("have.attr", "target", "_blank"); + }); + }); +}); diff --git a/cypress/integration/view_routing.js b/cypress/integration/view_routing.js new file mode 100644 index 0000000..0cf870f --- /dev/null +++ b/cypress/integration/view_routing.js @@ -0,0 +1,231 @@ +context("View", () => { + before(() => { + cy.login(); + cy.visit("/app/website"); + }); + + it("Route to ToDo List View", () => { + cy.visit("/app/todo/view/list"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("List"); + }); + }); + + it("Route to ToDo Report View", () => { + cy.visit("/app/todo/view/report"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + + it("Route to ToDo Dashboard View", () => { + cy.visit("/app/todo/view/dashboard"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Dashboard"); + }); + }); + + it("Route to ToDo Gantt View", () => { + cy.visit("/app/todo/view/gantt"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Gantt"); + }); + }); + + it("Route to ToDo Kanban View", () => { + cy.call("xhiveframework.tests.ui_test_helpers.create_kanban").then(() => { + cy.visit("/app/note/view/kanban/_Note _Kanban"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Kanban"); + }); + }); + }); + + it("Route to ToDo Calendar View", () => { + cy.visit("/app/todo/view/calendar"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Calendar"); + }); + }); + + it("Route to Custom Tree View", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_tree_doctype").then(() => { + cy.visit("/app/custom-tree/view/tree"); + cy.wait(500); + cy.window() + .its("cur_tree") + .then((list) => { + expect(list.view_name).to.equal("Tree"); + }); + }); + }); + + it("Route to Custom Image View", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_image_doctype").then(() => { + cy.visit("app/custom-image/view/image"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Image"); + }); + }); + }); + + it("Route to Communication Inbox View", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_inbox").then(() => { + cy.visit("app/communication/view/inbox"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Inbox"); + }); + }); + }); + + it("Route to File View", () => { + cy.intercept("POST", "/api/method/xhiveframework.desk.reportview.get").as("list_loaded"); + cy.visit("app/file"); + cy.wait("@list_loaded"); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("File"); + expect(list.current_folder).to.equal("Home"); + }); + + cy.visit("app/file/view/home/Attachments"); + cy.wait("@list_loaded"); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("File"); + expect(list.current_folder).to.equal("Home/Attachments"); + }); + }); + + it("Re-route to default view", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => { + cy.visit("app/event"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Route to default view from app/{doctype}", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => { + cy.visit("/app/event"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Route to default view from app/{doctype}/view", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => { + cy.visit("/app/event/view"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Force Route to default view from app/{doctype}", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { + view: "Report", + force_reroute: true, + }).then(() => { + cy.visit("/app/event"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Force Route to default view from app/{doctype}/view", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { + view: "Report", + force_reroute: true, + }).then(() => { + cy.visit("/app/event/view"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Force Route to default view from app/{doctype}/view", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { + view: "Report", + force_reroute: true, + }).then(() => { + cy.visit("/app/event/view/list"); + cy.wait(500); + cy.window() + .its("cur_list") + .then((list) => { + expect(list.view_name).to.equal("Report"); + }); + }); + }); + + it("Validate Route History for Default View", () => { + cy.call("xhiveframework.tests.ui_test_helpers.setup_default_view", { view: "Report" }).then(() => { + cy.visit("/app/event"); + cy.visit("/app/event/view/list"); + cy.location("pathname").should("eq", "/app/event/view/list"); + cy.go("back"); + cy.location("pathname").should("eq", "/app/event"); + }); + }); + + it("Route to Form", () => { + const test_user = cy.config("testUser"); + cy.visit(`/app/user/${test_user}`); + cy.window() + .its("cur_frm") + .then((frm) => { + expect(frm.doc.name).to.equal(test_user); + }); + }); + + it("Route to Website Workspace", () => { + cy.visit("/app/website"); + cy.get(".title-text").should("contain", "Website"); + }); +}); diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js new file mode 100644 index 0000000..931ac5d --- /dev/null +++ b/cypress/integration/web_form.js @@ -0,0 +1,278 @@ +context("Web Form", () => { + before(() => { + cy.login("Administrator"); + cy.visit("/app/"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.prepare_webform_test"); + }); + }); + + it("Create Web Form", () => { + cy.visit("/app/web-form/new"); + + cy.intercept("POST", "/api/method/xhiveframework.desk.form.save.savedocs").as("save_form"); + + cy.fill_field("title", "Note"); + cy.fill_field("doc_type", "Note", "Link"); + cy.fill_field("module", "Website", "Link"); + cy.click_custom_action_button("Get Fields"); + cy.click_custom_action_button("Publish"); + + cy.wait("@save_form"); + + cy.get_field("route").should("have.value", "note"); + cy.get(".title-area .indicator-pill").contains("Published"); + }); + + it("Open Web Form", () => { + cy.visit("/note"); + cy.fill_field("title", "Note 1"); + cy.get(".web-form-actions button").contains("Save").click(); + + cy.url().should("include", "/note/new"); + + cy.call("logout"); + cy.visit("/note"); + + cy.url().should("include", "/note/new"); + + cy.fill_field("title", "Guest Note 1"); + cy.get(".web-form-actions button").contains("Save").click(); + + cy.url().should("include", "/note/new"); + + cy.visit("/note"); + cy.url().should("include", "/note/new"); + }); + + it("Login Required", () => { + cy.call("logout"); + cy.login("Administrator"); + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get('input[data-fieldname="login_required"]').check({ force: true }); + + cy.save(); + + cy.visit("/note"); + + cy.call("logout"); + + cy.visit("/note"); + cy.get_open_dialog() + .get(".modal-message") + .contains("You are not permitted to access this page without login."); + }); + + it("Show List", () => { + cy.login("Administrator"); + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get(".section-head").contains("List Settings").click(); + cy.get('input[data-fieldname="show_list"]').check(); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-table").should("be.visible"); + }); + + it("Show Custom List Title", () => { + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.fill_field("list_title", "Note List"); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-header h1").should("contain.text", "Note List"); + }); + + it("Show Custom List Columns", () => { + cy.visit("/note"); + cy.url().should("include", "/note/list"); + + cy.get(".web-list-table thead th").contains("Sr."); + cy.get(".web-list-table thead th").contains("Title"); + + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + + cy.get('[data-fieldname="list_columns"] .grid-footer button') + .contains("Add Row") + .as("add-row"); + + cy.get("@add-row").click(); + cy.get('[data-fieldname="list_columns"] .grid-body .rows').as("grid-rows"); + cy.get("@grid-rows").find('.grid-row:first [data-fieldname="fieldname"]').click(); + cy.get("@grid-rows") + .find('.grid-row:first select[data-fieldname="fieldname"]') + .select("Title"); + + cy.get("@add-row").click(); + cy.get("@grid-rows").find('.grid-row[data-idx="2"] [data-fieldname="fieldname"]').click(); + cy.get("@grid-rows") + .find('.grid-row[data-idx="2"] select[data-fieldname="fieldname"]') + .select("Public"); + + cy.get("@add-row").click(); + cy.get("@grid-rows").find('.grid-row:last [data-fieldname="fieldname"]').click(); + cy.get("@grid-rows") + .find('.grid-row:last select[data-fieldname="fieldname"]') + .select("Content"); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-table thead th").contains("Sr."); + cy.get(".web-list-table thead th").contains("Title"); + cy.get(".web-list-table thead th").contains("Public"); + cy.get(".web-list-table thead th").contains("Content"); + }); + + it("Breadcrumbs", () => { + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-table tbody tr:last").click(); + + cy.get(".breadcrumb-container .breadcrumb .breadcrumb-item:first a") + .should("contain.text", "Note") + .click(); + cy.url().should("include", "/note/list"); + }); + + it("Custom Breadcrumbs", () => { + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Customization" }).click(); + cy.fill_field("breadcrumbs", '[{"label": _("Notes"), "route":"note"}]', "Code"); + cy.wait(2000); + cy.get(".form-tabs .nav-item .nav-link").contains("Customization").click(); + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-table tbody tr:last").click(); + cy.get(".breadcrumb-container .breadcrumb .breadcrumb-item:first a").should( + "contain.text", + "Notes" + ); + }); + + it("Read Only", () => { + cy.login("Administrator"); + cy.visit("/note"); + cy.url().should("include", "/note/list"); + + // Read Only Field + cy.get(".web-list-table tbody tr:last").click(); + cy.get('.xhiveframework-control[data-fieldname="title"] .control-input').should( + "have.css", + "display", + "none" + ); + }); + + it("Edit Mode", () => { + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get('input[data-fieldname="allow_edit"]').check(); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + cy.get(".web-list-table tbody tr:last").click(); + + cy.get(".web-form-actions a").contains("Edit Response").click(); + cy.url().should("include", "/edit"); + + // Editable Field + cy.get_field("title").should("have.value", "Note 1"); + + cy.fill_field("title", " Edited"); + cy.get(".web-form-actions button").contains("Save").click(); + cy.get(".success-page .edit-button").click(); + cy.get_field("title").should("have.value", "Note 1 Edited"); + }); + + it("Allow Multiple Response", () => { + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get('input[data-fieldname="allow_multiple"]').check(); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + + cy.get(".web-list-actions a:visible").contains("New").click(); + cy.url().should("include", "/note/new"); + + cy.fill_field("title", "Note 2"); + cy.get(".web-form-actions button").contains("Save").click(); + }); + + it("Allow Delete", () => { + cy.visit("/app/web-form/note"); + + cy.findByRole("tab", { name: "Settings" }).click(); + cy.get('input[data-fieldname="allow_delete"]').check(); + + cy.save(); + + cy.visit("/note"); + cy.url().should("include", "/note/list"); + + cy.get(".web-list-table tbody tr:nth-child(1) .list-col-checkbox input").click(); + cy.get(".web-list-table tbody tr:nth-child(2) .list-col-checkbox input").click(); + cy.get(".web-list-actions button:visible").contains("Delete").click({ force: true }); + + cy.get(".web-list-actions button").contains("Delete").should("not.be.visible"); + + cy.visit("/note"); + cy.get(".web-list-table tbody tr:nth-child(1)").should("not.exist"); + }); + + it("Navigate and Submit a WebForm", () => { + cy.visit("/update-profile"); + + cy.get(".web-form-actions a").contains("Edit Response").click(); + + cy.fill_field("middle_name", "_Test User"); + + cy.get(".web-form-actions .btn-primary").click(); + cy.url().should("include", "/me"); + }); + + it("Navigate and Submit a MultiStep WebForm", () => { + cy.call("xhiveframework.tests.ui_test_helpers.update_webform_to_multistep").then(() => { + cy.visit("/update-profile-duplicate"); + + cy.get(".web-form-actions a").contains("Edit Response").click(); + + cy.fill_field("middle_name", "_Test User"); + + cy.get(".btn-next").should("be.visible"); + cy.get(".btn-next").click(); + + cy.get(".btn-previous").should("be.visible"); + cy.get(".btn-next").should("not.be.visible"); + + cy.get(".web-form-actions .btn-primary").click(); + cy.url().should("include", "/me"); + }); + }); +}); diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js new file mode 100644 index 0000000..4174747 --- /dev/null +++ b/cypress/integration/workspace.js @@ -0,0 +1,254 @@ +context("Workspace 2.0", () => { + before(() => { + cy.visit("/login"); + cy.login(); + }); + + it("Navigate to page from sidebar", () => { + cy.visit("/app/build"); + cy.get(".codex-editor__redactor .ce-block"); + cy.get('.sidebar-item-container[item-name="Website"]').first().click(); + cy.location("pathname").should("eq", "/app/website"); + }); + + it("Create Private Page", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.new_page", + }).as("new_page"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); + cy.fill_field("title", "Test Private Page", "Data"); + cy.get_open_dialog().find(".modal-header").click(); + cy.get_open_dialog().find(".btn-primary").click(); + + // check if sidebar item is added in pubic section + cy.get('.sidebar-item-container[item-name="Test Private Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + cy.wait(300); + cy.get('.sidebar-item-container[item-name="Test Private Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.wait("@new_page"); + }); + + it("Create Child Page", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.new_page", + }).as("new_page"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); + cy.fill_field("title", "Test Child Page", "Data"); + cy.fill_field("parent", "Test Private Page", "Select"); + cy.get_open_dialog().find(".modal-header").click(); + cy.get_open_dialog().find(".btn-primary").click(); + + // check if sidebar item is added in pubic section + cy.get('.sidebar-item-container[item-name="Test Child Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + cy.wait(300); + cy.get('.sidebar-item-container[item-name="Test Child Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.wait("@new_page"); + }); + + it("Duplicate Page", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.duplicate_page", + }).as("page_duplicated"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item"); + + cy.get("@sidebar-item").find(".standard-sidebar-item").first().click(); + cy.get("@sidebar-item").find(".dropdown-btn").first().click(); + cy.get("@sidebar-item") + .find(".dropdown-list .dropdown-item") + .contains("Duplicate") + .first() + .click({ force: true }); + + cy.get_open_dialog().fill_field("title", "Duplicate Page", "Data"); + cy.click_modal_primary_button("Duplicate"); + + cy.wait("@page_duplicated"); + }); + + it("Drag Sidebar Item", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.sort_pages", + }).as("page_sorted"); + + cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item"); + + cy.get("@sidebar-item").find(".standard-sidebar-item").first().click(); + cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 }); + + cy.get('.sidebar-item-container[item-name="Build"]').as("sidebar-item"); + + cy.get("@sidebar-item").find(".standard-sidebar-item").first().click(); + cy.get("@sidebar-item").find(".drag-handle").first().move({ deltaX: 0, deltaY: 100 }); + + cy.wait("@page_sorted"); + }); + + it("Edit Page Detail", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.update_page", + }).as("page_updated"); + + cy.get('.sidebar-item-container[item-name="Test Private Page"]').as("sidebar-item"); + + cy.get("@sidebar-item").find(".standard-sidebar-item").first().click(); + cy.get("@sidebar-item").find(".dropdown-btn").first().click(); + cy.get("@sidebar-item") + .find(".dropdown-list .dropdown-item") + .contains("Edit") + .first() + .click({ force: true }); + + cy.get_open_dialog().fill_field("title", " 1", "Data"); + cy.get_open_dialog().find('input[data-fieldname="is_public"]').check(); + cy.click_modal_primary_button("Update"); + + cy.get( + '.standard-sidebar-section:first .sidebar-item-container[item-name="Test Private Page"]' + ).should("not.exist"); + cy.get( + '.standard-sidebar-section:last .sidebar-item-container[item-name="Test Private Page 1"]' + ).should("exist"); + + cy.wait("@page_updated"); + }); + + it("Add New Block", () => { + cy.get('.sidebar-item-container[item-name="Duplicate Page"]').as("sidebar-item"); + + cy.get("@sidebar-item").find(".standard-sidebar-item").first().click(); + + cy.get(".ce-block").click().type("{enter}"); + cy.get(".block-list-container .block-list-item").contains("Heading").click(); + cy.get(":focus").type("Header"); + cy.get(".ce-block:last").find(".ce-header").should("exist"); + + cy.get(".ce-block:last").click().type("{enter}"); + cy.get(".block-list-container .block-list-item").contains("Text").click(); + cy.get(":focus").type("Paragraph text"); + cy.get(".ce-block:last").find(".ce-paragraph").should("exist"); + }); + + it("Delete A Block", () => { + cy.get(":focus").click(); + cy.get(".paragraph-control .setting-btn").click(); + cy.get(".paragraph-control .dropdown-item").contains("Delete").click(); + cy.get(".ce-block:last").find(".ce-paragraph").should("not.exist"); + }); + + it("Shrink and Expand A Block", () => { + cy.get(":focus").click(); + cy.get(".ce-block:last .setting-btn").click(); + cy.get(".ce-block:last .dropdown-item").contains("Shrink").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-11"); + cy.get(".ce-block:last .dropdown-item").contains("Shrink").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-10"); + cy.get(".ce-block:last .dropdown-item").contains("Shrink").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-9"); + cy.get(".ce-block:last .dropdown-item").contains("Expand").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-10"); + cy.get(".ce-block:last .dropdown-item").contains("Expand").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-11"); + cy.get(".ce-block:last .dropdown-item").contains("Expand").click(); + cy.get(".ce-block:last").should("have.class", "col-xs-12"); + + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + }); + + it("Hide/Unhide Workspaces", () => { + // hide + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.hide_page", + }).as("hide_page"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + cy.get('.sidebar-item-container[item-name="Duplicate Page"]') + .find(".sidebar-item-control .setting-btn") + .click(); + cy.get('.sidebar-item-container[item-name="Duplicate Page"]') + .find('.dropdown-item[title="Hide Workspace"]') + .click({ force: true }); + cy.wait(300); + cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click(); + cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.be.visible"); + + cy.wait("@hide_page"); + + // unhide + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.unhide_page", + }).as("unhide_page"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + cy.get('.sidebar-item-container[item-name="Duplicate Page"]') + .find('[title="Unhide Workspace"]') + .click({ force: true }); + cy.wait(300); + + cy.get('.standard-actions .btn-secondary[data-label="Discard"]').click(); + cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("be.visible"); + + cy.wait("@unhide_page"); + }); + + it("Delete Duplicate Page", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.delete_page", + }).as("page_deleted"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + cy.get('.sidebar-item-container[item-name="Duplicate Page"]') + .find(".sidebar-item-control .setting-btn") + .click(); + cy.get('.sidebar-item-container[item-name="Duplicate Page"]') + .find('.dropdown-item[title="Delete Workspace"]') + .click({ force: true }); + cy.wait(300); + cy.get(".modal-footer > .standard-actions > .btn-modal-primary:visible").first().click(); + cy.get('.sidebar-item-container[item-name="Duplicate Page"]').should("not.exist"); + + cy.wait("@page_deleted"); + }); +}); diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js new file mode 100644 index 0000000..57ca332 --- /dev/null +++ b/cypress/integration/workspace_blocks.js @@ -0,0 +1,185 @@ +context("Workspace Blocks", () => { + before(() => { + cy.login(); + cy.visit("/app"); + return cy + .window() + .its("xhiveframework") + .then((xhiveframework) => { + return xhiveframework.xcall("xhiveframework.tests.ui_test_helpers.setup_workflow"); + }); + }); + + it("Create Test Page", () => { + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.doctype.workspace.workspace.new_page", + }).as("new_page"); + + cy.visit("/app/website"); + cy.get(".codex-editor__redactor .ce-block"); + cy.get('.custom-actions button[data-label="Create%20Workspace"]').click(); + cy.fill_field("title", "Test Block Page", "Data"); + cy.get_open_dialog().find(".modal-header").click(); + cy.get_open_dialog().find(".btn-primary").click(); + + // check if sidebar item is added in private section + cy.get('.sidebar-item-container[item-name="Test Block Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + cy.wait(300); + cy.get('.sidebar-item-container[item-name="Test Block Page"]').should( + "have.attr", + "item-public", + "0" + ); + + cy.wait("@new_page"); + }); + + it.skip("Quick List Block", () => { + cy.create_records([ + { + doctype: "ToDo", + description: "Quick List ToDo 1", + status: "Open", + }, + { + doctype: "ToDo", + description: "Quick List ToDo 2", + status: "Open", + }, + { + doctype: "ToDo", + description: "Quick List ToDo 3", + status: "Open", + }, + { + doctype: "ToDo", + description: "Quick List ToDo 4", + status: "Open", + }, + ]); + + cy.intercept({ + method: "GET", + url: "api/method/xhiveframework.desk.form.load.getdoctype?**", + }).as("get_doctype"); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + // test quick list creation + cy.get(".ce-block").first().click({ force: true }).type("{enter}"); + cy.get(".block-list-container .block-list-item").contains("Quick List").click(); + + cy.fill_field("label", "ToDo", "Data"); + cy.fill_field("document_type", "ToDo", "Link").blur(); + cy.wait("@get_doctype"); + + cy.get_open_dialog().find(".filter-edit-area").should("contain", "No filters selected"); + cy.get_open_dialog().find(".filter-area .add-filter").click(); + + cy.get_open_dialog() + .find(".fieldname-select-area input") + .type("Workflow State{enter}") + .blur(); + cy.get_open_dialog().find(".filter-field .input-with-feedback").type("Pending"); + + cy.get_open_dialog().find(".modal-header").click(); + cy.get_open_dialog().find(".btn-primary").click(); + + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + + cy.get(".codex-editor__redactor .ce-block"); + + cy.get(".ce-block .quick-list-widget-box").first().as("todo-quick-list"); + + cy.get("@todo-quick-list").find(".quick-list-item .status").should("contain", "Pending"); + + // test quick-list-item + cy.get("@todo-quick-list") + .find(".quick-list-item .title") + .first() + .invoke("attr", "title") + .then((title) => { + cy.get("@todo-quick-list").find(".quick-list-item").contains(title).click(); + cy.get_field("description", "Text Editor").should("contain", title); + cy.click_action_button("Approve"); + }); + cy.go("back"); + + // test filter-list + cy.get("@todo-quick-list").realHover().find(".widget-control .filter-list").click(); + + cy.get_open_dialog() + .find(".filter-field .input-with-feedback") + .focus() + .type("{selectall}Approved"); + cy.get_open_dialog().find(".modal-header").click(); + cy.get_open_dialog().find(".btn-primary").click(); + + cy.get("@todo-quick-list").find(".quick-list-item .status").should("contain", "Approved"); + + // test refresh-list + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.desk.reportview.get", + }).as("refresh-list"); + + cy.get("@todo-quick-list").realHover().find(".widget-control .refresh-list").click(); + cy.wait("@refresh-list"); + + // test add-new + cy.get("@todo-quick-list").realHover().find(".widget-control .add-new").click(); + cy.url().should("include", `/todo/new-todo-1`); + cy.go("back"); + + // test see-all + cy.get("@todo-quick-list").find(".widget-footer .see-all").click(); + cy.open_list_filter(); + cy.get('.filter-field input[data-fieldname="workflow_state"]') + .invoke("val") + .should("eq", "Pending"); + cy.go("back"); + }); + + it("Number Card Block", () => { + cy.create_records([ + { + doctype: "Number Card", + label: "Test Number Card", + document_type: "ToDo", + color: "#f74343", + }, + ]); + + cy.get(".codex-editor__redactor .ce-block"); + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + + cy.get(".ce-block").first().click({ force: true }).type("{enter}"); + cy.get(".block-list-container .block-list-item").contains("Number Card").click(); + + // add number card + cy.fill_field("number_card_name", "Test Number Card", "Link"); + cy.get('[data-fieldname="number_card_name"] ul li').contains("Test Number Card").click(); + cy.click_modal_primary_button("Add"); + cy.get(".ce-block .number-widget-box").first().as("number_card"); + cy.get("@number_card").find(".widget-title").should("contain", "Test Number Card"); + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + cy.get("@number_card").find(".widget-title").should("contain", "Test Number Card"); + + // edit number card + cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); + cy.get("@number_card").realHover().find(".widget-control .edit-button").click(); + cy.get_field("label", "Data").invoke("val", "ToDo Count"); + cy.click_modal_primary_button("Save"); + cy.get("@number_card").find(".widget-title").should("contain", "ToDo Count"); + cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); + cy.get("@number_card").find(".widget-title").should("contain", "ToDo Count"); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000..b132753 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = (on, config) => { + require("@cypress/code-coverage/task")(on, config); + return config; +}; diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000..cd23b6b --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,550 @@ +import "@testing-library/cypress/add-commands"; +import "@4tw/cypress-drag-drop"; +import "cypress-real-events/support"; +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }); +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }); +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }); +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }); + +Cypress.Commands.add("login", (email, password) => { + if (!email) { + email = Cypress.config("testUser") || "Administrator"; + } + if (!password) { + password = Cypress.env("adminPassword"); + } + // cy.session clears all localStorage on new login, so we need to retain the last route + const session_last_route = window.localStorage.getItem("session_last_route"); + return cy + .session( + [email, password] || "", + () => { + return cy.request({ + url: "/api/method/login", + method: "POST", + body: { + usr: email, + pwd: password, + }, + }); + }, + { + cacheAcrossSpecs: true, + } + ) + .then(() => { + if (session_last_route) { + window.localStorage.setItem("session_last_route", session_last_route); + } + }); +}); + +Cypress.Commands.add("call", (method, args) => { + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + url: `/api/method/${method}`, + method: "POST", + body: args, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + }) + .then((res) => { + expect(res.status).eq(200); + if (method === "logout") { + Cypress.session.clearAllSavedSessions(); + } + return res.body; + }); + }); +}); + +Cypress.Commands.add("get_list", (doctype, fields = [], filters = []) => { + filters = JSON.stringify(filters); + fields = JSON.stringify(fields); + let url = `/api/resource/${doctype}?fields=${fields}&filters=${filters}`; + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + method: "GET", + url, + headers: { + Accept: "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + }) + .then((res) => { + expect(res.status).eq(200); + return res.body; + }); + }); +}); + +Cypress.Commands.add("get_doc", (doctype, name) => { + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + method: "GET", + url: `/api/resource/${doctype}/${name}`, + headers: { + Accept: "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + }) + .then((res) => { + expect(res.status).eq(200); + return res.body; + }); + }); +}); + +Cypress.Commands.add("remove_doc", (doctype, name) => { + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + method: "DELETE", + url: `/api/resource/${doctype}/${name}`, + headers: { + Accept: "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + }) + .then((res) => { + expect(res.status).eq(202); + return res.body; + }); + }); +}); + +Cypress.Commands.add("create_records", (doc) => { + return cy + .call("xhiveframework.tests.ui_test_helpers.create_if_not_exists", { doc: JSON.stringify(doc) }) + .then((r) => r.message); +}); + +Cypress.Commands.add("set_value", (doctype, name, obj) => { + return cy.call("xhiveframework.client.set_value", { + doctype, + name, + fieldname: obj, + }); +}); + +Cypress.Commands.add("fill_field", (fieldname, value, fieldtype = "Data") => { + cy.get_field(fieldname, fieldtype).as("input"); + + if (["Date", "Time", "Datetime"].includes(fieldtype)) { + cy.get("@input").click().wait(200); + cy.get(".datepickers-container .datepicker.active").should("exist"); + } + if (fieldtype === "Time") { + cy.get("@input").clear().wait(200); + } + + if (fieldtype === "Select") { + cy.get("@input").select(value); + } else { + cy.get("@input").type(value, { + waitForAnimations: false, + parseSpecialCharSequences: false, + force: true, + delay: 100, + }); + } + return cy.get("@input"); +}); + +Cypress.Commands.add("get_field", (fieldname, fieldtype = "Data") => { + let field_element = fieldtype === "Select" ? "select" : "input"; + let selector = `[data-fieldname="${fieldname}"] ${field_element}:visible`; + + if (fieldtype === "Text Editor") { + selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]:visible`; + } + if (fieldtype === "Code") { + selector = `[data-fieldname="${fieldname}"] .ace_text-input`; + } + if (fieldtype === "Markdown Editor") { + selector = `[data-fieldname="${fieldname}"] .ace-editor-target`; + } + + return cy.get(selector).first(); +}); + +Cypress.Commands.add( + "fill_table_field", + (tablefieldname, row_idx, fieldname, value, fieldtype = "Data") => { + cy.get_table_field(tablefieldname, row_idx, fieldname, fieldtype).as("input"); + + if (["Date", "Time", "Datetime"].includes(fieldtype)) { + cy.get("@input").click().wait(200); + cy.get(".datepickers-container .datepicker.active").should("exist"); + } + if (fieldtype === "Time") { + cy.get("@input").clear().wait(200); + } + + if (fieldtype === "Select") { + cy.get("@input").select(value); + } else { + cy.get("@input").type(value, { waitForAnimations: false, force: true }); + } + return cy.get("@input"); + } +); + +Cypress.Commands.add( + "get_table_field", + (tablefieldname, row_idx, fieldname, fieldtype = "Data") => { + let selector = `.xhiveframework-control[data-fieldname="${tablefieldname}"]`; + selector += ` [data-idx="${row_idx}"]`; + + if (fieldtype === "Text Editor") { + selector += ` [data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]`; + } else if (fieldtype === "Code") { + selector += ` [data-fieldname="${fieldname}"] .ace_text-input`; + } else { + selector += ` [data-fieldname="${fieldname}"]`; + return cy.get(selector).find(".form-control:visible, .static-area:visible").first(); + } + return cy.get(selector); + } +); + +Cypress.Commands.add("awesomebar", (text) => { + cy.get("#navbar-search").type(`${text}{downarrow}{enter}`, { delay: 700 }); +}); + +Cypress.Commands.add("new_form", (doctype) => { + let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); + cy.visit(`/app/${dt_in_route}/new`); + cy.get("body").should(($body) => { + const dataRoute = $body.attr("data-route"); + expect(dataRoute).to.match(new RegExp(`^Form/${doctype}/new-${dt_in_route}-`)); + }); + cy.get("body").should("have.attr", "data-ajax-state", "complete"); +}); + +Cypress.Commands.add("select_form_tab", (label) => { + cy.get(".form-tabs-list [data-toggle='tab']").contains(label).click().wait(500); +}); + +Cypress.Commands.add("go_to_list", (doctype) => { + let dt_in_route = doctype.toLowerCase().replace(/ /g, "-"); + cy.visit(`/app/${dt_in_route}`); +}); + +Cypress.Commands.add("clear_cache", () => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + xhiveframework.ui.toolbar.clear_cache(); + }); +}); + +Cypress.Commands.add("dialog", (opts) => { + return cy + .window({ log: false }) + .its("xhiveframework", { log: false }) + .then((xhiveframework) => { + Cypress.log({ + name: "dialog", + displayName: "dialog", + message: "xhiveframework.ui.Dialog", + consoleProps: () => { + return { + options: opts, + dialog: d, + }; + }, + }); + + var d = new xhiveframework.ui.Dialog(opts); + d.show(); + return d; + }); +}); + +Cypress.Commands.add("get_open_dialog", () => { + return cy.get(".modal:visible").last(); +}); + +Cypress.Commands.add("save", () => { + cy.intercept("/api/method/xhiveframework.desk.form.save.savedocs").as("save_call"); + cy.get(`.page-container:visible button[data-label="Save"]`).click({ force: true }); + cy.wait("@save_call"); +}); +Cypress.Commands.add("hide_dialog", () => { + cy.wait(500); + cy.get_open_dialog().focus().find(".btn-modal-close").click(); + cy.get(".modal:visible").should("not.exist"); +}); + +Cypress.Commands.add("clear_dialogs", () => { + cy.window().then((win) => { + win.$(".modal, .modal-backdrop").remove(); + }); + cy.get(".modal").should("not.exist"); +}); + +Cypress.Commands.add("clear_datepickers", () => { + cy.window().then((win) => { + win.$(".datepicker").remove(); + }); + cy.get(".datepicker").should("not.exist"); +}); + +Cypress.Commands.add("insert_doc", (doctype, args, ignore_duplicate) => { + if (!args.doctype) { + args.doctype = doctype; + } + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + method: "POST", + url: `/api/resource/${doctype}`, + body: args, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + failOnStatusCode: !ignore_duplicate, + }) + .then((res) => { + let status_codes = [200]; + if (ignore_duplicate) { + status_codes.push(409); + } + + let message = null; + if (ignore_duplicate && !status_codes.includes(res.status)) { + message = `Document insert failed, response: ${JSON.stringify( + res, + null, + "\t" + )}`; + } + expect(res.status).to.be.oneOf(status_codes, message); + return res.body.data; + }); + }); +}); + +Cypress.Commands.add("update_doc", (doctype, docname, args) => { + return cy + .window() + .its("xhiveframework.csrf_token") + .then((csrf_token) => { + return cy + .request({ + method: "PUT", + url: `/api/resource/${doctype}/${docname}`, + body: args, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-XhiveFramework-CSRF-Token": csrf_token, + }, + }) + .then((res) => { + expect(res.status).to.eq(200); + return res.body.data; + }); + }); +}); + +Cypress.Commands.add("switch_to_user", (user) => { + cy.call("logout"); + cy.wait(200); + cy.login(user); + cy.reload(); +}); + +Cypress.Commands.add("add_role", (user, role) => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + const session_user = xhiveframework.session.user; + add_remove_role("add", user, role, session_user); + }); +}); + +Cypress.Commands.add("remove_role", (user, role) => { + cy.window() + .its("xhiveframework") + .then((xhiveframework) => { + const session_user = xhiveframework.session.user; + add_remove_role("remove", user, role, session_user); + }); +}); + +const add_remove_role = (action, user, role, session_user) => { + if (session_user !== "Administrator") { + cy.switch_to_user("Administrator"); + } + + cy.call("xhiveframework.tests.ui_test_helpers.add_remove_role", { + action: action, + user: user, + role: role, + }); + + if (session_user !== "Administrator") { + cy.switch_to_user(session_user); + } +}; + +Cypress.Commands.add("open_list_filter", () => { + cy.get(".filter-section .filter-button").click(); + cy.wait(300); + cy.get(".filter-popover").should("exist"); +}); + +Cypress.Commands.add("click_custom_action_button", (name) => { + cy.get(`.custom-actions [data-label="${encodeURIComponent(name)}"]`).click(); +}); + +Cypress.Commands.add("click_action_button", (name) => { + cy.findByRole("button", { name: "Actions" }).click(); + cy.get(`.actions-btn-group [data-label="${encodeURIComponent(name)}"]`).click(); +}); + +Cypress.Commands.add("click_menu_button", (name) => { + cy.get(".standard-actions .menu-btn-group > .btn").click(); + cy.get(`.menu-btn-group [data-label="${encodeURIComponent(name)}"]`).click(); +}); + +Cypress.Commands.add("clear_filters", () => { + let has_filter = false; + cy.intercept({ + method: "POST", + url: "api/method/xhiveframework.model.utils.user_settings.save", + }).as("filter-saved"); + cy.get(".filter-section .filter-button").click({ force: true }); + cy.wait(300); + cy.get(".filter-popover").should("exist"); + cy.get(".filter-popover").then((popover) => { + if (popover.find("input.input-with-feedback")[0].value != "") { + has_filter = true; + } + }); + cy.get(".filter-popover").find(".clear-filters").click(); + cy.get(".filter-section .filter-button").click(); + cy.window() + .its("cur_list") + .then((cur_list) => { + cur_list && cur_list.filter_area && cur_list.filter_area.clear(); + has_filter && cy.wait("@filter-saved"); + }); +}); + +Cypress.Commands.add("click_modal_primary_button", (btn_name) => { + cy.wait(400); + cy.get(".modal-footer > .standard-actions > .btn-primary") + .contains(btn_name) + .click({ force: true }); +}); + +Cypress.Commands.add("click_sidebar_button", (btn_name) => { + cy.get(".list-group-by-fields .list-link > a").contains(btn_name).click({ force: true }); +}); + +Cypress.Commands.add("click_listview_row_item", (row_no) => { + cy.get(".list-row > .level-left > .list-subject > .level-item > .ellipsis") + .eq(row_no) + .click({ force: true }); +}); + +Cypress.Commands.add("click_listview_row_item_with_text", (text) => { + cy.get(".list-row > .level-left > .list-subject > .level-item > .ellipsis") + .contains(text) + .first() + .click({ force: true }); +}); + +Cypress.Commands.add("click_filter_button", () => { + cy.get(".filter-button").click(); +}); + +Cypress.Commands.add("click_listview_primary_button", (btn_name) => { + cy.get(".primary-action").contains(btn_name).click({ force: true }); +}); + +Cypress.Commands.add("click_doc_primary_button", (btn_name) => { + cy.get(".primary-action").contains(btn_name).click({ force: true }); +}); + +Cypress.Commands.add("click_timeline_action_btn", (btn_name) => { + cy.get(".timeline-message-box .actions .action-btn").contains(btn_name).click(); +}); + +Cypress.Commands.add("select_listview_row_checkbox", (row_no) => { + cy.get(".xhiveframework-list .select-like > .list-row-checkbox").eq(row_no).click(); +}); + +Cypress.Commands.add("click_form_section", (section_name) => { + cy.get(".section-head").contains(section_name).click(); +}); + +const compare_document = (expected, actual) => { + for (const prop in expected) { + if (expected[prop] instanceof Array) { + // recursively compare child documents. + expected[prop].forEach((item, idx) => { + compare_document(item, actual[prop][idx]); + }); + } else { + assert.equal(expected[prop], actual[prop], `${prop} should be equal.`); + } + } +}; + +Cypress.Commands.add("compare_document", (expected_document) => { + cy.window() + .its("cur_frm") + .then((frm) => { + // Don't remove this, cypress can't magically wait for events it has no control over. + cy.wait(1000); + compare_document(expected_document, frm.doc); + }); +}); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js new file mode 100644 index 0000000..10fb377 --- /dev/null +++ b/cypress/support/e2e.js @@ -0,0 +1,25 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import "./commands"; +import "@cypress/code-coverage/support"; + +Cypress.on("uncaught:exception", (err, runnable) => { + return false; +}); + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000..d90ebf6 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "allowJs": true, + "baseUrl": "../node_modules", + "types": [ + "cypress" + ] + }, + "include": [ + "**/*.*" + ] +} \ No newline at end of file diff --git a/esbuild/build-cleanup.js b/esbuild/build-cleanup.js new file mode 100644 index 0000000..77b3c0c --- /dev/null +++ b/esbuild/build-cleanup.js @@ -0,0 +1,30 @@ +const path = require("path"); +const fs = require("fs"); +const glob = require("fast-glob"); + +module.exports = { + name: "build_cleanup", + setup(build) { + build.onEnd((result) => { + if (result.errors.length) return; + clean_dist_files(Object.keys(result.metafile.outputs)); + }); + }, +}; + +function clean_dist_files(new_files) { + new_files.forEach((file) => { + if (file.endsWith(".map")) return; + + const pattern = file.split(".").slice(0, -2).join(".") + "*"; + glob.sync(pattern).forEach((file_to_delete) => { + if (file_to_delete.startsWith(file)) return; + + fs.unlink(path.resolve(file_to_delete), (err) => { + if (!err) return; + + console.error(`Error deleting ${file.split(path.sep).pop()}`); + }); + }); + }); +} diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js new file mode 100644 index 0000000..2e39846 --- /dev/null +++ b/esbuild/esbuild.js @@ -0,0 +1,525 @@ +const path = require("path"); +const fs = require("fs"); +const glob = require("fast-glob"); +const esbuild = require("esbuild"); +const vue = require("esbuild-plugin-vue3"); +const yargs = require("yargs"); +const cliui = require("cliui")(); +const chalk = require("chalk"); +const html_plugin = require("./xhiveframework-html"); +const vue_style_plugin = require("./xhiveframework-vue-style"); +const rtlcss = require("rtlcss"); +const postCssPlugin = require("@xhiveframework/esbuild-plugin-postcss2").default; +const ignore_assets = require("./ignore-assets"); +const sass_options = require("./sass_options"); +const build_cleanup_plugin = require("./build-cleanup"); + +const { + app_list, + assets_path, + apps_path, + sites_path, + get_app_path, + get_public_path, + log, + log_warn, + log_error, + bench_path, + get_redis_subscriber, +} = require("./utils"); + +const argv = yargs + .usage("Usage: node esbuild [options]") + .option("apps", { + type: "string", + description: "Run build for specific apps", + }) + .option("skip_xhiveframework", { + type: "boolean", + description: "Skip building xhiveframework assets", + }) + .option("files", { + type: "string", + description: "Run build for specified bundles", + }) + .option("watch", { + type: "boolean", + description: "Run in watch mode and rebuild on file changes", + }) + .option("live-reload", { + type: "boolean", + description: `Automatically reload Desk when assets are rebuilt. + Can only be used with the --watch flag.`, + }) + .option("production", { + type: "boolean", + description: "Run build in production mode", + }) + .option("run-build-command", { + type: "boolean", + description: "Run build command for apps", + }) + .option("save-metafiles", { + type: "boolean", + description: + "Saves esbuild metafiles for built assets. Useful for analyzing bundle size. More info: https://esbuild.github.io/api/#metafile", + }) + .example("node esbuild --apps xhiveframework,xhiveerp", "Run build only for xhiveframework and xhiveerp") + .example( + "node esbuild --files xhiveframework/website.bundle.js,xhiveframework/desk.bundle.js", + "Run build only for specified bundles" + ) + .version(false).argv; + +const APPS = (!argv.apps ? app_list : argv.apps.split(",")).filter( + (app) => !(argv.skip_xhiveframework && app == "xhiveframework") +); +const FILES_TO_BUILD = argv.files ? argv.files.split(",") : []; +const WATCH_MODE = Boolean(argv.watch); +const PRODUCTION = Boolean(argv.production); +const RUN_BUILD_COMMAND = !WATCH_MODE && Boolean(argv["run-build-command"]); + +const TOTAL_BUILD_TIME = `${chalk.black.bgGreen(" DONE ")} Total Build Time`; +const NODE_PATHS = [].concat( + // node_modules of apps directly importable + app_list + .map((app) => path.resolve(get_app_path(app), "../node_modules")) + .filter(fs.existsSync), + // import js file of any app if you provide the full path + app_list.map((app) => path.resolve(get_app_path(app), "..")).filter(fs.existsSync) +); + +execute().catch((e) => { + console.error(e); + process.exit(1); +}); + +if (WATCH_MODE) { + // listen for open files in editor event + open_in_editor(); +} + +async function execute() { + console.time(TOTAL_BUILD_TIME); + + let results; + try { + results = await build_assets_for_apps(APPS, FILES_TO_BUILD); + } catch (e) { + log_error("There were some problems during build"); + log(); + log(chalk.dim(e.stack)); + if (process.env.CI || PRODUCTION) { + process.kill(process.pid); + } + return; + } + + if (!WATCH_MODE) { + log_built_assets(results); + console.timeEnd(TOTAL_BUILD_TIME); + log(); + } else { + log("Watching for changes..."); + } + for (const result of results) { + await write_assets_json(result.metafile); + } + RUN_BUILD_COMMAND && run_build_command_for_apps(APPS); + if (!WATCH_MODE) { + process.exit(0); + } +} + +function build_assets_for_apps(apps, files) { + let { include_patterns, ignore_patterns } = files.length + ? get_files_to_build(files) + : get_all_files_to_build(apps); + + return glob(include_patterns, { ignore: ignore_patterns }).then((files) => { + let output_path = assets_path; + + let file_map = {}; + let style_file_map = {}; + let rtl_style_file_map = {}; + for (let file of files) { + let relative_app_path = path.relative(apps_path, file); + let app = relative_app_path.split(path.sep)[0]; + + let extension = path.extname(file); + let output_name = path.basename(file, extension); + if ([".css", ".scss", ".less", ".sass", ".styl"].includes(extension)) { + output_name = path.join("css", output_name); + } else if ([".js", ".ts"].includes(extension)) { + output_name = path.join("js", output_name); + } + output_name = path.join(app, "dist", output_name); + + if ( + Object.keys(file_map).includes(output_name) || + Object.keys(style_file_map).includes(output_name) + ) { + log_warn(`Duplicate output file ${output_name} generated from ${file}`); + } + if ([".css", ".scss", ".less", ".sass", ".styl"].includes(extension)) { + style_file_map[output_name] = file; + rtl_style_file_map[output_name.replace("/css/", "/css-rtl/")] = file; + } else { + file_map[output_name] = file; + } + } + let build = build_files({ + files: file_map, + outdir: output_path, + }); + let style_build = build_style_files({ + files: style_file_map, + outdir: output_path, + }); + let rtl_style_build = build_style_files({ + files: rtl_style_file_map, + outdir: output_path, + rtl_style: true, + }); + return Promise.all([build, style_build, rtl_style_build]); + }); +} + +function get_all_files_to_build(apps) { + let include_patterns = []; + let ignore_patterns = []; + + for (let app of apps) { + let public_path = get_public_path(app); + include_patterns.push( + path.resolve(public_path, "**", "*.bundle.{js,ts,css,sass,scss,less,styl,jsx}") + ); + ignore_patterns.push( + path.resolve(public_path, "node_modules"), + path.resolve(public_path, "dist") + ); + } + + return { + include_patterns, + ignore_patterns, + }; +} + +function get_files_to_build(files) { + // files: ['xhiveframework/website.bundle.js', 'xhiveerp/main.bundle.js'] + let include_patterns = []; + let ignore_patterns = []; + + for (let file of files) { + let [app, bundle] = file.split("/"); + let public_path = get_public_path(app); + include_patterns.push(path.resolve(public_path, "**", bundle)); + ignore_patterns.push( + path.resolve(public_path, "node_modules"), + path.resolve(public_path, "dist") + ); + } + + return { + include_patterns, + ignore_patterns, + }; +} + +function build_files({ files, outdir }) { + let build_plugins = [vue(), html_plugin, build_cleanup_plugin, vue_style_plugin]; + return esbuild.build(get_build_options(files, outdir, build_plugins)); +} + +function build_style_files({ files, outdir, rtl_style = false }) { + let plugins = []; + if (rtl_style) { + plugins.push(rtlcss); + } + + let build_plugins = [ + ignore_assets, + build_cleanup_plugin, + postCssPlugin({ + plugins: plugins, + sassOptions: sass_options, + }), + ]; + + plugins.push(require("autoprefixer")); + return esbuild.build(get_build_options(files, outdir, build_plugins)); +} + +function get_build_options(files, outdir, plugins) { + return { + entryPoints: files, + entryNames: "[dir]/[name].[hash]", + target: ["es2017"], + outdir, + sourcemap: true, + bundle: true, + metafile: true, + minify: PRODUCTION, + nodePaths: NODE_PATHS, + define: { + "process.env.NODE_ENV": JSON.stringify(PRODUCTION ? "production" : "development"), + __VUE_OPTIONS_API__: JSON.stringify(true), + __VUE_PROD_DEVTOOLS__: JSON.stringify(false), + }, + plugins: plugins, + watch: get_watch_config(), + }; +} + +function get_watch_config() { + if (WATCH_MODE) { + return { + async onRebuild(error, result) { + if (error) { + log_error("There was an error during rebuilding changes."); + log(); + log(chalk.dim(error.stack)); + notify_redis({ error }); + } else { + let { new_assets_json, prev_assets_json } = await write_assets_json( + result.metafile + ); + + let changed_files; + if (prev_assets_json) { + changed_files = get_rebuilt_assets(prev_assets_json, new_assets_json); + + let timestamp = new Date().toLocaleTimeString(); + let message = `${timestamp}: Compiled ${changed_files.length} files...`; + log(chalk.yellow(message)); + for (let filepath of changed_files) { + let filename = path.basename(filepath); + log(" " + filename); + } + log(); + } + notify_redis({ success: true, changed_files }); + } + }, + }; + } + return null; +} + +function log_built_assets(results) { + let outputs = {}; + for (const result of results) { + outputs = Object.assign(outputs, result.metafile.outputs); + } + let column_widths = [60, 20]; + cliui.div( + { + text: chalk.cyan.bold("File"), + width: column_widths[0], + }, + { + text: chalk.cyan.bold("Size"), + width: column_widths[1], + } + ); + cliui.div(""); + + let output_by_dist_path = {}; + for (let outfile in outputs) { + if (outfile.endsWith(".map")) continue; + let data = outputs[outfile]; + outfile = path.resolve(outfile); + outfile = path.relative(assets_path, outfile); + let filename = path.basename(outfile); + let dist_path = outfile.replace(filename, ""); + output_by_dist_path[dist_path] = output_by_dist_path[dist_path] || []; + output_by_dist_path[dist_path].push({ + name: filename, + size: (data.bytes / 1000).toFixed(2) + " Kb", + }); + } + + for (let dist_path in output_by_dist_path) { + let files = output_by_dist_path[dist_path]; + cliui.div({ + text: dist_path, + width: column_widths[0], + }); + + for (let i in files) { + let file = files[i]; + let branch = ""; + if (i < files.length - 1) { + branch = "├─ "; + } else { + branch = "└─ "; + } + let color = file.name.endsWith(".js") ? "green" : "blue"; + cliui.div( + { + text: branch + chalk[color]("" + file.name), + width: column_widths[0], + }, + { + text: file.size, + width: column_widths[1], + } + ); + } + cliui.div(""); + } + log(cliui.toString()); +} + +// to store previous build's assets.json for comparison +let prev_assets_json; +let curr_assets_json; + +async function write_assets_json(metafile) { + let rtl = false; + prev_assets_json = curr_assets_json; + let out = {}; + for (let output in metafile.outputs) { + let info = metafile.outputs[output]; + let asset_path = "/" + path.relative(sites_path, output); + if (info.entryPoint) { + let key = path.basename(info.entryPoint); + if (key.endsWith(".css") && asset_path.includes("/css-rtl/")) { + rtl = true; + key = `rtl_${key}`; + } + out[key] = asset_path; + } + } + + let assets_json_path = path.resolve(assets_path, `assets${rtl ? "-rtl" : ""}.json`); + let assets_json; + try { + assets_json = await fs.promises.readFile(assets_json_path, "utf-8"); + } catch (error) { + assets_json = "{}"; + } + assets_json = JSON.parse(assets_json); + // update with new values + let new_assets_json = Object.assign({}, assets_json, out); + curr_assets_json = new_assets_json; + + await fs.promises.writeFile(assets_json_path, JSON.stringify(new_assets_json, null, 4)); + await update_assets_json_in_cache(); + if (argv["save-metafiles"]) { + // use current timestamp in readable formate as a suffix for filename + let current_timestamp = new Date().getTime(); + const metafile_name = `meta-${current_timestamp}.json`; + await fs.promises.writeFile(`${metafile_name}`, JSON.stringify(metafile)); + log(`Saved metafile as ${metafile_name}`); + } + return { + new_assets_json, + prev_assets_json, + }; +} + +async function update_assets_json_in_cache() { + // update assets_json cache in redis, so that it can be read directly by python + let client = get_redis_subscriber("redis_cache"); + // handle error event to avoid printing stack traces + try { + await client.connect(); + } catch (e) { + log_warn("Cannot connect to redis_cache to update assets_json"); + } + client.del("assets_json", (err) => { + client.unref(); + }); +} + +function run_build_command_for_apps(apps) { + let cwd = process.cwd(); + let { execSync } = require("child_process"); + + for (let app of apps) { + if (app === "xhiveframework") continue; + + let root_app_path = path.resolve(get_app_path(app), ".."); + let package_json = path.resolve(root_app_path, "package.json"); + if (fs.existsSync(package_json)) { + let { scripts } = require(package_json); + if (scripts && scripts.build) { + log("\nRunning build command for", chalk.bold(app)); + process.chdir(root_app_path); + execSync("yarn build", { encoding: "utf8", stdio: "inherit" }); + } + } + } + + process.chdir(cwd); +} + +async function notify_redis({ error, success, changed_files }) { + // notify redis which in turns tells socketio to publish this to browser + let subscriber = get_redis_subscriber("redis_queue"); + try { + await subscriber.connect(); + } catch (e) { + log_warn("Cannot connect to redis_queue for browser events"); + } + + let payload = null; + if (error) { + let formatted = await esbuild.formatMessages(error.errors, { + kind: "error", + terminalWidth: 100, + }); + let stack = error.stack.replace(new RegExp(bench_path, "g"), ""); + payload = { + error, + formatted, + stack, + }; + } + if (success) { + payload = { + success: true, + changed_files, + live_reload: argv["live-reload"], + }; + } + + await subscriber.publish( + "events", + JSON.stringify({ + event: "build_event", + message: payload, + }) + ); +} + +async function open_in_editor() { + let subscriber = get_redis_subscriber("redis_queue"); + try { + await subscriber.connect(); + } catch (e) { + log_warn("Cannot connect to redis_queue for open_in_editor events"); + } + subscriber.subscribe("open_in_editor", (file) => { + file = JSON.parse(file); + let file_path = path.resolve(file.file); + log("Opening file in editor:", file_path); + let launch = require("launch-editor"); + launch(`${file_path}:${file.line}:${file.column}`); + }); +} + +function get_rebuilt_assets(prev_assets, new_assets) { + let added_files = []; + let old_files = Object.values(prev_assets); + let new_files = Object.values(new_assets); + + for (let filepath of new_files) { + if (!old_files.includes(filepath)) { + added_files.push(filepath); + } + } + return added_files; +} diff --git a/esbuild/ignore-assets.js b/esbuild/ignore-assets.js new file mode 100644 index 0000000..eb5e00a --- /dev/null +++ b/esbuild/ignore-assets.js @@ -0,0 +1,11 @@ +module.exports = { + name: "xhiveframework-ignore-asset", + setup(build) { + build.onResolve({ filter: /^\/assets\// }, (args) => { + return { + path: args.path, + external: true, + }; + }); + }, +}; diff --git a/esbuild/index.js b/esbuild/index.js new file mode 100644 index 0000000..2721673 --- /dev/null +++ b/esbuild/index.js @@ -0,0 +1 @@ +require("./esbuild"); diff --git a/esbuild/sass_options.js b/esbuild/sass_options.js new file mode 100644 index 0000000..f6a7b11 --- /dev/null +++ b/esbuild/sass_options.js @@ -0,0 +1,24 @@ +let path = require("path"); +let { get_app_path, app_list } = require("./utils"); + +let node_modules_path = path.resolve(get_app_path("xhiveframework"), "..", "node_modules"); +let app_paths = app_list.map(get_app_path).map((app_path) => path.resolve(app_path, "..")); + +module.exports = { + includePaths: [node_modules_path, ...app_paths], + quietDeps: true, + importer: function (url) { + if (url.startsWith("~")) { + // strip ~ so that it can resolve from node_modules + url = url.slice(1); + } + if (url.endsWith(".css")) { + // strip .css from end of path + url = url.slice(0, -4); + } + // normal file, let it go + return { + file: url, + }; + }, +}; diff --git a/esbuild/utils.js b/esbuild/utils.js new file mode 100644 index 0000000..de4a0e3 --- /dev/null +++ b/esbuild/utils.js @@ -0,0 +1,146 @@ +const path = require("path"); +const fs = require("fs"); +const chalk = require("chalk"); + +const xhiveframework_path = path.resolve(__dirname, ".."); +const bench_path = path.resolve(xhiveframework_path, "..", ".."); +const sites_path = path.resolve(bench_path, "sites"); +const apps_path = path.resolve(bench_path, "apps"); +const assets_path = path.resolve(sites_path, "assets"); +const app_list = get_apps_list(); + +const app_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(apps_path, app, app); + return out; +}, {}); +const public_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(app_paths[app], "public"); + return out; +}, {}); +const public_js_paths = app_list.reduce((out, app) => { + out[app] = path.resolve(app_paths[app], "public/js"); + return out; +}, {}); + +const bundle_map = app_list.reduce((out, app) => { + const public_js_path = public_js_paths[app]; + if (fs.existsSync(public_js_path)) { + const all_files = fs.readdirSync(public_js_path); + const js_files = all_files.filter((file) => file.endsWith(".js")); + + for (let js_file of js_files) { + const filename = path.basename(js_file).split(".")[0]; + out[path.join(app, "js", filename)] = path.resolve(public_js_path, js_file); + } + } + + return out; +}, {}); + +const get_public_path = (app) => public_paths[app]; + +const get_build_json_path = (app) => path.resolve(get_public_path(app), "build.json"); + +function get_build_json(app) { + try { + return require(get_build_json_path(app)); + } catch (e) { + // build.json does not exist + return null; + } +} + +function delete_file(path) { + if (fs.existsSync(path)) { + fs.unlinkSync(path); + } +} + +function run_serially(tasks) { + let result = Promise.resolve(); + tasks.forEach((task) => { + if (task) { + result = result.then ? result.then(task) : Promise.resolve(); + } + }); + return result; +} + +const get_app_path = (app) => app_paths[app]; + +function get_apps_list() { + return fs + .readFileSync(path.resolve(sites_path, "apps.txt"), { + encoding: "utf-8", + }) + .split("\n") + .filter(Boolean); +} + +function get_cli_arg(name) { + let args = process.argv.slice(2); + let arg = `--${name}`; + let index = args.indexOf(arg); + + let value = null; + if (index != -1) { + value = true; + } + if (value && args[index + 1]) { + value = args[index + 1]; + } + return value; +} + +function log_error(message, badge = "ERROR") { + badge = chalk.white.bgRed(` ${badge} `); + console.error(`${badge} ${message}`); +} + +function log_warn(message, badge = "WARN") { + badge = chalk.black.bgYellowBright(` ${badge} `); + console.warn(`${badge} ${message}`); +} + +function log(...args) { + console.log(...args); +} + +function get_redis_subscriber(kind) { + // get redis subscriber that aborts after 10 connection attempts + let retry_strategy; + let { get_redis_subscriber: get_redis, get_conf } = require("../node_utils"); + + if (process.env.CI == 1 || get_conf().developer_mode == 0) { + retry_strategy = () => {}; + } else { + retry_strategy = function (options) { + // abort after 5 x 3 connection attempts ~= 3 seconds + if (options.attempt > 4) { + return undefined; + } + return options.attempt * 100; + }; + } + return get_redis(kind, { retry_strategy }); +} + +module.exports = { + app_list, + bench_path, + assets_path, + sites_path, + apps_path, + bundle_map, + get_public_path, + get_build_json_path, + get_build_json, + get_app_path, + delete_file, + run_serially, + get_cli_arg, + log, + log_warn, + log_error, + get_redis_subscriber, +}; diff --git a/esbuild/xhiveframework-html.js b/esbuild/xhiveframework-html.js new file mode 100644 index 0000000..766b993 --- /dev/null +++ b/esbuild/xhiveframework-html.js @@ -0,0 +1,44 @@ +module.exports = { + name: "xhiveframework-html", + setup(build) { + let path = require("path"); + let fs = require("fs/promises"); + + build.onResolve({ filter: /\.html$/ }, (args) => { + return { + path: path.join(args.resolveDir, args.path), + namespace: "xhiveframework-html", + }; + }); + + build.onLoad({ filter: /.*/, namespace: "xhiveframework-html" }, (args) => { + let filepath = args.path; + let filename = path.basename(filepath).split(".")[0]; + + return fs + .readFile(filepath, "utf-8") + .then((content) => { + content = scrub_html_template(content); + return { + contents: `\n\txhiveframework.templates['${filename}'] = \`${content}\`;\n`, + watchFiles: [filepath], + }; + }) + .catch(() => { + return { + contents: "", + warnings: [ + { + text: `There was an error importing ${filepath}`, + }, + ], + }; + }); + }); + }, +}; + +function scrub_html_template(content) { + content = content.replace(/`/g, "\\`"); + return content; +} diff --git a/esbuild/xhiveframework-vue-style.js b/esbuild/xhiveframework-vue-style.js new file mode 100644 index 0000000..f729559 --- /dev/null +++ b/esbuild/xhiveframework-vue-style.js @@ -0,0 +1,59 @@ +const fs = require("fs"); +const path = require("path"); +const { sites_path } = require("./utils"); + +module.exports = { + name: "xhiveframework-vue-style", + setup(build) { + build.initialOptions.write = false; + build.onEnd((result) => { + let files = get_files(result.metafile.outputs); + let keys = Object.keys(files); + for (let out of result.outputFiles) { + let asset_path = "/" + path.relative(sites_path, out.path); + let dir = path.dirname(out.path); + if (out.path.endsWith(".js") && keys.includes(asset_path)) { + let name = out.path.split(".bundle.")[0]; + name = path.basename(name); + + let index = result.outputFiles.findIndex((f) => { + return f.path.endsWith(".css") && f.path.includes(`/${name}.bundle.`); + }); + + let css_data = JSON.stringify(result.outputFiles[index].text); + let modified = `xhiveframework.dom.set_style(${css_data});\n${out.text}`; + out.contents = Buffer.from(modified); + + result.outputFiles.splice(index, 1); + if (result.outputFiles[index - 1].path.endsWith(".css.map")) { + result.outputFiles.splice(index - 1, 1); + } + } + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFile(out.path, out.contents, (err) => { + err && console.error(err); + }); + } + }); + }, +}; + +function get_files(files) { + let result = {}; + for (let file in files) { + let info = files[file]; + let asset_path = "/" + path.relative(sites_path, file); + if (info && info.entryPoint && Object.keys(info.inputs).length !== 0) { + for (let input in info.inputs) { + if (input.includes(".vue?type=style")) { + let bundle_css = path.basename(info.entryPoint).replace(".js", ".css"); + result[asset_path] = bundle_css; + break; + } + } + } + } + return result; +} diff --git a/generate_bootstrap_theme.js b/generate_bootstrap_theme.js new file mode 100644 index 0000000..ca2b0df --- /dev/null +++ b/generate_bootstrap_theme.js @@ -0,0 +1,28 @@ +const sass = require("sass"); +const fs = require("fs"); +const sass_options = require("./esbuild/sass_options"); +let output_path = process.argv[2]; +let scss_content = process.argv[3]; +scss_content = scss_content.replace(/\\n/g, "\n"); + +sass.render( + { + data: scss_content, + outputStyle: "compressed", + ...sass_options, + }, + function (err, result) { + if (err) { + console.error(err.formatted); + return; + } + + fs.writeFile(output_path, result.css, function (err) { + if (!err) { + console.log(output_path); + } else { + console.error(err); + } + }); + } +); diff --git a/hooks.md b/hooks.md new file mode 100644 index 0000000..97ea97d --- /dev/null +++ b/hooks.md @@ -0,0 +1,36 @@ +### List of Hooks + +#### Application Name and Details + +1. `app_name` - slugified name e.g. "xhiveframework" +1. `app_title` - full title name e.g. "XhiveFramework" +1. `app_publisher` +1. `app_description` +1. `app_version` + +#### Install + +1. `before_install` - method +1. `after_install` - method + + +#### Javascript / CSS Builds + +1. `app_include_js` - include in "app" +1. `app_include_css` - assets/xhiveframework/css/splash.css + +1. `web_include_js` - assets/js/xhiveframework-web.min.js +1. `web_include_css` - assets/css/xhiveframework-web.css + +#### Desktop + +1. `get_desktop_icons` - method to get list of desktop icons + +#### Notifications + +1. `notification_config` - method to get notification configuration + +#### Permissions + +1. `permission_query_conditions:[doctype]` - method to return additional query conditions at time of report / list etc. +1. `has_permission:[doctype]` - method to call permissions to check at individual level diff --git a/node_utils.js b/node_utils.js new file mode 100644 index 0000000..d4f9fd4 --- /dev/null +++ b/node_utils.js @@ -0,0 +1,60 @@ +const fs = require("fs"); +const path = require("path"); +const redis = require("@redis/client"); +const bench_path = path.resolve(__dirname, "..", ".."); + +const dns = require("dns"); + +// Since node17, node resolves to ipv6 unless system is configured otherwise. +// In XhiveFramework context using ipv4 - 127.0.0.1 is fine. +dns.setDefaultResultOrder("ipv4first"); + +function get_conf() { + // defaults + var conf = { + socketio_port: 9000, + }; + + var read_config = function (file_path) { + const full_path = path.resolve(bench_path, file_path); + + if (fs.existsSync(full_path)) { + var bench_config = JSON.parse(fs.readFileSync(full_path)); + for (var key in bench_config) { + if (bench_config[key]) { + conf[key] = bench_config[key]; + } + } + } + }; + + // get ports from bench/config.json + read_config("config.json"); + read_config("sites/common_site_config.json"); + + // set overrides from environment + if (process.env.XHIVEFRAMEWORK_SITE) { + conf.default_site = process.env.XHIVEFRAMEWORK_SITE; + } + if (process.env.XHIVEFRAMEWORK_REDIS_CACHE) { + conf.redis_cache = process.env.XHIVEFRAMEWORK_REDIS_CACHE; + } + if (process.env.XHIVEFRAMEWORK_REDIS_QUEUE) { + conf.redis_queue = process.env.XHIVEFRAMEWORK_REDIS_QUEUE; + } + if (process.env.XHIVEFRAMEWORK_SOCKETIO_PORT) { + conf.socketio_port = process.env.XHIVEFRAMEWORK_SOCKETIO_PORT; + } + return conf; +} + +function get_redis_subscriber(kind = "redis_queue", options = {}) { + const conf = get_conf(); + const host = conf[kind]; + return redis.createClient({ url: host, ...options }); +} + +module.exports = { + get_conf, + get_redis_subscriber, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..87706d2 --- /dev/null +++ b/package.json @@ -0,0 +1,91 @@ +{ + "name": "xhiveframework-framework", + "scripts": { + "build": "node esbuild", + "production": "node esbuild --production", + "watch": "node esbuild --watch", + "coverage:report": "npx nyc report --reporter=clover" + }, + "repository": { + "type": "git", + "url": "git+https://lab.membtech.com/xhiveframework/xhiveframework15.git" + }, + "author": "XhiveFramework Technologies Pvt. Ltd.", + "license": "MIT", + "bugs": { + "url": "https://lab.membtech.com/xhiveframework/xhiveframework15/issues" + }, + "engines": { + "node": ">=18" + }, + "homepage": "https://xhiveframework.com", + "dependencies": { + "@editorjs/editorjs": "~2.26.3", + "@xhiveframework/esbuild-plugin-postcss2": "^0.1.3", + "@headlessui/vue": "^1.7.16", + "@popperjs/core": "^2.11.2", + "@redis/client": "^1.5.8", + "@sentry/browser": "^7.15.0", + "@vue-flow/background": "^1.1.0", + "@vue-flow/core": "^1.16.2", + "@vue/component-compiler": "^4.2.4", + "@vueuse/core": "^9.5.0", + "ace-builds": "^1.4.8", + "air-datepicker": "github:xhiveframework/air-datepicker", + "autoprefixer": "10", + "awesomplete": "^1.1.5", + "bootstrap": "4.6.2", + "chalk": "^2.3.2", + "cliui": "^7.0.4", + "cookie": "^0.4.0", + "cropperjs": "^1.5.12", + "cssnano": "^5.0.0", + "driver.js": "^0.9.8", + "editorjs-undo": "0.1.6", + "esbuild": "^0.14.29", + "esbuild-plugin-vue3": "^0.3.0", + "fast-deep-equal": "^2.0.1", + "fast-glob": "^3.2.5", + "xhiveframework-charts": "2.0.0-rc22", + "xhiveframework-datatable": "1.17.15", + "xhiveframework-gantt": "^0.6.0", + "highlight.js": "^10.4.1", + "html5-qrcode": "^2.3.8", + "jquery": "3.7.0", + "js-sha256": "^0.9.0", + "jsbarcode": "^3.11.0", + "launch-editor": "^2.2.1", + "localforage": "^1.10.0", + "md5": "^2.3.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.35", + "pinia": "^2.0.23", + "plyr": "^3.7.8", + "popper.js": "^1.16.0", + "postcss": "8", + "quill": "2.0.0-dev.4", + "quill-image-resize": "^3.0.9", + "quill-magic-url": "^3.0.0", + "qz-tray": "^2.0.8", + "rtlcss": "^4.0.0", + "sass": "^1.63.0", + "showdown": "^2.1.0", + "socket.io": "^4.7.1", + "socket.io-client": "^4.7.1", + "sortablejs": "^1.15.0", + "superagent": "^8.0.0", + "touch": "^3.1.0", + "vue": "^3.3.0", + "vue-router": "^4.1.5", + "vuedraggable": "^4.1.0", + "vuex": "4.0.2", + "yargs": "^17.5.1" + }, + "nyc": { + "report-dir": ".cypress-coverage" + }, + "optionalDependencies": { + "bufferutil": "^4.0.8", + "utf-8-validate": "^6.0.3" + } +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..34980d2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,138 @@ +[project] +name = "xhiveframework" +authors = [ + { name = "XhiveFramework Technologies Pvt Ltd", email = "developers@xhiveframework.io"} +] +description = "Metadata driven, full-stack low code web framework" +requires-python = ">=3.10" +readme = "README.md" +dynamic = ["version"] +dependencies = [ + # core dependencies + "Babel~=2.12.1", + "Click~=8.1.7", + "filelock~=3.8.0", + "filetype~=1.2.0", + "GitPython~=3.1.34", + "Jinja2~=3.1.2", + "Pillow~=10.2.0", + "PyJWT~=2.8.0", + # We depend on internal attributes, + # do NOT add loose requirements on PyMySQL versions. + "PyMySQL==1.1.0", + "pypdf~=3.17.0", + "PyPika==0.48.9", + "PyQRCode~=1.2.1", + "PyYAML~=6.0.1", + "RestrictedPython~=6.2", + "WeasyPrint==59.0", + "Werkzeug~=3.0.1", + "Whoosh~=2.7.4", + "beautifulsoup4~=4.12.2", + "bleach-allowlist~=1.0.3", + "bleach[css]~=6.0.0", + "cairocffi==1.5.1", + "chardet~=5.1.0", + "croniter~=1.4.1", + "cryptography~=42.0.0", + "email-reply-parser~=0.5.12", + "git-url-parse~=1.2.2", + "gunicorn~=21.2.0", + "html5lib~=1.1", + "ipython~=8.15.0", + "ldap3~=2.9", + "markdown2~=2.4.8", + "MarkupSafe>=2.1.0,<3", + "maxminddb-geolite2==2018.703", + "num2words~=0.5.12", + "oauthlib~=3.2.2", + "openpyxl~=3.1.2", + "passlib~=1.7.4", + "pdfkit~=1.0.0", + "phonenumbers==8.13.13", + "premailer~=3.10.0", + "psutil~=5.9.5", + "psycopg2-binary~=2.9.1", + "pyOpenSSL~=24.0.0", + "pydantic==2.3.0", + "pyotp~=2.8.0", + "python-dateutil~=2.8.2", + "pytz==2023.3", + "rauth~=0.7.3", + "redis~=4.5.5", + "hiredis~=2.2.3", + "requests-oauthlib~=1.3.1", + "requests~=2.31.0", + "rq~=1.15.1", + "rsa>=4.1", + "semantic-version~=2.10.0", + "sentry-sdk~=1.37.1", + "sqlparse~=0.4.4", + "tenacity~=8.2.2", + "terminaltables~=3.1.10", + "traceback-with-variables~=2.0.4", + "typing_extensions>=4.6.1,<5", + "xlrd~=2.0.1", + "zxcvbn~=4.4.28", + "markdownify~=0.11.6", + + # integration dependencies + "boto3~=1.28.10", + "dropbox~=11.36.2", + "google-api-python-client~=2.2.0", + "google-auth-oauthlib~=0.4.4", + "google-auth~=1.29.0", + "posthog~=3.0.1", +] + +[build-system] +requires = ["flit_core >=3.4,<4"] +build-backend = "flit_core.buildapi" + +[tool.bench.dev-dependencies] +coverage = "~=6.5.0" +Faker = "~=18.10.1" +pyngrok = "~=6.0.0" +unittest-xml-reporting = "~=3.2.0" +watchdog = "~=3.0.0" +hypothesis = "~=6.77.0" +responses = "==0.23.1" +freezegun = "~=1.2.2" + +[tool.ruff] +line-length = 110 +target-version = "py310" + +[tool.ruff.lint] +select = [ + "F", + "E", + "W", + "I", + "UP", + "B", + "RUF", +] +ignore = [ + "B017", # assertRaises(Exception) - should be more specific + "B018", # useless expression, not assigned to anything + "B023", # function doesn't bind loop variable - will have last iteration's value + "B904", # raise inside except without from + "E101", # indentation contains mixed spaces and tabs + "E402", # module level import not at top of file + "E501", # line too long + "E741", # ambiguous variable name + "F401", # "unused" imports + "F403", # can't detect undefined names from * import + "F405", # can't detect undefined names from * import + "F722", # syntax error in forward type annotation + "W191", # indentation contains tabs + "RUF001", # string contains ambiguous unicode character +] +typing-modules = ["xhiveframework.types.DF"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "tab" +docstring-code-format = true + diff --git a/realtime/handlers/xhiveframework_handlers.js b/realtime/handlers/xhiveframework_handlers.js new file mode 100644 index 0000000..90d0446 --- /dev/null +++ b/realtime/handlers/xhiveframework_handlers.js @@ -0,0 +1,193 @@ +const { xhiveframework_request } = require("../utils"); +const log = console.log; + +const WEBSITE_ROOM = "website"; +const SITE_ROOM = "all"; + +function xhiveframework_handlers(realtime, socket) { + socket.join(user_room(socket.user)); + socket.join(WEBSITE_ROOM); + + if (socket.user_type == "System User") { + socket.join(SITE_ROOM); + } + + socket.on("doctype_subscribe", function (doctype) { + can_subscribe_doctype({ + socket, + doctype, + callback: () => { + socket.join(doctype_room(doctype)); + }, + }); + }); + + socket.on("doctype_unsubscribe", function (doctype) { + socket.leave(doctype_room(doctype)); + }); + + socket.on("task_subscribe", function (task_id) { + const room = task_room(task_id); + socket.join(room); + }); + + socket.on("task_unsubscribe", function (task_id) { + const room = task_room(task_id); + socket.leave(room); + }); + + socket.on("progress_subscribe", function (task_id) { + const room = task_room(task_id); + socket.join(room); + }); + + socket.on("doc_subscribe", function (doctype, docname) { + can_subscribe_doc({ + socket, + doctype, + docname, + callback: () => { + let room = doc_room(doctype, docname); + socket.join(room); + }, + }); + }); + + socket.on("doc_unsubscribe", function (doctype, docname) { + let room = doc_room(doctype, docname); + socket.leave(room); + }); + + socket.on("doc_open", function (doctype, docname) { + can_subscribe_doc({ + socket, + doctype, + docname, + callback: () => { + let room = open_doc_room(doctype, docname); + socket.join(room); + if (!socket.subscribed_documents) socket.subscribed_documents = []; + socket.subscribed_documents.push([doctype, docname]); + + // show who is currently viewing the form + notify_subscribed_doc_users({ + socket: socket, + doctype: doctype, + docname: docname, + }); + }, + }); + }); + + socket.on("doc_close", function (doctype, docname) { + // remove this user from the list of 'who is currently viewing the form' + let room = open_doc_room(doctype, docname); + socket.leave(room); + + if (socket.subscribed_documents) { + socket.subscribed_documents = socket.subscribed_documents.filter(([dt, dn]) => { + !(dt == doctype && dn == docname); + }); + } + + notify_subscribed_doc_users({ + socket: socket, + doctype: doctype, + docname: docname, + }); + }); + + socket.on("disconnect", () => { + notify_disconnected_documents(socket); + }); +} + +function notify_disconnected_documents(socket) { + if (socket.subscribed_documents) { + socket.subscribed_documents.forEach(([doctype, docname]) => { + notify_subscribed_doc_users({ socket, doctype, docname }); + }); + } +} + +function can_subscribe_doctype(args) { + if (!args) return; + if (!args.doctype) return; + xhiveframework_request("/api/method/xhiveframework.realtime.can_subscribe_doctype", args.socket) + .type("form") + .query({ + doctype: args.doctype, + }) + .end(function (err, res) { + if (!res || res.status == 403 || err) { + if (err) { + log(err); + } + return false; + } else if (res.status == 200) { + args.callback && args.callback(err, res); + return true; + } + log("ERROR (can_subscribe_doctype): ", err, res); + }); +} + +function notify_subscribed_doc_users(args) { + if (!(args && args.doctype && args.docname)) { + return; + } + const socket = args.socket; + const room = open_doc_room(args.doctype, args.docname); + + const clients = Array.from(socket.nsp.adapter.rooms.get(room) || []); + + let users = []; + + socket.nsp.sockets.forEach((sock) => { + if (clients.includes(sock.id)) { + users.push(sock.user); + } + }); + + // dont send update to self. meaningless. + if (users.length == 1 && users[0] == args.socket.user) return; + + // notify + socket.nsp.to(room).emit("doc_viewers", { + doctype: args.doctype, + docname: args.docname, + users: Array.from(new Set(users)), + }); +} + +function can_subscribe_doc(args) { + if (!args) return; + if (!args.doctype || !args.docname) return; + xhiveframework_request("/api/method/xhiveframework.realtime.can_subscribe_doc", args.socket) + .type("form") + .query({ + doctype: args.doctype, + docname: args.docname, + }) + .end(function (err, res) { + if (!res) { + log("No response for doc_subscribe"); + } else if (res.status == 403) { + return; + } else if (err) { + log(err); + } else if (res.status == 200) { + args.callback(err, res); + } else { + log("Something went wrong", err, res); + } + }); +} + +const doc_room = (doctype, docname) => "doc:" + doctype + "/" + docname; +const open_doc_room = (doctype, docname) => "open_doc:" + doctype + "/" + docname; +const user_room = (user) => "user:" + user; +const doctype_room = (doctype) => "doctype:" + doctype; +const task_room = (task_id) => "task_progress:" + task_id; + +module.exports = xhiveframework_handlers; diff --git a/realtime/index.js b/realtime/index.js new file mode 100644 index 0000000..25fa60c --- /dev/null +++ b/realtime/index.js @@ -0,0 +1,60 @@ +const { Server } = require("socket.io"); + +const { get_conf, get_redis_subscriber } = require("../node_utils"); +const conf = get_conf(); + +let io = new Server({ + cors: { + // Should be fine since we are ensuring whether hostname and origin are same before adding setting listeners for s socket + origin: true, + credentials: true, + }, + cleanupEmptyChildNamespaces: true, +}); + +// Multitenancy implementation. +// allow arbitrary sitename as namespaces +// namespaces get validated during authentication. +const realtime = io.of(/^\/.*$/); + +// load and register middlewares +const authenticate = require("./middlewares/authenticate"); +realtime.use(authenticate); +// ======================= + +// load and register handlers +const xhiveframework_handlers = require("./handlers/xhiveframework_handlers"); +function on_connection(socket) { + xhiveframework_handlers(realtime, socket); + + // ESBUild "open in editor" on error + socket.on("open_in_editor", async (data) => { + await subscriber.connect(); + subscriber.publish("open_in_editor", JSON.stringify(data)); + }); +} + +realtime.on("connection", on_connection); +// ======================= + +// Consume events sent from python via redis pub-sub channel. +const subscriber = get_redis_subscriber(); + +(async () => { + await subscriber.connect(); + subscriber.subscribe("events", (message) => { + message = JSON.parse(message); + let namespace = "/" + message.namespace; + if (message.room) { + io.of(namespace).to(message.room).emit(message.event, message.message); + } else { + // publish to ALL sites only used for things like build event. + realtime.emit(message.event, message.message); + } + }); +})(); +// ======================= + +let port = conf.socketio_port; +io.listen(port); +console.log("Realtime service listening on: ", port); diff --git a/realtime/middlewares/authenticate.js b/realtime/middlewares/authenticate.js new file mode 100644 index 0000000..69e827e --- /dev/null +++ b/realtime/middlewares/authenticate.js @@ -0,0 +1,81 @@ +const cookie = require("cookie"); +const request = require("superagent"); +const { get_url } = require("../utils"); + +const { get_conf } = require("../../node_utils"); +const conf = get_conf(); + +function authenticate_with_xhiveframework(socket, next) { + let namespace = socket.nsp.name; + namespace = namespace.slice(1, namespace.length); // remove leading `/` + + if (namespace != get_site_name(socket)) { + next(new Error("Invalid namespace")); + } + + if (get_hostname(socket.request.headers.host) != get_hostname(socket.request.headers.origin)) { + next(new Error("Invalid origin")); + return; + } + + if (!socket.request.headers.cookie) { + next(new Error("No cookie transmitted.")); + return; + } + + let cookies = cookie.parse(socket.request.headers.cookie || ""); + let authorization_header = socket.request.headers.authorization; + + if (!cookies.sid && !authorization_header) { + next(new Error("No authentication method used. Use cookie or authorization header.")); + return; + } + + let auth_req = request.get(get_url(socket, "/api/method/xhiveframework.realtime.get_user_info")); + if (cookies.sid) { + auth_req = auth_req.query({ sid: cookies.sid }); + } else { + auth_req = auth_req.set("Authorization", authorization_header); + } + + auth_req + .type("form") + .then((res) => { + socket.user = res.body.message.user; + socket.user_type = res.body.message.user_type; + socket.sid = cookies.sid; + socket.authorization_header = authorization_header; + next(); + }) + .catch((e) => { + next(new Error(`Unauthorized: ${e}`)); + }); +} + +function get_site_name(socket) { + if (socket.site_name) { + return socket.site_name; + } else if (socket.request.headers["x-xhiveframework-site-name"]) { + socket.site_name = get_hostname(socket.request.headers["x-xhiveframework-site-name"]); + } else if ( + conf.default_site && + ["localhost", "127.0.0.1"].indexOf(get_hostname(socket.request.headers.host)) !== -1 + ) { + socket.site_name = conf.default_site; + } else if (socket.request.headers.origin) { + socket.site_name = get_hostname(socket.request.headers.origin); + } else { + socket.site_name = get_hostname(socket.request.headers.host); + } + return socket.site_name; +} + +function get_hostname(url) { + if (!url) return undefined; + if (url.indexOf("://") > -1) { + url = url.split("/")[2]; + } + return url.match(/:/g) ? url.slice(0, url.indexOf(":")) : url; +} + +module.exports = authenticate_with_xhiveframework; diff --git a/realtime/utils.js b/realtime/utils.js new file mode 100644 index 0000000..701dbed --- /dev/null +++ b/realtime/utils.js @@ -0,0 +1,23 @@ +const request = require("superagent"); + +function get_url(socket, path) { + if (!path) { + path = ""; + } + return socket.request.headers.origin + path; +} + +// Authenticates a partial request created using superagent +function xhiveframework_request(path, socket) { + const partial_req = request.get(get_url(socket, path)); + if (socket.sid) { + return partial_req.query({ sid: socket.sid }); + } else if (socket.authorization_header) { + return partial_req.set("Authorization", socket.authorization_header); + } +} + +module.exports = { + get_url, + xhiveframework_request, +}; diff --git a/sider.yml b/sider.yml new file mode 100644 index 0000000..2ca6e8d --- /dev/null +++ b/sider.yml @@ -0,0 +1,3 @@ +linter: + flake8: + config: .flake8 \ No newline at end of file diff --git a/socketio.js b/socketio.js new file mode 100644 index 0000000..14360d9 --- /dev/null +++ b/socketio.js @@ -0,0 +1 @@ +require("./realtime"); diff --git a/xhiveframework/__init__.py b/xhiveframework/__init__.py new file mode 100644 index 0000000..239b025 --- /dev/null +++ b/xhiveframework/__init__.py @@ -0,0 +1,2481 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +""" +XhiveFramework - Low Code Open Source Framework in Python and JS + +XhiveFramework, pronounced fra-pay, is a full stack, batteries-included, web +framework written in Python and Javascript with MariaDB as the database. +It is the framework which powers XhiveERP. It is pretty generic and can +be used to build database driven apps. + +Read the documentation: https://xhiveframework.com/docs +""" +import copy +import faulthandler +import functools +import gc +import importlib +import inspect +import json +import os +import re +import signal +import traceback +import unicodedata +import warnings +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, overload + +import click +from werkzeug.local import Local, release_local + +import xhiveframework +from xhiveframework.query_builder import ( + get_query, + get_query_builder, + patch_query_aggregation, + patch_query_execute, +) +from xhiveframework.utils.caching import request_cache +from xhiveframework.utils.data import cint, cstr, sbool + +# Local application imports +from .exceptions import * +from .utils.jinja import ( + get_email_from_template, + get_jenv, + get_jloader, + get_template, + render_template, +) +from .utils.lazy_loader import lazy_import + +__version__ = "15.18.2" +__title__ = "Xhive Framework" + +controllers = {} +local = Local() +cache = None +STANDARD_USERS = ("Guest", "Administrator") + +_qb_patched = {} +_dev_server = int(sbool(os.environ.get("DEV_SERVER", False))) +_tune_gc = bool(sbool(os.environ.get("XHIVEFRAMEWORK_TUNE_GC", True))) + +if _dev_server: + warnings.simplefilter("always", DeprecationWarning) + warnings.simplefilter("always", PendingDeprecationWarning) + + +class _dict(dict): + """dict like object that exposes keys as attributes""" + + __slots__ = () + __getattr__ = dict.get + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__ + __setstate__ = dict.update + + def __getstate__(self): + return self + + def update(self, *args, **kwargs): + """update and return self -- the missing dict feature in python""" + + super().update(*args, **kwargs) + return self + + def copy(self): + return _dict(self) + + +def _(msg: str, lang: str | None = None, context: str | None = None) -> str: + """Returns translated string in current lang, if exists. + Usage: + _('Change') + _('Change', context='Coins') + """ + from xhiveframework.translate import get_all_translations + from xhiveframework.utils import is_html, strip_html_tags + + if not hasattr(local, "lang"): + local.lang = lang or "en" + + if not lang: + lang = local.lang + + non_translated_string = msg + + if is_html(msg): + msg = strip_html_tags(msg) + + # msg should always be unicode + msg = as_unicode(msg).strip() + + translated_string = "" + + all_translations = get_all_translations(lang) + if context: + string_key = f"{msg}:{context}" + translated_string = all_translations.get(string_key) + + if not translated_string: + translated_string = all_translations.get(msg) + + return translated_string or non_translated_string + + +def as_unicode(text: str, encoding: str = "utf-8") -> str: + """Convert to unicode if required""" + if isinstance(text, str): + return text + elif text is None: + return "" + elif isinstance(text, bytes): + return str(text, encoding) + else: + return str(text) + + +def get_lang_dict(fortype: str, name: str | None = None) -> dict[str, str]: + """Returns the translated language dict for the given type and name. + + :param fortype: must be one of `doctype`, `page`, `report`, `include`, `jsfile`, `boot` + :param name: name of the document for which assets are to be returned.""" + from xhiveframework.translate import get_dict + + return get_dict(fortype, name) + + +def set_user_lang(user: str, user_language: str | None = None) -> None: + """Guess and set user language for the session. `xhiveframework.local.lang`""" + from xhiveframework.translate import get_user_lang + + local.lang = get_user_lang(user) or user_language + + +# local-globals + +db = local("db") +qb = local("qb") +conf = local("conf") +form = form_dict = local("form_dict") +request = local("request") +job = local("job") +response = local("response") +session = local("session") +user = local("user") +flags = local("flags") + +error_log = local("error_log") +debug_log = local("debug_log") +message_log = local("message_log") + +lang = local("lang") + +# This if block is never executed when running the code. It is only used for +# telling static code analyzer where to find dynamically defined attributes. +if TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Request + + from xhiveframework.database.mariadb.database import MariaDBDatabase + from xhiveframework.database.postgres.database import PostgresDatabase + from xhiveframework.email.doctype.email_queue.email_queue import EmailQueue + from xhiveframework.model.document import Document + from xhiveframework.query_builder.builder import MariaDB, Postgres + from xhiveframework.utils.redis_wrapper import RedisWrapper + + db: MariaDBDatabase | PostgresDatabase + qb: MariaDB | Postgres + cache: RedisWrapper + response: _dict + conf: _dict + form_dict: _dict + flags: _dict + request: Request + session: _dict + user: str + flags: _dict + lang: str + + +# end: static analysis hack + + +def init(site: str, sites_path: str = ".", new_site: bool = False, force=False) -> None: + """Initialize xhiveframework for the current site. Reset thread locals `xhiveframework.local`""" + if getattr(local, "initialised", None) and not force: + return + + local.error_log = [] + local.message_log = [] + local.debug_log = [] + local.flags = _dict( + { + "currently_saving": [], + "redirect_location": "", + "in_install_db": False, + "in_install_app": False, + "in_import": False, + "in_test": False, + "mute_messages": False, + "ignore_links": False, + "mute_emails": False, + "has_dataurl": False, + "new_site": new_site, + "read_only": False, + } + ) + local.locked_documents = [] + local.test_objects = {} + + local.site = site + local.sites_path = sites_path + local.site_path = os.path.join(sites_path, site) + local.all_apps = None + + local.request_ip = None + local.response = _dict({"docs": []}) + local.task_id = None + + local.conf = _dict(get_site_config()) + local.lang = local.conf.lang or "en" + + local.module_app = None + local.app_modules = None + + local.user = None + local.user_perms = None + local.session = None + local.role_permissions = {} + local.valid_columns = {} + local.new_doc_templates = {} + + local.jenv = None + local.jloader = None + local.cache = {} + local.form_dict = _dict() + local.preload_assets = {"style": [], "script": [], "icons": []} + local.session = _dict() + local.dev_server = _dev_server + local.qb = get_query_builder(local.conf.db_type) + local.qb.get_query = get_query + setup_redis_cache_connection() + + if not _qb_patched.get(local.conf.db_type): + patch_query_execute() + patch_query_aggregation() + _register_fault_handler() + + setup_module_map(include_all_apps=not (xhiveframework.request or xhiveframework.job or xhiveframework.flags.in_migrate)) + + local.initialised = True + + +def connect(site: str | None = None, db_name: str | None = None, set_admin_as_user: bool = True) -> None: + """Connect to site database instance. + + :param site: If site is given, calls `xhiveframework.init`. + :param db_name: Optional. Will use from `site_config.json`. + :param set_admin_as_user: Set Administrator as current user. + """ + from xhiveframework.database import get_db + + if site: + init(site) + + local.db = get_db( + host=local.conf.db_host, + port=local.conf.db_port, + user=db_name or local.conf.db_name, + password=None, + ) + if set_admin_as_user: + set_user("Administrator") + + +def connect_replica() -> bool: + from xhiveframework.database import get_db + + if local and hasattr(local, "replica_db") and hasattr(local, "primary_db"): + return False + + user = local.conf.db_name + password = local.conf.db_password + port = local.conf.replica_db_port + + if local.conf.different_credentials_for_replica: + user = local.conf.replica_db_name + password = local.conf.replica_db_password + + local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password, port=port) + + # swap db connections + local.primary_db = local.db + local.db = local.replica_db + + return True + + +def get_site_config(sites_path: str | None = None, site_path: str | None = None) -> dict[str, Any]: + """Returns `site_config.json` combined with `sites/common_site_config.json`. + `site_config` is a set of site wide settings like database name, password, email etc.""" + config = _dict() + + sites_path = sites_path or getattr(local, "sites_path", None) + site_path = site_path or getattr(local, "site_path", None) + + if sites_path: + config.update(get_common_site_config(sites_path)) + + if site_path: + site_config = os.path.join(site_path, "site_config.json") + if os.path.exists(site_config): + try: + config.update(get_file_json(site_config)) + except Exception as error: + click.secho(f"{local.site}/site_config.json is invalid", fg="red") + print(error) + elif local.site and not local.flags.new_site: + raise IncorrectSitePath(f"{local.site} does not exist") + + # Generalized env variable overrides and defaults + def db_default_ports(db_type): + from xhiveframework.database.mariadb.database import MariaDBDatabase + + return { + "mariadb": MariaDBDatabase.default_port, # 3306 + "postgres": 5432, + }[db_type] + + config["redis_queue"] = ( + os.environ.get("XHIVEFRAMEWORK_REDIS_QUEUE") or config.get("redis_queue") or "redis://127.0.0.1:11311" + ) + config["redis_cache"] = ( + os.environ.get("XHIVEFRAMEWORK_REDIS_CACHE") or config.get("redis_cache") or "redis://127.0.0.1:13311" + ) + config["db_type"] = os.environ.get("XHIVEFRAMEWORK_DB_TYPE") or config.get("db_type") or "mariadb" + config["db_host"] = os.environ.get("XHIVEFRAMEWORK_DB_HOST") or config.get("db_host") or "127.0.0.1" + config["db_port"] = ( + os.environ.get("XHIVEFRAMEWORK_DB_PORT") or config.get("db_port") or db_default_ports(config["db_type"]) + ) + + # Allow externally extending the config with hooks + if extra_config := config.get("extra_config"): + if isinstance(extra_config, str): + extra_config = [extra_config] + for hook in extra_config: + try: + module, method = hook.rsplit(".", 1) + config |= getattr(importlib.import_module(module), method)() + except Exception: + print(f"Config hook {hook} failed") + traceback.print_exc() + + return config + + +def get_common_site_config(sites_path: str | None = None) -> dict[str, Any]: + """Returns common site config as dictionary. + + This is useful for: + - checking configuration which should only be allowed in common site config + - When no site context is present and fallback is required. + """ + sites_path = sites_path or getattr(local, "sites_path", None) + + common_site_config = os.path.join(sites_path, "common_site_config.json") + if os.path.exists(common_site_config): + try: + return _dict(get_file_json(common_site_config)) + except Exception as error: + click.secho("common_site_config.json is invalid", fg="red") + print(error) + return _dict() + + +def get_conf(site: str | None = None) -> dict[str, Any]: + if hasattr(local, "conf"): + return local.conf + + # if no site, get from common_site_config.json + with init_site(site): + return local.conf + + +class init_site: + def __init__(self, site=None): + """If site is None, initialize it for empty site ('') to load common_site_config.json""" + self.site = site or "" + + def __enter__(self): + init(self.site) + return local + + def __exit__(self, type, value, traceback): + destroy() + + +def destroy(): + """Closes connection and releases werkzeug local.""" + if db: + db.close() + + release_local(local) + + +def setup_redis_cache_connection(): + """Defines `xhiveframework.cache` as `RedisWrapper` instance""" + global cache + + if not cache: + from xhiveframework.utils.redis_wrapper import setup_cache + + cache = setup_cache() + + +def get_traceback(with_context: bool = False) -> str: + """Returns error traceback.""" + from xhiveframework.utils import get_traceback + + return get_traceback(with_context=with_context) + + +def errprint(msg: str) -> None: + """Log error. This is sent back as `exc` in response. + + :param msg: Message.""" + msg = as_unicode(msg) + if not request or ("cmd" not in local.form_dict) or conf.developer_mode: + print(msg) + + error_log.append({"exc": msg}) + + +def print_sql(enable: bool = True) -> None: + return cache.set_value("flag_print_sql", enable) + + +def log(msg: str) -> None: + """Add to `debug_log`. + + :param msg: Message.""" + if not request: + print(repr(msg)) + + debug_log.append(as_unicode(msg)) + + +@functools.lru_cache(maxsize=1024) +def _strip_html_tags(message): + from xhiveframework.utils import strip_html_tags + + return strip_html_tags(message) + + +def msgprint( + msg: str, + title: str | None = None, + raise_exception: bool | type[Exception] = False, + as_table: bool = False, + as_list: bool = False, + indicator: Literal["blue", "green", "orange", "red", "yellow"] | None = None, + alert: bool = False, + primary_action: str | None = None, + is_minimizable: bool = False, + wide: bool = False, + *, + realtime=False, +) -> None: + """Print a message to the user (via HTTP response). + Messages are sent in the `__server_messages` property in the + response JSON and shown in a pop-up / modal. + + :param msg: Message. + :param title: [optional] Message title. Default: "Message". + :param raise_exception: [optional] Raise given exception and show message. + :param as_table: [optional] If `msg` is a list of lists, render as HTML table. + :param as_list: [optional] If `msg` is a list, render as un-ordered list. + :param primary_action: [optional] Bind a primary server/client side action. + :param is_minimizable: [optional] Allow users to minimize the modal + :param wide: [optional] Show wide modal + :param realtime: Publish message immediately using websocket. + """ + import inspect + import sys + + msg = safe_decode(msg) + out = _dict(message=msg) + + def _raise_exception(): + if raise_exception: + if inspect.isclass(raise_exception) and issubclass(raise_exception, Exception): + exc = raise_exception(msg) + else: + exc = ValidationError(msg) + if out.__xhiveframework_exc_id: + exc.__xhiveframework_exc_id = out.__xhiveframework_exc_id + raise exc + + if flags.mute_messages: + _raise_exception() + return + + if as_table and type(msg) in (list, tuple): + out.as_table = 1 + + if as_list and type(msg) in (list, tuple): + out.as_list = 1 + + if sys.stdin and sys.stdin.isatty(): + if out.as_list: + msg = [_strip_html_tags(msg) for msg in out.message] + else: + msg = _strip_html_tags(out.message) + + if flags.print_messages and out.message: + print(f"Message: {_strip_html_tags(out.message)}") + + out.title = title or _("Message", context="Default title of the message dialog") + + if not indicator and raise_exception: + indicator = "red" + + if indicator: + out.indicator = indicator + + if is_minimizable: + out.is_minimizable = is_minimizable + + if alert: + out.alert = 1 + + if raise_exception: + out.raise_exception = 1 + out.__xhiveframework_exc_id = generate_hash() + + if primary_action: + out.primary_action = primary_action + + if wide: + out.wide = wide + + if realtime: + publish_realtime(event="msgprint", message=out) + else: + message_log.append(out) + _raise_exception() + + +def clear_messages(): + local.message_log = [] + + +def get_message_log() -> list[dict]: + return [msg_out for msg_out in local.message_log] + + +def clear_last_message(): + if len(local.message_log) > 0: + local.message_log = local.message_log[:-1] + + +def throw( + msg: str, + exc: type[Exception] = ValidationError, + title: str | None = None, + is_minimizable: bool = False, + wide: bool = False, + as_list: bool = False, + primary_action=None, +) -> None: + """Throw execption and show message (`msgprint`). + + :param msg: Message. + :param exc: Exception class. Default `xhiveframework.ValidationError` + :param title: [optional] Message title. Default: "Message". + :param is_minimizable: [optional] Allow users to minimize the modal + :param wide: [optional] Show wide modal + :param as_list: [optional] If `msg` is a list, render as un-ordered list. + :param primary_action: [optional] Bind a primary server/client side action. + """ + msgprint( + msg, + raise_exception=exc, + title=title, + indicator="red", + is_minimizable=is_minimizable, + wide=wide, + as_list=as_list, + primary_action=primary_action, + ) + + +def create_folder(path, with_init=False): + """Create a folder in the given path and add an `__init__.py` file (optional). + + :param path: Folder path. + :param with_init: Create `__init__.py` in the new folder.""" + from xhiveframework.utils import touch_file + + if not os.path.exists(path): + os.makedirs(path) + + if with_init: + touch_file(os.path.join(path, "__init__.py")) + + +def set_user(username: str): + """Set current user. + + :param username: **User** name to set as current user.""" + local.session.user = username + local.session.sid = username + local.cache = {} + local.form_dict = _dict() + local.jenv = None + local.session.data = _dict() + local.role_permissions = {} + local.new_doc_templates = {} + local.user_perms = None + + +def get_user(): + from xhiveframework.utils.user import UserPermissions + + if not local.user_perms: + local.user_perms = UserPermissions(local.session.user) + return local.user_perms + + +def get_roles(username=None) -> list[str]: + """Returns roles of current user.""" + if not local.session or not local.session.user: + return ["Guest"] + import xhiveframework.permissions + + return xhiveframework.permissions.get_roles(username or local.session.user) + + +def get_request_header(key, default=None): + """Return HTTP request header. + + :param key: HTTP header key. + :param default: Default value.""" + return request.headers.get(key, default) + + +def sendmail( + recipients=None, + sender="", + subject="No Subject", + message="No Message", + as_markdown=False, + delayed=True, + reference_doctype=None, + reference_name=None, + unsubscribe_method=None, + unsubscribe_params=None, + unsubscribe_message=None, + add_unsubscribe_link=1, + attachments=None, + content=None, + doctype=None, + name=None, + reply_to=None, + queue_separately=False, + cc=None, + bcc=None, + message_id=None, + in_reply_to=None, + send_after=None, + expose_recipients=None, + send_priority=1, + communication=None, + retry=1, + now=None, + read_receipt=None, + is_notification=False, + inline_images=None, + template=None, + args=None, + header=None, + print_letterhead=False, + with_container=False, + email_read_tracker_url=None, +) -> Optional["EmailQueue"]: + """Send email using user's default **Email Account** or global default **Email Account**. + + + :param recipients: List of recipients. + :param sender: Email sender. Default is current user or default outgoing account. + :param subject: Email Subject. + :param message: (or `content`) Email Content. + :param as_markdown: Convert content markdown to HTML. + :param delayed: Send via scheduled email sender **Email Queue**. Don't send immediately. Default is true + :param send_priority: Priority for Email Queue, default 1. + :param reference_doctype: (or `doctype`) Append as communication to this DocType. + :param reference_name: (or `name`) Append as communication to this document name. + :param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe` + :param unsubscribe_params: Unsubscribe paramaters to be loaded on the unsubscribe_method [optional] (dict). + :param attachments: List of attachments. + :param reply_to: Reply-To Email Address. + :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email. + :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. + :param send_after: Send after the given datetime. + :param expose_recipients: Display all recipients in the footer message - "This email was sent to" + :param communication: Communication link to be set in Email Queue record + :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id + :param template: Name of html template from templates/emails folder + :param args: Arguments for rendering the template + :param header: Append header in email + :param with_container: Wraps email inside a styled container + """ + + if recipients is None: + recipients = [] + if cc is None: + cc = [] + if bcc is None: + bcc = [] + + text_content = None + if template: + message, text_content = get_email_from_template(template, args) + + message = content or message + + if as_markdown: + from xhiveframework.utils import md_to_html + + message = md_to_html(message) + + if not delayed: + now = True + + from xhiveframework.email.doctype.email_queue.email_queue import QueueBuilder + + builder = QueueBuilder( + recipients=recipients, + sender=sender, + subject=subject, + message=message, + text_content=text_content, + reference_doctype=doctype or reference_doctype, + reference_name=name or reference_name, + add_unsubscribe_link=add_unsubscribe_link, + unsubscribe_method=unsubscribe_method, + unsubscribe_params=unsubscribe_params, + unsubscribe_message=unsubscribe_message, + attachments=attachments, + reply_to=reply_to, + cc=cc, + bcc=bcc, + message_id=message_id, + in_reply_to=in_reply_to, + send_after=send_after, + expose_recipients=expose_recipients, + send_priority=send_priority, + queue_separately=queue_separately, + communication=communication, + read_receipt=read_receipt, + is_notification=is_notification, + inline_images=inline_images, + header=header, + print_letterhead=print_letterhead, + with_container=with_container, + email_read_tracker_url=email_read_tracker_url, + ) + + # build email queue and send the email if send_now is True. + return builder.process(send_now=now) + + +whitelisted = [] +guest_methods = [] +xss_safe_methods = [] +allowed_http_methods_for_whitelisted_func = {} + + +def whitelist(allow_guest=False, xss_safe=False, methods=None): + """ + Decorator for whitelisting a function and making it accessible via HTTP. + Standard request will be `/api/method/[path.to.method]` + + :param allow_guest: Allow non logged-in user to access this method. + :param methods: Allowed http method to access the method. + + Use as: + + @xhiveframework.whitelist() + def myfunc(param1, param2): + pass + """ + + if not methods: + methods = ["GET", "POST", "PUT", "DELETE"] + + def innerfn(fn): + from xhiveframework.utils.typing_validations import validate_argument_types + + global whitelisted, guest_methods, xss_safe_methods, allowed_http_methods_for_whitelisted_func + + # validate argument types only if request is present + in_request_or_test = lambda: getattr(local, "request", None) or local.flags.in_test # noqa: E731 + + # get function from the unbound / bound method + # this is needed because functions can be compared, but not methods + method = None + if hasattr(fn, "__func__"): + method = validate_argument_types(fn, apply_condition=in_request_or_test) + fn = method.__func__ + else: + fn = validate_argument_types(fn, apply_condition=in_request_or_test) + + whitelisted.append(fn) + allowed_http_methods_for_whitelisted_func[fn] = methods + + if allow_guest: + guest_methods.append(fn) + + if xss_safe: + xss_safe_methods.append(fn) + + return method or fn + + return innerfn + + +def is_whitelisted(method): + from xhiveframework.utils import sanitize_html + + is_guest = session["user"] == "Guest" + if method not in whitelisted or is_guest and method not in guest_methods: + summary = _("You are not permitted to access this resource.") + detail = _("Function {0} is not whitelisted.").format(bold(f"{method.__module__}.{method.__name__}")) + msg = f"
    {summary}{detail}
    " + throw(msg, PermissionError, title="Method Not Allowed") + + if is_guest and method not in xss_safe_methods: + # strictly sanitize form_dict + # escapes html characters like <> except for predefined tags like a, b, ul etc. + for key, value in form_dict.items(): + if isinstance(value, str): + form_dict[key] = sanitize_html(value) + + +def read_only(): + def innfn(fn): + @functools.wraps(fn) + def wrapper_fn(*args, **kwargs): + # xhiveframework.read_only could be called from nested functions, in such cases don't swap the + # connection again. + switched_connection = False + if conf.read_from_replica: + switched_connection = connect_replica() + + try: + retval = fn(*args, **get_newargs(fn, kwargs)) + finally: + if switched_connection and local and hasattr(local, "primary_db"): + local.db.close() + local.db = local.primary_db + + return retval + + return wrapper_fn + + return innfn + + +def write_only(): + # if replica connection exists, we have to replace it momentarily with the primary connection + def innfn(fn): + def wrapper_fn(*args, **kwargs): + primary_db = getattr(local, "primary_db", None) + replica_db = getattr(local, "replica_db", None) + in_read_only = getattr(local, "db", None) != primary_db + + # switch to primary connection + if in_read_only and primary_db: + local.db = local.primary_db + + try: + retval = fn(*args, **get_newargs(fn, kwargs)) + finally: + # switch back to replica connection + if in_read_only and replica_db: + local.db = replica_db + + return retval + + return wrapper_fn + + return innfn + + +def only_for(roles: list[str] | tuple[str] | str, message=False): + """ + Raises `xhiveframework.PermissionError` if the user does not have any of the permitted roles. + + :param roles: Permitted role(s) + """ + + if local.flags.in_test or local.session.user == "Administrator": + return + + if isinstance(roles, str): + roles = (roles,) + + if set(roles).isdisjoint(get_roles()): + if not message: + raise PermissionError + + throw( + _("This action is only allowed for {}").format( + ", ".join(bold(_(role)) for role in roles), + ), + PermissionError, + _("Not Permitted"), + ) + + +def get_domain_data(module): + try: + domain_data = get_hooks("domains") + if module in domain_data: + return _dict(get_attr(get_hooks("domains")[module][0] + ".data")) + else: + return _dict() + except ImportError: + if local.flags.in_test: + return _dict() + else: + raise + + +def clear_cache(user: str | None = None, doctype: str | None = None): + """Clear **User**, **DocType** or global cache. + + :param user: If user is given, only user cache is cleared. + :param doctype: If doctype is given, only DocType cache is cleared.""" + import xhiveframework.cache_manager + import xhiveframework.utils.caching + from xhiveframework.website.router import clear_routing_cache + + if doctype: + xhiveframework.cache_manager.clear_doctype_cache(doctype) + reset_metadata_version() + elif user: + xhiveframework.cache_manager.clear_user_cache(user) + else: # everything + # Delete ALL keys associated with this site. + xhiveframework.cache.delete_keys("") + reset_metadata_version() + local.cache = {} + local.new_doc_templates = {} + + for fn in get_hooks("clear_cache"): + get_attr(fn)() + + xhiveframework.utils.caching._SITE_CACHE.clear() + local.role_permissions = {} + if hasattr(local, "request_cache"): + local.request_cache.clear() + if hasattr(local, "system_settings"): + del local.system_settings + if hasattr(local, "website_settings"): + del local.website_settings + + clear_routing_cache() + + +def only_has_select_perm(doctype, user=None, ignore_permissions=False): + if ignore_permissions: + return False + + from xhiveframework.permissions import get_role_permissions + + user = user or local.session.user + permissions = get_role_permissions(doctype, user=user) + + return permissions.get("select") and not permissions.get("read") + + +def has_permission( + doctype=None, + ptype="read", + doc=None, + user=None, + throw=False, + *, + parent_doctype=None, + debug=False, +): + """ + Returns True if the user has permission `ptype` for given `doctype` or `doc` + Raises `xhiveframework.PermissionError` if user isn't permitted and `throw` is truthy + + :param doctype: DocType for which permission is to be check. + :param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`. + :param doc: [optional] Checks User permissions for given doc. + :param user: [optional] Check for given user. Default: current user. + :param parent_doctype: Required when checking permission for a child DocType (unless doc is specified). + """ + import xhiveframework.permissions + + if not doctype and doc: + doctype = doc.doctype + + out = xhiveframework.permissions.has_permission( + doctype, + ptype, + doc=doc, + user=user, + raise_exception=throw, + parent_doctype=parent_doctype, + debug=debug, + ) + + if throw and not out: + document_label = f"{_(doctype)} {doc if isinstance(doc, str) else doc.name}" if doc else _(doctype) + xhiveframework.flags.error_message = _("No permission for {0}").format(document_label) + raise xhiveframework.PermissionError + + return out + + +def has_website_permission(doc=None, ptype="read", user=None, verbose=False, doctype=None): + """Raises `xhiveframework.PermissionError` if not permitted. + + :param doctype: DocType for which permission is to be check. + :param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`. + :param doc: Checks User permissions for given doc. + :param user: [optional] Check for given user. Default: current user.""" + + if not user: + user = session.user + + if doc: + if isinstance(doc, str): + doc = get_doc(doctype, doc) + + doctype = doc.doctype + + if doc.flags.ignore_permissions: + return True + + # check permission in controller + if hasattr(doc, "has_website_permission"): + return doc.has_website_permission(ptype, user, verbose=verbose) + + hooks = (get_hooks("has_website_permission") or {}).get(doctype, []) + if hooks: + for method in hooks: + result = call(method, doc=doc, ptype=ptype, user=user, verbose=verbose) + # if even a single permission check is Falsy + if not result: + return False + + # else it is Truthy + return True + + else: + return False + + +def is_table(doctype: str) -> bool: + """Returns True if `istable` property (indicating child Table) is set for given DocType.""" + + def get_tables(): + return db.get_values("DocType", filters={"istable": 1}, order_by=None, pluck=True) + + tables = cache.get_value("is_table", get_tables) + return doctype in tables + + +def get_precision( + doctype: str, fieldname: str, currency: str | None = None, doc: Optional["Document"] = None +) -> int: + """Get precision for a given field""" + from xhiveframework.model.meta import get_field_precision + + return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency) + + +def generate_hash(txt: str | None = None, length: int = 56) -> str: + """Generate random hash using best available randomness source.""" + import math + import secrets + + if not length: + length = 56 + + return secrets.token_hex(math.ceil(length / 2))[:length] + + +def reset_metadata_version(): + """Reset `metadata_version` (Client (Javascript) build ID) hash.""" + v = generate_hash() + cache.set_value("metadata_version", v) + return v + + +def new_doc( + doctype: str, + *, + parent_doc: Optional["Document"] = None, + parentfield: str | None = None, + as_dict: bool = False, + **kwargs, +) -> "Document": + """Returns a new document of the given DocType with defaults set. + + :param doctype: DocType of the new document. + :param parent_doc: [optional] add to parent document. + :param parentfield: [optional] add against this `parentfield`. + :param as_dict: [optional] return as dictionary instead of Document. + :param kwargs: [optional] You can specify fields as field=value pairs in function call. + """ + + from xhiveframework.model.create_new import get_new_doc + + new_doc = get_new_doc(doctype, parent_doc, parentfield, as_dict=as_dict) + + return new_doc.update(kwargs) + + +def set_value(doctype, docname, fieldname, value=None): + """Set document value. Calls `xhiveframework.client.set_value`""" + import xhiveframework.client + + return xhiveframework.client.set_value(doctype, docname, fieldname, value) + + +def get_cached_doc(*args, **kwargs) -> "Document": + if (key := can_cache_doc(args)) and (doc := cache.get_value(key)): + return doc + + # Not found in cache, fetch from DB + doc = get_doc(*args, **kwargs) + + # Store in cache + if not key: + key = get_document_cache_key(doc.doctype, doc.name) + + _set_document_in_cache(key, doc) + + return doc + + +def _set_document_in_cache(key: str, doc: "Document") -> None: + cache.set_value(key, doc) + + +def can_cache_doc(args) -> str | None: + """ + Determine if document should be cached based on get_doc params. + Returns cache key if doc can be cached, None otherwise. + """ + + if not args: + return + + doctype = args[0] + name = doctype if len(args) == 1 or args[1] is None else args[1] + + # Only cache if both doctype and name are strings + if isinstance(doctype, str) and isinstance(name, str): + return get_document_cache_key(doctype, name) + + +def get_document_cache_key(doctype: str, name: str): + return f"document_cache::{doctype}::{name}" + + +def clear_document_cache(doctype: str, name: str | None = None) -> None: + def clear_in_redis(): + if name is not None: + cache.delete_value(get_document_cache_key(doctype, name)) + else: + cache.delete_keys(get_document_cache_key(doctype, "")) + + clear_in_redis() + if hasattr(db, "after_commit"): + db.after_commit.add(clear_in_redis) + db.after_rollback.add(clear_in_redis) + + if doctype == "System Settings" and hasattr(local, "system_settings"): + delattr(local, "system_settings") + + if doctype == "Website Settings" and hasattr(local, "website_settings"): + delattr(local, "website_settings") + + +def get_cached_value(doctype: str, name: str, fieldname: str = "name", as_dict: bool = False) -> Any: + try: + doc = get_cached_doc(doctype, name) + except DoesNotExistError: + clear_last_message() + return + + if isinstance(fieldname, str): + if as_dict: + throw("Cannot make dict for single fieldname") + return doc.get(fieldname) + + values = [doc.get(f) for f in fieldname] + if as_dict: + return _dict(zip(fieldname, values, strict=False)) + return values + + +_SingleDocument: TypeAlias = "Document" +_NewDocument: TypeAlias = "Document" + + +@overload +def get_doc(document: "Document", /) -> "Document": + pass + + +@overload +def get_doc(doctype: str, /) -> _SingleDocument: + """Retrieve Single DocType from DB, doctype must be positional argument.""" + pass + + +@overload +def get_doc(doctype: str, name: str, /, *, for_update: bool | None = None) -> "Document": + """Retrieve DocType from DB, doctype and name must be positional argument.""" + pass + + +@overload +def get_doc(**kwargs: dict) -> "_NewDocument": + """Initialize document from kwargs. + Not recommended. Use `xhiveframework.new_doc` instead.""" + pass + + +@overload +def get_doc(documentdict: dict) -> "_NewDocument": + """Create document from dict. + Not recommended. Use `xhiveframework.new_doc` instead.""" + pass + + +def get_doc(*args, **kwargs): + """Return a `xhiveframework.model.document.Document` object of the given type and name. + + :param arg1: DocType name as string **or** document JSON. + :param arg2: [optional] Document name as string. + + Examples: + + # insert a new document + todo = xhiveframework.get_doc({"doctype":"ToDo", "description": "test"}) + todo.insert() + + # open an existing document + todo = xhiveframework.get_doc("ToDo", "TD0001") + + """ + import xhiveframework.model.document + + doc = xhiveframework.model.document.get_doc(*args, **kwargs) + + # Replace cache if stale one exists + if not kwargs.get("for_update") and (key := can_cache_doc(args)) and cache.exists(key): + _set_document_in_cache(key, doc) + + return doc + + +def get_last_doc(doctype, filters=None, order_by="creation desc", *, for_update=False): + """Get last created document of this type.""" + d = get_all(doctype, filters=filters, limit_page_length=1, order_by=order_by, pluck="name") + if d: + return get_doc(doctype, d[0], for_update=for_update) + else: + raise DoesNotExistError + + +def get_single(doctype): + """Return a `xhiveframework.model.document.Document` object of the given Single doctype.""" + return get_doc(doctype, doctype) + + +def get_meta(doctype, cached=True): + """Get `xhiveframework.model.meta.Meta` instance of given doctype name.""" + import xhiveframework.model.meta + + return xhiveframework.model.meta.get_meta(doctype, cached=cached) + + +def get_meta_module(doctype): + import xhiveframework.modules + + return xhiveframework.modules.load_doctype_module(doctype) + + +def delete_doc( + doctype: str | None = None, + name: str | None = None, + force: bool = False, + ignore_doctypes: list[str] | None = None, + for_reload: bool = False, + ignore_permissions: bool = False, + flags: None = None, + ignore_on_trash: bool = False, + ignore_missing: bool = True, + delete_permanently: bool = False, +): + """Delete a document. Calls `xhiveframework.model.delete_doc.delete_doc`. + + :param doctype: DocType of document to be delete. + :param name: Name of document to be delete. + :param force: Allow even if document is linked. Warning: This may lead to data integrity errors. + :param ignore_doctypes: Ignore if child table is one of these. + :param for_reload: Call `before_reload` trigger before deleting. + :param ignore_permissions: Ignore user permissions. + :param delete_permanently: Do not create a Deleted Document for the document.""" + import xhiveframework.model.delete_doc + + return xhiveframework.model.delete_doc.delete_doc( + doctype, + name, + force, + ignore_doctypes, + for_reload, + ignore_permissions, + flags, + ignore_on_trash, + ignore_missing, + delete_permanently, + ) + + +def delete_doc_if_exists(doctype, name, force=0): + """Delete document if exists.""" + delete_doc(doctype, name, force=force, ignore_missing=True) + + +def reload_doctype(doctype, force=False, reset_permissions=False): + """Reload DocType from model (`[module]/[doctype]/[name]/[name].json`) files.""" + reload_doc( + scrub(db.get_value("DocType", doctype, "module")), + "doctype", + scrub(doctype), + force=force, + reset_permissions=reset_permissions, + ) + + +def reload_doc( + module: str, + dt: str | None = None, + dn: str | None = None, + force: bool = False, + reset_permissions: bool = False, +): + """Reload Document from model (`[module]/[doctype]/[name]/[name].json`) files. + + :param module: Module name. + :param dt: DocType name. + :param dn: Document name. + :param force: Reload even if `modified` timestamp matches. + """ + + import xhiveframework.modules + + return xhiveframework.modules.reload_doc(module, dt, dn, force=force, reset_permissions=reset_permissions) + + +@whitelist(methods=["POST", "PUT"]) +def rename_doc( + doctype: str, + old: str, + new: str, + force: bool = False, + merge: bool = False, + *, + ignore_if_exists: bool = False, + show_alert: bool = True, + rebuild_search: bool = True, +) -> str: + """ + Renames a doc(dt, old) to doc(dt, new) and updates all linked fields of type "Link" + + Calls `xhiveframework.model.rename_doc.rename_doc` + """ + + from xhiveframework.model.rename_doc import rename_doc + + return rename_doc( + doctype=doctype, + old=old, + new=new, + force=force, + merge=merge, + ignore_if_exists=ignore_if_exists, + show_alert=show_alert, + rebuild_search=rebuild_search, + ) + + +def get_module(modulename): + """Returns a module object for given Python module name using `importlib.import_module`.""" + return importlib.import_module(modulename) + + +def scrub(txt: str) -> str: + """Returns sluggified string. e.g. `Sales Order` becomes `sales_order`.""" + return cstr(txt).replace(" ", "_").replace("-", "_").lower() + + +def unscrub(txt: str) -> str: + """Returns titlified string. e.g. `sales_order` becomes `Sales Order`.""" + return txt.replace("_", " ").replace("-", " ").title() + + +def get_module_path(module, *joins): + """Get the path of the given module name. + + :param module: Module name. + :param *joins: Join additional path elements using `os.path.join`.""" + from xhiveframework.modules.utils import get_module_app + + app = get_module_app(module) + return get_pymodule_path(app + "." + scrub(module), *joins) + + +def get_app_path(app_name, *joins): + """Return path of given app. + + :param app: App name. + :param *joins: Join additional path elements using `os.path.join`.""" + return get_pymodule_path(app_name, *joins) + + +def get_app_source_path(app_name, *joins): + """Return source path of given app. + + :param app: App name. + :param *joins: Join additional path elements using `os.path.join`.""" + return get_app_path(app_name, "..", *joins) + + +def get_site_path(*joins): + """Return path of current site. + + :param *joins: Join additional path elements using `os.path.join`.""" + from os.path import join + + return join(local.site_path, *joins) + + +def get_pymodule_path(modulename, *joins): + """Return path of given Python module name. + + :param modulename: Python module name. + :param *joins: Join additional path elements using `os.path.join`.""" + from os.path import abspath, dirname, join + + if "public" not in joins: + joins = [scrub(part) for part in joins] + + return abspath(join(dirname(get_module(scrub(modulename)).__file__ or ""), *joins)) + + +def get_module_list(app_name): + """Get list of modules for given all via `app/modules.txt`.""" + return get_file_items(get_app_path(app_name, "modules.txt")) + + +def get_all_apps(with_internal_apps=True, sites_path=None): + """Get list of all apps via `sites/apps.txt`.""" + if not sites_path: + sites_path = local.sites_path + + apps = get_file_items(os.path.join(sites_path, "apps.txt"), raise_not_found=True) + + if with_internal_apps: + for app in get_file_items(os.path.join(local.site_path, "apps.txt")): + if app not in apps: + apps.append(app) + + if "xhiveframework" in apps: + apps.remove("xhiveframework") + apps.insert(0, "xhiveframework") + + return apps + + +@request_cache +def get_installed_apps(*, _ensure_on_bench=False) -> list[str]: + """ + Get list of installed apps in current site. + + :param _ensure_on_bench: Only return apps that are present on bench. + """ + if getattr(flags, "in_install_db", True): + return [] + + if not db: + connect() + + installed = json.loads(db.get_global("installed_apps") or "[]") + + if _ensure_on_bench: + all_apps = cache.get_value("all_apps", get_all_apps) + installed = [app for app in installed if app in all_apps] + + return installed + + +def get_doc_hooks(): + """Returns hooked methods for given doc. It will expand the dict tuple if required.""" + if not hasattr(local, "doc_events_hooks"): + hooks = get_hooks("doc_events", {}) + out = {} + for key, value in hooks.items(): + if isinstance(key, tuple): + for doctype in key: + append_hook(out, doctype, value) + else: + append_hook(out, key, value) + + local.doc_events_hooks = out + + return local.doc_events_hooks + + +@request_cache +def _load_app_hooks(app_name: str | None = None): + import types + + hooks = {} + apps = [app_name] if app_name else get_installed_apps(_ensure_on_bench=True) + + for app in apps: + try: + app_hooks = get_module(f"{app}.hooks") + except ImportError as e: + if local.flags.in_install_app: + # if app is not installed while restoring + # ignore it + pass + print(f'Could not find app "{app}": \n{e}') + raise + + def _is_valid_hook(obj): + return not isinstance(obj, types.ModuleType | types.FunctionType | type) + + for key, value in inspect.getmembers(app_hooks, predicate=_is_valid_hook): + if not key.startswith("_"): + append_hook(hooks, key, value) + return hooks + + +def get_hooks( + hook: str | None = None, default: Any | None = "_KEEP_DEFAULT_LIST", app_name: str | None = None +) -> _dict: + """Get hooks via `app/hooks.py` + + :param hook: Name of the hook. Will gather all hooks for this name and return as a list. + :param default: Default if no hook found. + :param app_name: Filter by app.""" + + if app_name: + hooks = _dict(_load_app_hooks(app_name)) + else: + if conf.developer_mode: + hooks = _dict(_load_app_hooks()) + else: + hooks = _dict(cache.get_value("app_hooks", _load_app_hooks)) + + if hook: + return hooks.get(hook, ([] if default == "_KEEP_DEFAULT_LIST" else default)) + return hooks + + +def append_hook(target, key, value): + """appends a hook to the the target dict. + + If the hook key, exists, it will make it a key. + + If the hook value is a dict, like doc_events, it will + listify the values against the key. + """ + if isinstance(value, dict): + # dict? make a list of values against each key + target.setdefault(key, {}) + for inkey in value: + append_hook(target[key], inkey, value[inkey]) + else: + # make a list + target.setdefault(key, []) + if not isinstance(value, list): + value = [value] + target[key].extend(value) + + +def setup_module_map(include_all_apps: bool = True) -> None: + """ + Function to rebuild map of all modules + + :param: include_all_apps: Include all apps on bench, or just apps installed on the site. + :return: Nothing + """ + if include_all_apps: + local.app_modules = cache.get_value("app_modules") + local.module_app = cache.get_value("module_app") + else: + local.app_modules = cache.get_value("installed_app_modules") + local.module_app = cache.get_value("module_installed_app") + + if not (local.app_modules and local.module_app): + local.module_app, local.app_modules = {}, {} + if include_all_apps: + apps = get_all_apps(with_internal_apps=True) + else: + apps = get_installed_apps(_ensure_on_bench=True) + for app in apps: + local.app_modules.setdefault(app, []) + for module in get_module_list(app): + module = scrub(module) + if module in local.module_app: + print( + f"WARNING: module `{module}` found in apps `{local.module_app[module]}` and `{app}`" + ) + local.module_app[module] = app + local.app_modules[app].append(module) + + if include_all_apps: + cache.set_value("app_modules", local.app_modules) + cache.set_value("module_app", local.module_app) + else: + cache.set_value("installed_app_modules", local.app_modules) + cache.set_value("module_installed_app", local.module_app) + + +def get_file_items(path, raise_not_found=False, ignore_empty_lines=True): + """Returns items from text file as a list. Ignores empty lines.""" + import xhiveframework.utils + + content = read_file(path, raise_not_found=raise_not_found) + if content: + content = xhiveframework.utils.strip(content) + + return [ + p.strip() + for p in content.splitlines() + if (not ignore_empty_lines) or (p.strip() and not p.startswith("#")) + ] + else: + return [] + + +def get_file_json(path): + """Read a file and return parsed JSON object.""" + with open(path) as f: + return json.load(f) + + +def read_file(path, raise_not_found=False): + """Open a file and return its content as Unicode.""" + if isinstance(path, str): + path = path.encode("utf-8") + + if os.path.exists(path): + with open(path) as f: + return as_unicode(f.read()) + elif raise_not_found: + raise OSError(f"{path} Not Found") + else: + return None + + +def get_attr(method_string: str) -> Any: + """Get python method object from its name.""" + app_name = method_string.split(".", 1)[0] + if not local.flags.in_uninstall and not local.flags.in_install and app_name not in get_installed_apps(): + throw(_("App {0} is not installed").format(app_name), AppNotInstalledError) + + modulename = ".".join(method_string.split(".")[:-1]) + methodname = method_string.split(".")[-1] + return getattr(get_module(modulename), methodname) + + +def call(fn: str | Callable, *args, **kwargs): + """Call a function and match arguments.""" + if isinstance(fn, str): + fn = get_attr(fn) + + newargs = get_newargs(fn, kwargs) + + return fn(*args, **newargs) + + +def get_newargs(fn: Callable, kwargs: dict[str, Any]) -> dict[str, Any]: + """Remove any kwargs that are not supported by the function. + + Example: + >>> def fn(a=1, b=2): + ... pass + + >>> get_newargs(fn, {"a": 2, "c": 1}) + {"a": 2} + """ + + # if function has any **kwargs parameter that capture arbitrary keyword arguments + # Ref: https://docs.python.org/3/library/inspect.html#inspect.Parameter.kind + varkw_exist = False + + signature = inspect.signature(fn) + fnargs = list(signature.parameters) + + for param_name, parameter in signature.parameters.items(): + if parameter.kind == inspect.Parameter.VAR_KEYWORD: + varkw_exist = True + fnargs.remove(param_name) + break + + newargs = {} + for a in kwargs: + if (a in fnargs) or varkw_exist: + newargs[a] = kwargs.get(a) + + # WARNING: This behaviour is now part of business logic in places, never remove. + newargs.pop("ignore_permissions", None) + newargs.pop("flags", None) + + return newargs + + +def make_property_setter( + args, ignore_validate=False, validate_fields_for_doctype=True, is_system_generated=True +): + """Create a new **Property Setter** (for overriding DocType and DocField properties). + + If doctype is not specified, it will create a property setter for all fields with the + given fieldname""" + args = _dict(args) + if not args.doctype_or_field: + args.doctype_or_field = "DocField" + if not args.property_type: + args.property_type = ( + db.get_value("DocField", {"parent": "DocField", "fieldname": args.property}, "fieldtype") + or "Data" + ) + + if not args.doctype: + DocField_doctype = qb.DocType("DocField") + doctype_list = ( + qb.from_(DocField_doctype) + .select(DocField_doctype.parent) + .where(DocField_doctype.fieldname == args.fieldname) + .distinct() + ).run(pluck=True) + + else: + doctype_list = [args.doctype] + + for doctype in doctype_list: + if not args.property_type: + args.property_type = ( + db.get_value("DocField", {"parent": doctype, "fieldname": args.fieldname}, "fieldtype") + or "Data" + ) + + ps = get_doc( + { + "doctype": "Property Setter", + "doctype_or_field": args.doctype_or_field, + "doc_type": doctype, + "field_name": args.fieldname, + "row_name": args.row_name, + "property": args.property, + "value": args.value, + "property_type": args.property_type or "Data", + "is_system_generated": is_system_generated, + "__islocal": 1, + } + ) + ps.flags.ignore_validate = ignore_validate + ps.flags.validate_fields_for_doctype = validate_fields_for_doctype + ps.validate_fieldtype_change() + ps.insert() + + +def import_doc(path): + """Import a file using Data Import.""" + from xhiveframework.core.doctype.data_import.data_import import import_doc + + import_doc(path) + + +def copy_doc(doc: "Document", ignore_no_copy: bool = True) -> "Document": + """No_copy fields also get copied.""" + import copy + + def remove_no_copy_fields(d): + for df in d.meta.get("fields", {"no_copy": 1}): + if hasattr(d, df.fieldname): + d.set(df.fieldname, None) + + fields_to_clear = ["name", "owner", "creation", "modified", "modified_by"] + + if not local.flags.in_test: + fields_to_clear.append("docstatus") + + if not isinstance(doc, dict): + d = doc.as_dict() + else: + d = doc + + newdoc = get_doc(copy.deepcopy(d)) + newdoc.set("__islocal", 1) + for fieldname in [*fields_to_clear, "amended_from", "amendment_date"]: + newdoc.set(fieldname, None) + + if not ignore_no_copy: + remove_no_copy_fields(newdoc) + + for _i, d in enumerate(newdoc.get_all_children()): + d.set("__islocal", 1) + + for fieldname in fields_to_clear: + d.set(fieldname, None) + + if not ignore_no_copy: + remove_no_copy_fields(d) + + return newdoc + + +def respond_as_web_page( + title, + html, + success=None, + http_status_code=None, + context=None, + indicator_color=None, + primary_action="/", + primary_label=None, + fullpage=False, + width=None, + template="message", +): + """Send response as a web page with a message rather than JSON. Used to show permission errors etc. + + :param title: Page title and heading. + :param message: Message to be shown. + :param success: Alert message. + :param http_status_code: HTTP status code + :param context: web template context + :param indicator_color: color of indicator in title + :param primary_action: route on primary button (default is `/`) + :param primary_label: label on primary button (default is "Home") + :param fullpage: hide header / footer + :param width: Width of message in pixels + :param template: Optionally pass view template + """ + local.message_title = title + local.message = html + local.response["type"] = "page" + local.response["route"] = template + local.no_cache = 1 + + if http_status_code: + local.response["http_status_code"] = http_status_code + + if not context: + context = {} + + if not indicator_color: + if success: + indicator_color = "green" + elif http_status_code and http_status_code > 300: + indicator_color = "red" + else: + indicator_color = "blue" + + context["indicator_color"] = indicator_color + context["primary_label"] = primary_label + context["primary_action"] = primary_action + context["error_code"] = http_status_code + context["fullpage"] = fullpage + if width: + context["card_width"] = width + + local.response["context"] = context + + +def redirect(url): + """Raise a 301 redirect to url""" + from xhiveframework.exceptions import Redirect + + flags.redirect_location = url + raise Redirect + + +def redirect_to_message(title, html, http_status_code=None, context=None, indicator_color=None): + """Redirects to /message?id=random + Similar to respond_as_web_page, but used to 'redirect' and show message pages like success, failure, etc. with a detailed message + + :param title: Page title and heading. + :param message: Message to be shown. + :param http_status_code: HTTP status code. + + Example Usage: + xhiveframework.redirect_to_message(_('Thank you'), "

    You will receive an email at test@example.com

    ") + + """ + + message_id = generate_hash(length=8) + message = {"context": context or {}, "http_status_code": http_status_code or 200} + message["context"].update({"header": title, "title": title, "message": html}) + + if indicator_color: + message["context"].update({"indicator_color": indicator_color}) + + cache.set_value(f"message_id:{message_id}", message, expires_in_sec=60) + location = f"/message?id={message_id}" + + if not getattr(local, "is_ajax", False): + local.response["type"] = "redirect" + local.response["location"] = location + + else: + return location + + +def build_match_conditions(doctype, as_condition=True): + """Return match (User permissions) for given doctype as list or SQL.""" + import xhiveframework.desk.reportview + + return xhiveframework.desk.reportview.build_match_conditions(doctype, as_condition=as_condition) + + +def get_list(doctype, *args, **kwargs): + """List database query via `xhiveframework.model.db_query`. Will also check for permissions. + + :param doctype: DocType on which query is to be made. + :param fields: List of fields or `*`. + :param filters: List of filters (see example). + :param order_by: Order By e.g. `modified desc`. + :param limit_start: Start results at record #. Default 0. + :param limit_page_length: No of records in the page. Default 20. + + Example usage: + + # simple dict filter + xhiveframework.get_list("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"}) + + # filter as a list of lists + xhiveframework.get_list("ToDo", fields="*", filters = [["modified", ">", "2014-01-01"]]) + """ + import xhiveframework.model.db_query + + return xhiveframework.model.db_query.DatabaseQuery(doctype).execute(*args, **kwargs) + + +def get_all(doctype, *args, **kwargs): + """List database query via `xhiveframework.model.db_query`. Will **not** check for permissions. + Parameters are same as `xhiveframework.get_list` + + :param doctype: DocType on which query is to be made. + :param fields: List of fields or `*`. Default is: `["name"]`. + :param filters: List of filters (see example). + :param order_by: Order By e.g. `modified desc`. + :param limit_start: Start results at record #. Default 0. + :param limit_page_length: No of records in the page. Default 20. + + Example usage: + + # simple dict filter + xhiveframework.get_all("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"}) + + # filter as a list of lists + xhiveframework.get_all("ToDo", fields=["*"], filters = [["modified", ">", "2014-01-01"]]) + """ + kwargs["ignore_permissions"] = True + if "limit_page_length" not in kwargs: + kwargs["limit_page_length"] = 0 + return get_list(doctype, *args, **kwargs) + + +def get_value(*args, **kwargs): + """Returns a document property or list of properties. + + Alias for `xhiveframework.db.get_value` + + :param doctype: DocType name. + :param filters: Filters like `{"x":"y"}` or name of the document. `None` if Single DocType. + :param fieldname: Column name. + :param ignore: Don't raise exception if table, column is missing. + :param as_dict: Return values as dict. + :param debug: Print query in error log. + """ + return db.get_value(*args, **kwargs) + + +def as_json(obj: dict | list, indent=1, separators=None, ensure_ascii=True) -> str: + from xhiveframework.utils.response import json_handler + + if separators is None: + separators = (",", ": ") + + try: + return json.dumps( + obj, + indent=indent, + sort_keys=True, + default=json_handler, + separators=separators, + ensure_ascii=ensure_ascii, + ) + except TypeError: + # this would break in case the keys are not all os "str" type - as defined in the JSON + # adding this to ensure keys are sorted (expected behaviour) + sorted_obj = dict(sorted(obj.items(), key=lambda kv: str(kv[0]))) + return json.dumps( + sorted_obj, + indent=indent, + default=json_handler, + separators=separators, + ensure_ascii=ensure_ascii, + ) + + +def are_emails_muted(): + return flags.mute_emails or cint(conf.get("mute_emails") or 0) or False + + +def get_test_records(doctype): + """Returns list of objects from `test_records.json` in the given doctype's folder.""" + from xhiveframework.modules import get_doctype_module, get_module_path + + path = os.path.join( + get_module_path(get_doctype_module(doctype)), "doctype", scrub(doctype), "test_records.json" + ) + if os.path.exists(path): + with open(path) as f: + return json.loads(f.read()) + else: + return [] + + +def format_value(*args, **kwargs): + """Format value with given field properties. + + :param value: Value to be formatted. + :param df: (Optional) DocField object with properties `fieldtype`, `options` etc.""" + import xhiveframework.utils.formatters + + return xhiveframework.utils.formatters.format_value(*args, **kwargs) + + +def format(*args, **kwargs): + """Format value with given field properties. + + :param value: Value to be formatted. + :param df: (Optional) DocField object with properties `fieldtype`, `options` etc.""" + import xhiveframework.utils.formatters + + return xhiveframework.utils.formatters.format_value(*args, **kwargs) + + +def get_print( + doctype=None, + name=None, + print_format=None, + style=None, + as_pdf=False, + doc=None, + output=None, + no_letterhead=0, + password=None, + pdf_options=None, + letterhead=None, +): + """Get Print Format for given document. + + :param doctype: DocType of document. + :param name: Name of document. + :param print_format: Print Format name. Default 'Standard', + :param style: Print Format style. + :param as_pdf: Return as PDF. Default False. + :param password: Password to encrypt the pdf with. Default None""" + from xhiveframework.utils.pdf import get_pdf + from xhiveframework.website.serve import get_response_without_exception_handling + + original_form_dict = copy.deepcopy(local.form_dict) + try: + local.form_dict.doctype = doctype + local.form_dict.name = name + local.form_dict.format = print_format + local.form_dict.style = style + local.form_dict.doc = doc + local.form_dict.no_letterhead = no_letterhead + local.form_dict.letterhead = letterhead + + pdf_options = pdf_options or {} + if password: + pdf_options["password"] = password + + response = get_response_without_exception_handling("printview", 200) + html = str(response.data, "utf-8") + finally: + local.form_dict = original_form_dict + + return get_pdf(html, options=pdf_options, output=output) if as_pdf else html + + +def attach_print( + doctype, + name, + file_name=None, + print_format=None, + style=None, + html=None, + doc=None, + lang=None, + print_letterhead=True, + password=None, + letterhead=None, +): + from xhiveframework.translate import print_language + from xhiveframework.utils import scrub_urls + from xhiveframework.utils.pdf import get_pdf + + print_settings = db.get_singles_dict("Print Settings") + + kwargs = dict( + print_format=print_format, + style=style, + doc=doc, + no_letterhead=not print_letterhead, + letterhead=letterhead, + password=password, + ) + + local.flags.ignore_print_permissions = True + + with print_language(lang or local.lang): + content = "" + if cint(print_settings.send_print_as_pdf): + ext = ".pdf" + kwargs["as_pdf"] = True + content = ( + get_pdf(html, options={"password": password} if password else None) + if html + else get_print(doctype, name, **kwargs) + ) + else: + ext = ".html" + content = html or scrub_urls(get_print(doctype, name, **kwargs)).encode("utf-8") + + local.flags.ignore_print_permissions = False + + if not file_name: + file_name = name + file_name = cstr(file_name).replace(" ", "").replace("/", "-") + ext + + return {"fname": file_name, "fcontent": content} + + +def publish_progress(*args, **kwargs): + """Show the user progress for a long request + + :param percent: Percent progress + :param title: Title + :param doctype: Optional, for document type + :param docname: Optional, for document name + :param description: Optional description + """ + import xhiveframework.realtime + + return xhiveframework.realtime.publish_progress(*args, **kwargs) + + +def publish_realtime(*args, **kwargs): + """Publish real-time updates + + :param event: Event name, like `task_progress` etc. + :param message: JSON message object. For async must contain `task_id` + :param room: Room in which to publish update (default entire site) + :param user: Transmit to user + :param doctype: Transmit to doctype, docname + :param docname: Transmit to doctype, docname + :param after_commit: (default False) will emit after current transaction is committed + """ + import xhiveframework.realtime + + return xhiveframework.realtime.publish_realtime(*args, **kwargs) + + +def local_cache(namespace, key, generator, regenerate_if_none=False): + """A key value store for caching within a request + + :param namespace: xhiveframework.local.cache[namespace] + :param key: xhiveframework.local.cache[namespace][key] used to retrieve value + :param generator: method to generate a value if not found in store + + """ + if namespace not in local.cache: + local.cache[namespace] = {} + + if key not in local.cache[namespace]: + local.cache[namespace][key] = generator() + + elif local.cache[namespace][key] is None and regenerate_if_none: + # if key exists but the previous result was None + local.cache[namespace][key] = generator() + + return local.cache[namespace][key] + + +def enqueue(*args, **kwargs): + """ + Enqueue method to be executed using a background worker + + :param method: method string or method object + :param queue: (optional) should be either long, default or short + :param timeout: (optional) should be set according to the functions + :param event: this is passed to enable clearing of jobs from queues + :param is_async: (optional) if is_async=False, the method is executed immediately, else via a worker + :param job_name: (optional) can be used to name an enqueue call, which can be used to prevent duplicate calls + :param kwargs: keyword arguments to be passed to the method + """ + import xhiveframework.utils.background_jobs + + return xhiveframework.utils.background_jobs.enqueue(*args, **kwargs) + + +def task(**task_kwargs): + def decorator_task(f): + f.enqueue = lambda **fun_kwargs: enqueue(f, **task_kwargs, **fun_kwargs) + return f + + return decorator_task + + +def enqueue_doc(*args, **kwargs): + """ + Enqueue method to be executed using a background worker + + :param doctype: DocType of the document on which you want to run the event + :param name: Name of the document on which you want to run the event + :param method: method string or method object + :param queue: (optional) should be either long, default or short + :param timeout: (optional) should be set according to the functions + :param kwargs: keyword arguments to be passed to the method + """ + import xhiveframework.utils.background_jobs + + return xhiveframework.utils.background_jobs.enqueue_doc(*args, **kwargs) + + +def get_doctype_app(doctype): + def _get_doctype_app(): + doctype_module = local.db.get_value("DocType", doctype, "module") + return local.module_app[scrub(doctype_module)] + + return local_cache("doctype_app", doctype, generator=_get_doctype_app) + + +loggers = {} +log_level = None + + +def logger(module=None, with_more_info=False, allow_site=True, filter=None, max_size=100_000, file_count=20): + """Returns a python logger that uses StreamHandler""" + from xhiveframework.utils.logger import get_logger + + return get_logger( + module=module, + with_more_info=with_more_info, + allow_site=allow_site, + filter=filter, + max_size=max_size, + file_count=file_count, + ) + + +def get_desk_link(doctype, name): + html = '
    {doctype_local} {name}' + return html.format(doctype=doctype, name=name, doctype_local=_(doctype)) + + +def bold(text): + return f"{text}" + + +def safe_eval(code, eval_globals=None, eval_locals=None): + """A safer `eval`""" + + from xhiveframework.utils.safe_exec import safe_eval + + return safe_eval(code, eval_globals, eval_locals) + + +def get_website_settings(key): + if not hasattr(local, "website_settings"): + try: + local.website_settings = get_cached_doc("Website Settings") + except DoesNotExistError: + clear_last_message() + return + + return local.website_settings.get(key) + + +def get_system_settings(key): + if not hasattr(local, "system_settings"): + try: + local.system_settings = get_cached_doc("System Settings") + except DoesNotExistError: # possible during new install + clear_last_message() + return + + return local.system_settings.get(key) + + +def get_active_domains(): + from xhiveframework.core.doctype.domain_settings.domain_settings import get_active_domains + + return get_active_domains() + + +def get_version(doctype, name, limit=None, head=False, raise_err=True): + """ + Returns a list of version information of a given DocType. + + Note: Applicable only if DocType has changes tracked. + + Example + >>> xhiveframework.get_version("User", "foobar@gmail.com") + >>> + [ + { + "version": [version.data], # Refer Version DocType get_diff method and data attribute + "user": "admin@gmail.com", # User that created this version + "creation": # Creation timestamp of that object. + } + ] + """ + meta = get_meta(doctype) + if meta.track_changes: + names = get_all( + "Version", + filters={ + "ref_doctype": doctype, + "docname": name, + "order_by": "creation" if head else None, + "limit": limit, + }, + as_list=1, + ) + + from xhiveframework.utils import dictify, safe_json_loads, squashify + + versions = [] + + for name in names: + name = squashify(name) + doc = get_doc("Version", name) + + data = doc.data + data = safe_json_loads(data) + data = dictify(dict(version=data, user=doc.owner, creation=doc.creation)) + + versions.append(data) + + return versions + else: + if raise_err: + raise ValueError(_("{0} has no versions tracked.").format(doctype)) + + +@whitelist(allow_guest=True) +def ping(): + return "pong" + + +def safe_encode(param, encoding="utf-8"): + try: + param = param.encode(encoding) + except Exception: + pass + return param + + +def safe_decode(param, encoding="utf-8"): + try: + param = param.decode(encoding) + except Exception: + pass + return param + + +def parse_json(val): + from xhiveframework.utils import parse_json + + return parse_json(val) + + +def mock(type, size=1, locale="en"): + import faker + + results = [] + fake = faker.Faker(locale) + if type not in dir(fake): + raise ValueError("Not a valid mock type.") + else: + for _i in range(size): + data = getattr(fake, type)() + results.append(data) + + from xhiveframework.utils import squashify + + return squashify(results) + + +def validate_and_sanitize_search_inputs(fn): + @functools.wraps(fn) + def wrapper(*args, **kwargs): + from xhiveframework.desk.search import sanitize_searchfield + + kwargs.update(dict(zip(fn.__code__.co_varnames, args, strict=False))) + sanitize_searchfield(kwargs["searchfield"]) + kwargs["start"] = cint(kwargs["start"]) + kwargs["page_len"] = cint(kwargs["page_len"]) + + if kwargs["doctype"] and not db.exists("DocType", kwargs["doctype"]): + return [] + + return fn(**kwargs) + + return wrapper + + +def _register_fault_handler(): + faulthandler.register(signal.SIGUSR1) + + +from xhiveframework.utils.error import log_error + +if _tune_gc: + # generational GC gets triggered after certain allocs (g0) which is 700 by default. + # This number is quite small for xhiveframework where a single query can potentially create 700+ + # objects easily. + # Bump this number higher, this will make GC less aggressive but that improves performance of + # everything else. + g0, g1, g2 = gc.get_threshold() # defaults are 700, 10, 10. + gc.set_threshold(g0 * 10, g1 * 2, g2 * 2) + +# Remove references to pattern that are pre-compiled and loaded to global scopes. +re.purge() diff --git a/xhiveframework/api/__init__.py b/xhiveframework/api/__init__.py new file mode 100644 index 0000000..52a1fdd --- /dev/null +++ b/xhiveframework/api/__init__.py @@ -0,0 +1,80 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from enum import Enum + +from werkzeug.exceptions import NotFound +from werkzeug.routing import Map, Submount +from werkzeug.wrappers import Request, Response + +import xhiveframework +import xhiveframework.client +from xhiveframework import _ +from xhiveframework.utils.response import build_response + + +class ApiVersion(str, Enum): + V1 = "v1" + V2 = "v2" + + +def handle(request: Request): + """ + Entry point for `/api` methods. + + APIs are versioned using second part of path. + v1 -> `/api/v1/*` + v2 -> `/api/v2/*` + + Different versions have different specification but broadly following things are supported: + + - `/api/method/{methodname}` will call a whitelisted method + - `/api/resource/{doctype}` will query a table + examples: + - `?fields=["name", "owner"]` + - `?filters=[["Task", "name", "like", "%005"]]` + - `?limit_start=0` + - `?limit_page_length=20` + - `/api/resource/{doctype}/{name}` will point to a resource + `GET` will return document + `POST` will insert + `PUT` will update + `DELETE` will delete + """ + + try: + endpoint, arguments = API_URL_MAP.bind_to_environ(request.environ).match() + except NotFound: # Wrap 404 - backward compatiblity + raise xhiveframework.DoesNotExistError + + data = endpoint(**arguments) + if isinstance(data, Response): + return data + + if data is not None: + xhiveframework.response["data"] = data + return build_response("json") + + +# Merge all API version routing rules +from xhiveframework.api.v1 import url_rules as v1_rules +from xhiveframework.api.v2 import url_rules as v2_rules + +API_URL_MAP = Map( + [ + # V1 routes + Submount("/api", v1_rules), + Submount(f"/api/{ApiVersion.V1.value}", v1_rules), + Submount(f"/api/{ApiVersion.V2.value}", v2_rules), + ], + strict_slashes=False, # Allows skipping trailing slashes + merge_slashes=False, +) + + +def get_api_version() -> ApiVersion | None: + if not xhiveframework.request: + return + + if xhiveframework.request.path.startswith(f"/api/{ApiVersion.V2.value}"): + return ApiVersion.V2 + return ApiVersion.V1 diff --git a/xhiveframework/api/utils.py b/xhiveframework/api/utils.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/api/v1.py b/xhiveframework/api/v1.py new file mode 100644 index 0000000..235d490 --- /dev/null +++ b/xhiveframework/api/v1.py @@ -0,0 +1,118 @@ +import json + +from werkzeug.routing import Rule + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils.data import sbool + + +def document_list(doctype: str): + if xhiveframework.form_dict.get("fields"): + xhiveframework.form_dict["fields"] = json.loads(xhiveframework.form_dict["fields"]) + + # set limit of records for xhiveframework.get_list + xhiveframework.form_dict.setdefault( + "limit_page_length", + xhiveframework.form_dict.limit or xhiveframework.form_dict.limit_page_length or 20, + ) + + # convert strings to native types - only as_dict and debug accept bool + for param in ["as_dict", "debug"]: + param_val = xhiveframework.form_dict.get(param) + if param_val is not None: + xhiveframework.form_dict[param] = sbool(param_val) + + # evaluate xhiveframework.get_list + return xhiveframework.call(xhiveframework.client.get_list, doctype, **xhiveframework.form_dict) + + +def handle_rpc_call(method: str): + import xhiveframework.handler + + method = method.split("/")[0] # for backward compatiblity + + xhiveframework.form_dict.cmd = method + return xhiveframework.handler.handle() + + +def create_doc(doctype: str): + data = get_request_form_data() + data.pop("doctype", None) + return xhiveframework.new_doc(doctype, **data).insert() + + +def update_doc(doctype: str, name: str): + data = get_request_form_data() + + doc = xhiveframework.get_doc(doctype, name, for_update=True) + if "flags" in data: + del data["flags"] + + doc.update(data) + doc.save() + + # check for child table doctype + if doc.get("parenttype"): + xhiveframework.get_doc(doc.parenttype, doc.parent).save() + + return doc + + +def delete_doc(doctype: str, name: str): + # TODO: child doc handling + xhiveframework.delete_doc(doctype, name, ignore_missing=False) + xhiveframework.response.http_status_code = 202 + return "ok" + + +def read_doc(doctype: str, name: str): + # Backward compatiblity + if "run_method" in xhiveframework.form_dict: + return execute_doc_method(doctype, name) + + doc = xhiveframework.get_doc(doctype, name) + if not doc.has_permission("read"): + raise xhiveframework.PermissionError + doc.apply_fieldlevel_read_permissions() + return doc + + +def execute_doc_method(doctype: str, name: str, method: str | None = None): + method = method or xhiveframework.form_dict.pop("run_method") + doc = xhiveframework.get_doc(doctype, name) + doc.is_whitelisted(method) + + if xhiveframework.request.method == "GET": + if not doc.has_permission("read"): + xhiveframework.throw(_("Not permitted"), xhiveframework.PermissionError) + return doc.run_method(method, **xhiveframework.form_dict) + + elif xhiveframework.request.method == "POST": + if not doc.has_permission("write"): + xhiveframework.throw(_("Not permitted"), xhiveframework.PermissionError) + + return doc.run_method(method, **xhiveframework.form_dict) + + +def get_request_form_data(): + if xhiveframework.form_dict.data is None: + data = xhiveframework.safe_decode(xhiveframework.request.get_data()) + else: + data = xhiveframework.form_dict.data + + try: + return xhiveframework.parse_json(data) + except ValueError: + return xhiveframework.form_dict + + +url_rules = [ + Rule("/method/", endpoint=handle_rpc_call), + Rule("/resource/", methods=["GET"], endpoint=document_list), + Rule("/resource/", methods=["POST"], endpoint=create_doc), + Rule("/resource///", methods=["GET"], endpoint=read_doc), + Rule("/resource///", methods=["PUT"], endpoint=update_doc), + Rule("/resource///", methods=["DELETE"], endpoint=delete_doc), + Rule("/resource///", methods=["POST"], endpoint=execute_doc_method), +] diff --git a/xhiveframework/api/v2.py b/xhiveframework/api/v2.py new file mode 100644 index 0000000..2bb3485 --- /dev/null +++ b/xhiveframework/api/v2.py @@ -0,0 +1,193 @@ +"""REST API v2 + +This file defines routes and implementation for REST API. + +Note: + - All functions in this file should be treated as "whitelisted" as they are exposed via routes + - None of the functions present here should be called from python code, their location and + internal implementation can change without treating it as "breaking change". +""" +import json +from typing import Any + +from werkzeug.routing import Rule + +import xhiveframework +import xhiveframework.client +from xhiveframework import _, get_newargs, is_whitelisted +from xhiveframework.core.doctype.server_script.server_script_utils import get_server_script_map +from xhiveframework.handler import is_valid_http_method, run_server_script, upload_file + +PERMISSION_MAP = { + "GET": "read", + "POST": "write", +} + + +def handle_rpc_call(method: str, doctype: str | None = None): + from xhiveframework.modules.utils import load_doctype_module + + if doctype: + # Expand to run actual method from doctype controller + module = load_doctype_module(doctype) + method = module.__name__ + "." + method + + for hook in reversed(xhiveframework.get_hooks("override_whitelisted_methods", {}).get(method, [])): + # override using the last hook + method = hook + break + + # via server script + server_script = get_server_script_map().get("_api", {}).get(method) + if server_script: + return run_server_script(server_script) + + try: + method = xhiveframework.get_attr(method) + except Exception as e: + xhiveframework.throw(_("Failed to get method {0} with {1}").format(method, e)) + + is_whitelisted(method) + is_valid_http_method(method) + + return xhiveframework.call(method, **xhiveframework.form_dict) + + +def login(): + """Login happens implicitly, this function doesn't do anything.""" + pass + + +def logout(): + xhiveframework.local.login_manager.logout() + xhiveframework.db.commit() + + +def read_doc(doctype: str, name: str): + doc = xhiveframework.get_doc(doctype, name) + doc.check_permission("read") + doc.apply_fieldlevel_read_permissions() + return doc + + +def document_list(doctype: str): + if xhiveframework.form_dict.get("fields"): + xhiveframework.form_dict["fields"] = json.loads(xhiveframework.form_dict["fields"]) + + # set limit of records for xhiveframework.get_list + xhiveframework.form_dict.limit_page_length = xhiveframework.form_dict.limit or 20 + # evaluate xhiveframework.get_list + return xhiveframework.call(xhiveframework.client.get_list, doctype, **xhiveframework.form_dict) + + +def count(doctype: str) -> int: + from xhiveframework.desk.reportview import get_count + + xhiveframework.form_dict.doctype = doctype + + return get_count() + + +def create_doc(doctype: str): + data = xhiveframework.form_dict + data.pop("doctype", None) + return xhiveframework.new_doc(doctype, **data).insert() + + +def update_doc(doctype: str, name: str): + data = xhiveframework.form_dict + + doc = xhiveframework.get_doc(doctype, name, for_update=True) + data.pop("flags", None) + doc.update(data) + doc.save() + + # check for child table doctype + if doc.get("parenttype"): + xhiveframework.get_doc(doc.parenttype, doc.parent).save() + + return doc + + +def delete_doc(doctype: str, name: str): + xhiveframework.client.delete_doc(doctype, name) + xhiveframework.response.http_status_code = 202 + return "ok" + + +def execute_doc_method(doctype: str, name: str, method: str | None = None): + """Get a document from DB and execute method on it. + + Use cases: + - Submitting/cancelling document + - Triggering some kind of update on a document + """ + method = method or xhiveframework.form_dict.pop("run_method") + doc = xhiveframework.get_doc(doctype, name) + doc.is_whitelisted(method) + + doc.check_permission(PERMISSION_MAP[xhiveframework.request.method]) + return doc.run_method(method, **xhiveframework.form_dict) + + +def run_doc_method(method: str, document: dict[str, Any] | str, kwargs=None): + """run a whitelisted controller method on in-memory document. + + + This is useful for building clients that don't necessarily encode all the business logic but + call server side function on object to validate and modify the doc. + + The doc CAN exists in DB too and can write to DB as well if method is POST. + """ + + if isinstance(document, str): + document = xhiveframework.parse_json(document) + + if kwargs is None: + kwargs = {} + + doc = xhiveframework.get_doc(document) + doc._original_modified = doc.modified + doc.check_if_latest() + + doc.check_permission(PERMISSION_MAP[xhiveframework.request.method]) + + method_obj = getattr(doc, method) + fn = getattr(method_obj, "__func__", method_obj) + is_whitelisted(fn) + is_valid_http_method(fn) + + new_kwargs = get_newargs(fn, kwargs) + response = doc.run_method(method, **new_kwargs) + xhiveframework.response.docs.append(doc) # send modified document and result both. + return response + + +url_rules = [ + # RPC calls + Rule("/method/login", endpoint=login), + Rule("/method/logout", endpoint=logout), + Rule("/method/ping", endpoint=xhiveframework.ping), + Rule("/method/upload_file", endpoint=upload_file), + Rule("/method/", endpoint=handle_rpc_call), + Rule( + "/method/run_doc_method", + methods=["GET", "POST"], + endpoint=lambda: xhiveframework.call(run_doc_method, **xhiveframework.form_dict), + ), + Rule("/method//", endpoint=handle_rpc_call), + # Document level APIs + Rule("/document/", methods=["GET"], endpoint=document_list), + Rule("/document/", methods=["POST"], endpoint=create_doc), + Rule("/document///", methods=["GET"], endpoint=read_doc), + Rule("/document///", methods=["PATCH", "PUT"], endpoint=update_doc), + Rule("/document///", methods=["DELETE"], endpoint=delete_doc), + Rule( + "/document///method//", + methods=["GET", "POST"], + endpoint=execute_doc_method, + ), + # Collection level APIs + Rule("/doctype//meta", methods=["GET"], endpoint=xhiveframework.get_meta), + Rule("/doctype//count", methods=["GET"], endpoint=count), +] diff --git a/xhiveframework/app.py b/xhiveframework/app.py new file mode 100644 index 0000000..0d459f2 --- /dev/null +++ b/xhiveframework/app.py @@ -0,0 +1,532 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import functools +import gc +import logging +import os +import re + +from werkzeug.exceptions import HTTPException, NotFound +from werkzeug.middleware.profiler import ProfilerMiddleware +from werkzeug.middleware.proxy_fix import ProxyFix +from werkzeug.middleware.shared_data import SharedDataMiddleware +from werkzeug.wrappers import Request, Response +from werkzeug.wsgi import ClosingIterator + +import xhiveframework +import xhiveframework.api +import xhiveframework.handler +import xhiveframework.monitor +import xhiveframework.rate_limiter +import xhiveframework.recorder +import xhiveframework.utils.response +from xhiveframework import _ +from xhiveframework.auth import SAFE_HTTP_METHODS, UNSAFE_HTTP_METHODS, HTTPRequest, validate_auth +from xhiveframework.middlewares import StaticDataMiddleware +from xhiveframework.utils import CallbackManager, cint, get_site_name +from xhiveframework.utils.data import escape_html +from xhiveframework.utils.deprecations import deprecation_warning +from xhiveframework.utils.error import log_error_snapshot +from xhiveframework.website.serve import get_response + +_site = None +_sites_path = os.environ.get("SITES_PATH", ".") + + +# If gc.freeze is done then importing modules before forking allows us to share the memory +if xhiveframework._tune_gc: + import bleach + import pydantic + + import xhiveframework.boot + import xhiveframework.client + import xhiveframework.core.doctype.file.file + import xhiveframework.core.doctype.user.user + import xhiveframework.database.mariadb.database # Load database related utils + import xhiveframework.database.query + import xhiveframework.desk.desktop # workspace + import xhiveframework.desk.form.save + import xhiveframework.model.db_query + import xhiveframework.query_builder + import xhiveframework.utils.background_jobs # Enqueue is very common + import xhiveframework.utils.data # common utils + import xhiveframework.utils.jinja # web page rendering + import xhiveframework.utils.jinja_globals + import xhiveframework.utils.redis_wrapper # Exact redis_wrapper + import xhiveframework.utils.safe_exec + import xhiveframework.utils.typing_validations # any whitelisted method uses this + import xhiveframework.website.path_resolver # all the page types and resolver + import xhiveframework.website.router # Website router + import xhiveframework.website.website_generator # web page doctypes + +# end: module pre-loading + + +def after_response_wrapper(app): + """Wrap a WSGI application to call after_response hooks after we have responded. + + This is done to reduce response time by deferring expensive tasks.""" + + @functools.wraps(app) + def application(environ, start_response): + return ClosingIterator( + app(environ, start_response), + ( + xhiveframework.rate_limiter.update, + xhiveframework.monitor.stop, + xhiveframework.recorder.dump, + xhiveframework.request.after_response.run, + xhiveframework.destroy, + ), + ) + + return application + + +@after_response_wrapper +@Request.application +def application(request: Request): + response = None + + try: + rollback = True + + init_request(request) + + validate_auth() + + if request.method == "OPTIONS": + response = Response() + + elif xhiveframework.form_dict.cmd: + deprecation_warning( + f"{xhiveframework.form_dict.cmd}: Sending `cmd` for RPC calls is deprecated, call REST API instead `/api/method/cmd`" + ) + xhiveframework.handler.handle() + response = xhiveframework.utils.response.build_response("json") + + elif request.path.startswith("/api/"): + response = xhiveframework.api.handle(request) + + elif request.path.startswith("/backups"): + response = xhiveframework.utils.response.download_backup(request.path) + + elif request.path.startswith("/private/files/"): + response = xhiveframework.utils.response.download_private_file(request.path) + + elif request.method in ("GET", "HEAD", "POST"): + response = get_response() + + else: + raise NotFound + + except HTTPException as e: + return e + + except Exception as e: + response = handle_exception(e) + + else: + rollback = sync_database(rollback) + + finally: + # Important note: + # this function *must* always return a response, hence any exception thrown outside of + # try..catch block like this finally block needs to be handled appropriately. + + if rollback and request.method in UNSAFE_HTTP_METHODS and xhiveframework.db: + xhiveframework.db.rollback() + + try: + run_after_request_hooks(request, response) + except Exception: + # We can not handle exceptions safely here. + xhiveframework.logger().error("Failed to run after request hook", exc_info=True) + + log_request(request, response) + process_response(response) + + return response + + +def run_after_request_hooks(request, response): + if not getattr(xhiveframework.local, "initialised", False): + return + + for after_request_task in xhiveframework.get_hooks("after_request"): + xhiveframework.call(after_request_task, response=response, request=request) + + +def init_request(request): + xhiveframework.local.request = request + xhiveframework.local.request.after_response = CallbackManager() + + xhiveframework.local.is_ajax = xhiveframework.get_request_header("X-Requested-With") == "XMLHttpRequest" + + site = _site or request.headers.get("X-XhiveFramework-Site-Name") or get_site_name(request.host) + xhiveframework.init(site=site, sites_path=_sites_path, force=True) + + if not (xhiveframework.local.conf and xhiveframework.local.conf.db_name): + # site does not exist + raise NotFound + + if xhiveframework.local.conf.maintenance_mode: + xhiveframework.connect() + if xhiveframework.local.conf.allow_reads_during_maintenance: + setup_read_only_mode() + else: + raise xhiveframework.SessionStopped("Session Stopped") + else: + xhiveframework.connect(set_admin_as_user=False) + if request.path.startswith("/api/method/upload_file"): + from xhiveframework.core.api.file import get_max_file_size + + request.max_content_length = get_max_file_size() + else: + request.max_content_length = cint(xhiveframework.local.conf.get("max_file_size")) or 25 * 1024 * 1024 + make_form_dict(request) + + if request.method != "OPTIONS": + xhiveframework.local.http_request = HTTPRequest() + + for before_request_task in xhiveframework.get_hooks("before_request"): + xhiveframework.call(before_request_task) + + +def setup_read_only_mode(): + """During maintenance_mode reads to DB can still be performed to reduce downtime. This + function sets up read only mode + + - Setting global flag so other pages, desk and database can know that we are in read only mode. + - Setup read only database access either by: + - Connecting to read replica if one exists + - Or setting up read only SQL transactions. + """ + xhiveframework.flags.read_only = True + + # If replica is available then just connect replica, else setup read only transaction. + if xhiveframework.conf.read_from_replica: + xhiveframework.connect_replica() + else: + xhiveframework.db.begin(read_only=True) + + +def log_request(request, response): + if hasattr(xhiveframework.local, "conf") and xhiveframework.local.conf.enable_xhiveframework_logger: + xhiveframework.logger("xhiveframework.web", allow_site=xhiveframework.local.site).info( + { + "site": get_site_name(request.host), + "remote_addr": getattr(request, "remote_addr", "NOTFOUND"), + "pid": os.getpid(), + "user": getattr(xhiveframework.local.session, "user", "NOTFOUND"), + "base_url": getattr(request, "base_url", "NOTFOUND"), + "full_path": getattr(request, "full_path", "NOTFOUND"), + "method": getattr(request, "method", "NOTFOUND"), + "scheme": getattr(request, "scheme", "NOTFOUND"), + "http_status_code": getattr(response, "status_code", "NOTFOUND"), + } + ) + + +def process_response(response): + if not response: + return + + # set cookies + if hasattr(xhiveframework.local, "cookie_manager"): + xhiveframework.local.cookie_manager.flush_cookies(response=response) + + # rate limiter headers + if hasattr(xhiveframework.local, "rate_limiter"): + response.headers.extend(xhiveframework.local.rate_limiter.headers()) + + if trace_id := xhiveframework.monitor.get_trace_id(): + response.headers.extend({"X-XhiveFramework-Request-Id": trace_id}) + + # CORS headers + if hasattr(xhiveframework.local, "conf"): + set_cors_headers(response) + + +def set_cors_headers(response): + if not ( + (allowed_origins := xhiveframework.conf.allow_cors) + and (request := xhiveframework.local.request) + and (origin := request.headers.get("Origin")) + ): + return + + if allowed_origins != "*": + if not isinstance(allowed_origins, list): + allowed_origins = [allowed_origins] + + if origin not in allowed_origins: + return + + cors_headers = { + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Origin": origin, + "Vary": "Origin", + } + + # only required for preflight requests + if request.method == "OPTIONS": + cors_headers["Access-Control-Allow-Methods"] = request.headers.get("Access-Control-Request-Method") + + if allowed_headers := request.headers.get("Access-Control-Request-Headers"): + cors_headers["Access-Control-Allow-Headers"] = allowed_headers + + # allow browsers to cache preflight requests for upto a day + if not xhiveframework.conf.developer_mode: + cors_headers["Access-Control-Max-Age"] = "86400" + + response.headers.extend(cors_headers) + + +def make_form_dict(request: Request): + import json + + request_data = request.get_data(as_text=True) + if request_data and request.is_json: + args = json.loads(request_data) + else: + args = {} + args.update(request.args or {}) + args.update(request.form or {}) + + if isinstance(args, dict): + xhiveframework.local.form_dict = xhiveframework._dict(args) + # _ is passed by $.ajax so that the request is not cached by the browser. So, remove _ from form_dict + xhiveframework.local.form_dict.pop("_", None) + elif isinstance(args, list): + xhiveframework.local.form_dict["data"] = args + else: + xhiveframework.throw(_("Invalid request arguments")) + + +def handle_exception(e): + response = None + http_status_code = getattr(e, "http_status_code", 500) + return_as_message = False + accept_header = xhiveframework.get_request_header("Accept") or "" + respond_as_json = ( + xhiveframework.get_request_header("Accept") + and (xhiveframework.local.is_ajax or "application/json" in accept_header) + or (xhiveframework.local.request.path.startswith("/api/") and not accept_header.startswith("text")) + ) + + allow_traceback = xhiveframework.get_system_settings("allow_error_traceback") if xhiveframework.db else False + + if not xhiveframework.session.user: + # If session creation fails then user won't be unset. This causes a lot of code that + # assumes presence of this to fail. Session creation fails => guest or expired login + # usually. + xhiveframework.session.user = "Guest" + + if respond_as_json: + # handle ajax responses first + # if the request is ajax, send back the trace or error message + response = xhiveframework.utils.response.report_error(http_status_code) + + elif isinstance(e, xhiveframework.SessionStopped): + response = xhiveframework.utils.response.handle_session_stopped() + + elif ( + http_status_code == 500 + and (xhiveframework.db and isinstance(e, xhiveframework.db.InternalError)) + and (xhiveframework.db and (xhiveframework.db.is_deadlocked(e) or xhiveframework.db.is_timedout(e))) + ): + http_status_code = 508 + + elif http_status_code == 401: + xhiveframework.respond_as_web_page( + _("Session Expired"), + _("Your session has expired, please login again to continue."), + http_status_code=http_status_code, + indicator_color="red", + ) + return_as_message = True + + elif http_status_code == 403: + xhiveframework.respond_as_web_page( + _("Not Permitted"), + _("You do not have enough permissions to complete the action"), + http_status_code=http_status_code, + indicator_color="red", + ) + return_as_message = True + + elif http_status_code == 404: + xhiveframework.respond_as_web_page( + _("Not Found"), + _("The resource you are looking for is not available"), + http_status_code=http_status_code, + indicator_color="red", + ) + return_as_message = True + + elif http_status_code == 429: + response = xhiveframework.rate_limiter.respond() + + else: + traceback = "
    " + escape_html(xhiveframework.get_traceback()) + "
    " + # disable traceback in production if flag is set + if xhiveframework.local.flags.disable_traceback or not allow_traceback and not xhiveframework.local.dev_server: + traceback = "" + + xhiveframework.respond_as_web_page( + "Server Error", traceback, http_status_code=http_status_code, indicator_color="red", width=640 + ) + return_as_message = True + + if e.__class__ == xhiveframework.AuthenticationError: + if hasattr(xhiveframework.local, "login_manager"): + xhiveframework.local.login_manager.clear_cookies() + + if http_status_code >= 500: + log_error_snapshot(e) + + if return_as_message: + response = get_response("message", http_status_code=http_status_code) + + if xhiveframework.conf.get("developer_mode") and not respond_as_json: + # don't fail silently for non-json response errors + print(xhiveframework.get_traceback()) + + return response + + +def sync_database(rollback: bool) -> bool: + # if HTTP method would change server state, commit if necessary + if xhiveframework.db and (xhiveframework.local.flags.commit or xhiveframework.local.request.method in UNSAFE_HTTP_METHODS): + xhiveframework.db.commit() + rollback = False + elif xhiveframework.db: + xhiveframework.db.rollback() + rollback = False + + # update session + if session := getattr(xhiveframework.local, "session_obj", None): + if session.update(): + xhiveframework.db.commit() + rollback = False + + return rollback + + +# Always initialize sentry SDK if the DSN is sent +if sentry_dsn := os.getenv("XHIVEFRAMEWORK_SENTRY_DSN"): + import sentry_sdk + from sentry_sdk.integrations.argv import ArgvIntegration + from sentry_sdk.integrations.atexit import AtexitIntegration + from sentry_sdk.integrations.dedupe import DedupeIntegration + from sentry_sdk.integrations.excepthook import ExcepthookIntegration + from sentry_sdk.integrations.modules import ModulesIntegration + from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware + + from xhiveframework.utils.sentry import XhiveFrameworkIntegration, before_send + + integrations = [ + AtexitIntegration(), + ExcepthookIntegration(), + DedupeIntegration(), + ModulesIntegration(), + ArgvIntegration(), + ] + + experiments = {} + kwargs = {} + + if os.getenv("ENABLE_SENTRY_DB_MONITORING"): + integrations.append(XhiveFrameworkIntegration()) + experiments["record_sql_params"] = True + + if tracing_sample_rate := os.getenv("SENTRY_TRACING_SAMPLE_RATE"): + kwargs["traces_sample_rate"] = float(tracing_sample_rate) + application = SentryWsgiMiddleware(application) + + sentry_sdk.init( + dsn=sentry_dsn, + before_send=before_send, + attach_stacktrace=True, + release=xhiveframework.__version__, + auto_enabling_integrations=False, + default_integrations=False, + integrations=integrations, + _experiments=experiments, + **kwargs, + ) + + +def serve( + port=8000, + profile=False, + no_reload=False, + no_threading=False, + site=None, + sites_path=".", + proxy=False, +): + global application, _site, _sites_path + _site = site + _sites_path = sites_path + + from werkzeug.serving import run_simple + + if profile or os.environ.get("USE_PROFILER"): + application = ProfilerMiddleware(application, sort_by=("cumtime", "calls")) + + if not os.environ.get("NO_STATICS"): + application = application_with_statics() + + if proxy or os.environ.get("USE_PROXY"): + application = ProxyFix(application, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1) + + application.debug = True + application.config = {"SERVER_NAME": "127.0.0.1:8000"} + + log = logging.getLogger("werkzeug") + log.propagate = False + + in_test_env = os.environ.get("CI") + if in_test_env: + log.setLevel(logging.ERROR) + + run_simple( + "0.0.0.0", + int(port), + application, + exclude_patterns=["test_*"], + use_reloader=False if in_test_env else not no_reload, + use_debugger=not in_test_env, + use_evalex=not in_test_env, + threaded=not no_threading, + ) + + +def application_with_statics(): + global application, _sites_path + + application = SharedDataMiddleware(application, {"/assets": str(os.path.join(_sites_path, "assets"))}) + + application = StaticDataMiddleware(application, {"/files": str(os.path.abspath(_sites_path))}) + + return application + + +# Remove references to pattern that are pre-compiled and loaded to global scopes. +re.purge() + +# Both Gunicorn and RQ use forking to spawn workers. In an ideal world, the fork should be sharing +# most of the memory if there are no writes made to data because of Copy on Write, however, +# python's GC is not CoW friendly and writes to data even if user-code doesn't. Specifically, the +# generational GC which stores and mutates every python object: `PyGC_Head` +# +# Calling gc.freeze() moves all the objects imported so far into permanant generation and hence +# doesn't mutate `PyGC_Head` +# +# Refer to issue for more info: https://lab.membtech.com/xhiveframework/xhiveframework15/issues/18927 +if xhiveframework._tune_gc: + gc.collect() # clean up any garbage created so far before freeze + gc.freeze() diff --git a/xhiveframework/auth.py b/xhiveframework/auth.py new file mode 100644 index 0000000..269ddef --- /dev/null +++ b/xhiveframework/auth.py @@ -0,0 +1,685 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE +import base64 +import binascii +from urllib.parse import quote, urlencode, urlparse + +from werkzeug.wrappers import Response + +import xhiveframework +import xhiveframework.database +import xhiveframework.utils +import xhiveframework.utils.user +from xhiveframework import _ +from xhiveframework.core.doctype.activity_log.activity_log import add_authentication_log +from xhiveframework.sessions import Session, clear_sessions, delete_session, get_expiry_in_seconds +from xhiveframework.translate import get_language +from xhiveframework.twofactor import ( + authenticate_for_2factor, + confirm_otp_token, + get_cached_user_pass, + should_run_2fa, +) +from xhiveframework.utils import cint, date_diff, datetime, get_datetime, today +from xhiveframework.utils.deprecations import deprecation_warning +from xhiveframework.utils.password import check_password, get_decrypted_password +from xhiveframework.website.utils import get_home_page + +SAFE_HTTP_METHODS = frozenset(("GET", "HEAD", "OPTIONS")) +UNSAFE_HTTP_METHODS = frozenset(("POST", "PUT", "DELETE", "PATCH")) +MAX_PASSWORD_SIZE = 512 + + +class HTTPRequest: + def __init__(self): + # set xhiveframework.local.request_ip + self.set_request_ip() + + # load cookies + self.set_cookies() + + # login and start/resume user session + self.set_session() + + # set request language + self.set_lang() + + # match csrf token from current session + self.validate_csrf_token() + + # write out latest cookies + xhiveframework.local.cookie_manager.init_cookies() + + @property + def domain(self): + if not getattr(self, "_domain", None): + self._domain = xhiveframework.request.host + if self._domain and self._domain.startswith("www."): + self._domain = self._domain[4:] + + return self._domain + + def set_request_ip(self): + if xhiveframework.get_request_header("X-Forwarded-For"): + xhiveframework.local.request_ip = (xhiveframework.get_request_header("X-Forwarded-For").split(",", 1)[0]).strip() + + elif xhiveframework.get_request_header("REMOTE_ADDR"): + xhiveframework.local.request_ip = xhiveframework.get_request_header("REMOTE_ADDR") + + else: + xhiveframework.local.request_ip = "127.0.0.1" + + def set_cookies(self): + xhiveframework.local.cookie_manager = CookieManager() + + def set_session(self): + xhiveframework.local.login_manager = LoginManager() + + def validate_csrf_token(self): + if ( + not xhiveframework.request + or xhiveframework.request.method not in UNSAFE_HTTP_METHODS + or xhiveframework.conf.ignore_csrf + or not xhiveframework.session + or not (saved_token := xhiveframework.session.data.csrf_token) + or ( + (xhiveframework.get_request_header("X-XhiveFramework-CSRF-Token") or xhiveframework.form_dict.pop("csrf_token", None)) + == saved_token + ) + ): + return + + xhiveframework.flags.disable_traceback = True + xhiveframework.throw(_("Invalid Request"), xhiveframework.CSRFTokenError) + + def set_lang(self): + xhiveframework.local.lang = get_language() + + +class LoginManager: + __slots__ = ("user", "info", "full_name", "user_type", "resume") + + def __init__(self): + self.user = None + self.info = None + self.full_name = None + self.user_type = None + + if xhiveframework.local.form_dict.get("cmd") == "login" or xhiveframework.local.request.path == "/api/method/login": + if self.login() is False: + return + self.resume = False + + # run login triggers + self.run_trigger("on_session_creation") + else: + try: + self.resume = True + self.make_session(resume=True) + self.get_user_info() + self.set_user_info(resume=True) + except AttributeError: + self.user = "Guest" + self.get_user_info() + self.make_session() + self.set_user_info() + + def login(self): + if xhiveframework.get_system_settings("disable_user_pass_login"): + xhiveframework.throw(_("Login with username and password is not allowed."), xhiveframework.AuthenticationError) + + # clear cache + xhiveframework.clear_cache(user=xhiveframework.form_dict.get("usr")) + user, pwd = get_cached_user_pass() + self.authenticate(user=user, pwd=pwd) + if self.force_user_to_reset_password(): + doc = xhiveframework.get_doc("User", self.user) + xhiveframework.local.response["redirect_to"] = doc.reset_password(send_email=False, password_expired=True) + xhiveframework.local.response["message"] = "Password Reset" + return False + + if should_run_2fa(self.user): + authenticate_for_2factor(self.user) + if not confirm_otp_token(self): + return False + xhiveframework.form_dict.pop("pwd", None) + self.post_login() + + def post_login(self): + self.run_trigger("on_login") + validate_ip_address(self.user) + self.validate_hour() + self.get_user_info() + self.make_session() + self.setup_boot_cache() + self.set_user_info() + + def get_user_info(self): + self.info = xhiveframework.get_cached_value( + "User", self.user, ["user_type", "first_name", "last_name", "user_image"], as_dict=1 + ) + + self.user_type = self.info.user_type + + def setup_boot_cache(self): + xhiveframework.cache_manager.build_table_count_cache() + xhiveframework.cache_manager.build_domain_restriced_doctype_cache() + xhiveframework.cache_manager.build_domain_restriced_page_cache() + + def set_user_info(self, resume=False): + # set sid again + xhiveframework.local.cookie_manager.init_cookies() + + self.full_name = " ".join(filter(None, [self.info.first_name, self.info.last_name])) + + if self.info.user_type == "Website User": + xhiveframework.local.cookie_manager.set_cookie("system_user", "no") + if not resume: + xhiveframework.local.response["message"] = "No App" + xhiveframework.local.response["home_page"] = "/" + get_home_page() + else: + xhiveframework.local.cookie_manager.set_cookie("system_user", "yes") + if not resume: + xhiveframework.local.response["message"] = "Logged In" + xhiveframework.local.response["home_page"] = "/app" + + if not resume: + xhiveframework.response["full_name"] = self.full_name + + # redirect information + redirect_to = xhiveframework.cache.hget("redirect_after_login", self.user) + if redirect_to: + xhiveframework.local.response["redirect_to"] = redirect_to + xhiveframework.cache.hdel("redirect_after_login", self.user) + + xhiveframework.local.cookie_manager.set_cookie("full_name", self.full_name) + xhiveframework.local.cookie_manager.set_cookie("user_id", self.user) + xhiveframework.local.cookie_manager.set_cookie("user_image", self.info.user_image or "") + + def clear_preferred_language(self): + xhiveframework.local.cookie_manager.delete_cookie("preferred_language") + + def make_session(self, resume=False): + # start session + xhiveframework.local.session_obj = Session( + user=self.user, resume=resume, full_name=self.full_name, user_type=self.user_type + ) + + # reset user if changed to Guest + self.user = xhiveframework.local.session_obj.user + xhiveframework.local.session = xhiveframework.local.session_obj.data + self.clear_active_sessions() + + def clear_active_sessions(self): + """Clear other sessions of the current user if `deny_multiple_sessions` is not set""" + if xhiveframework.session.user == "Guest": + return + + if not ( + cint(xhiveframework.conf.get("deny_multiple_sessions")) + or cint(xhiveframework.db.get_system_setting("deny_multiple_sessions")) + ): + return + + clear_sessions(xhiveframework.session.user, keep_current=True) + + def authenticate(self, user: str | None = None, pwd: str | None = None): + from xhiveframework.core.doctype.user.user import User + + if not (user and pwd): + user, pwd = xhiveframework.form_dict.get("usr"), xhiveframework.form_dict.get("pwd") + if not (user and pwd): + self.fail(_("Incomplete login details"), user=user) + + if len(pwd) > MAX_PASSWORD_SIZE: + self.fail(_("Password size exceeded the maximum allowed size"), user=user) + + _raw_user_name = user + user = User.find_by_credentials(user, pwd) + + ip_tracker = get_login_attempt_tracker(xhiveframework.local.request_ip) + if not user: + ip_tracker and ip_tracker.add_failure_attempt() + self.fail("Invalid login credentials", user=_raw_user_name) + + # Current login flow uses cached credentials for authentication while checking OTP. + # Incase of OTP check, tracker for auth needs to be disabled(If not, it can remove tracker history as it is going to succeed anyway) + # Tracker is activated for 2FA incase of OTP. + ignore_tracker = should_run_2fa(user.name) and ("otp" in xhiveframework.form_dict) + user_tracker = None if ignore_tracker else get_login_attempt_tracker(user.name) + + if not user.is_authenticated: + user_tracker and user_tracker.add_failure_attempt() + ip_tracker and ip_tracker.add_failure_attempt() + self.fail("Invalid login credentials", user=user.name) + elif not (user.name == "Administrator" or user.enabled): + user_tracker and user_tracker.add_failure_attempt() + ip_tracker and ip_tracker.add_failure_attempt() + self.fail("User disabled or missing", user=user.name) + else: + user_tracker and user_tracker.add_success_attempt() + ip_tracker and ip_tracker.add_success_attempt() + self.user = user.name + + def force_user_to_reset_password(self): + if not self.user: + return + + if self.user in xhiveframework.STANDARD_USERS: + return False + + reset_pwd_after_days = cint( + xhiveframework.db.get_single_value("System Settings", "force_user_to_reset_password") + ) + + if reset_pwd_after_days: + last_password_reset_date = ( + xhiveframework.db.get_value("User", self.user, "last_password_reset_date") or today() + ) + + last_pwd_reset_days = date_diff(today(), last_password_reset_date) + + if last_pwd_reset_days > reset_pwd_after_days: + return True + + def check_password(self, user, pwd): + """check password""" + try: + # returns user in correct case + return check_password(user, pwd) + except xhiveframework.AuthenticationError: + self.fail("Incorrect password", user=user) + + def fail(self, message, user=None): + if not user: + user = _("Unknown User") + xhiveframework.local.response["message"] = message + add_authentication_log(message, user, status="Failed") + xhiveframework.db.commit() + raise xhiveframework.AuthenticationError + + def run_trigger(self, event="on_login"): + for method in xhiveframework.get_hooks().get(event, []): + xhiveframework.call(xhiveframework.get_attr(method), login_manager=self) + + def validate_hour(self): + """check if user is logging in during restricted hours""" + login_before = int(xhiveframework.db.get_value("User", self.user, "login_before", ignore=True) or 0) + login_after = int(xhiveframework.db.get_value("User", self.user, "login_after", ignore=True) or 0) + + if not (login_before or login_after): + return + + from xhiveframework.utils import now_datetime + + current_hour = int(now_datetime().strftime("%H")) + + if login_before and current_hour >= login_before: + xhiveframework.throw(_("Login not allowed at this time"), xhiveframework.AuthenticationError) + + if login_after and current_hour < login_after: + xhiveframework.throw(_("Login not allowed at this time"), xhiveframework.AuthenticationError) + + def login_as_guest(self): + """login as guest""" + self.login_as("Guest") + + def login_as(self, user): + self.user = user + self.post_login() + + def impersonate(self, user): + current_user = xhiveframework.session.user + self.login_as(user) + # Flag this session as impersonated session, so other code can log this. + xhiveframework.local.session_obj.set_impersonsated(current_user) + + def logout(self, arg="", user=None): + if not user: + user = xhiveframework.session.user + self.run_trigger("on_logout") + + if user == xhiveframework.session.user: + delete_session(xhiveframework.session.sid, user=user, reason="User Manually Logged Out") + self.clear_cookies() + else: + clear_sessions(user) + + def clear_cookies(self): + clear_cookies() + + +class CookieManager: + def __init__(self): + self.cookies = {} + self.to_delete = [] + + def init_cookies(self): + if not xhiveframework.local.session.get("sid"): + return + + if xhiveframework.session.sid: + self.set_cookie("sid", xhiveframework.session.sid, max_age=get_expiry_in_seconds(), httponly=True) + + def set_cookie( + self, + key, + value, + expires=None, + secure=False, + httponly=False, + samesite="Lax", + max_age=None, + ): + if not secure and hasattr(xhiveframework.local, "request"): + secure = xhiveframework.local.request.scheme == "https" + + self.cookies[key] = { + "value": value, + "expires": expires, + "secure": secure, + "httponly": httponly, + "samesite": samesite, + "max_age": max_age, + } + + def delete_cookie(self, to_delete): + if not isinstance(to_delete, list | tuple): + to_delete = [to_delete] + + self.to_delete.extend(to_delete) + + def flush_cookies(self, response: Response): + for key, opts in self.cookies.items(): + response.set_cookie( + key, + quote((opts.get("value") or "").encode("utf-8")), + expires=opts.get("expires"), + secure=opts.get("secure"), + httponly=opts.get("httponly"), + samesite=opts.get("samesite"), + max_age=opts.get("max_age"), + ) + + # expires yesterday! + expires = datetime.datetime.now() + datetime.timedelta(days=-1) + for key in set(self.to_delete): + response.set_cookie(key, "", expires=expires) + + +@xhiveframework.whitelist() +def get_logged_user(): + return xhiveframework.session.user + + +def clear_cookies(): + if hasattr(xhiveframework.local, "session"): + xhiveframework.session.sid = "" + xhiveframework.local.cookie_manager.delete_cookie(["full_name", "user_id", "sid", "user_image", "system_user"]) + + +def validate_ip_address(user): + """check if IP Address is valid""" + from xhiveframework.core.doctype.user.user import get_restricted_ip_list + + # Only fetch required fields - for perf + user_fields = ["restrict_ip", "bypass_restrict_ip_check_if_2fa_enabled"] + user_info = ( + xhiveframework.get_cached_value("User", user, user_fields, as_dict=True) + if not xhiveframework.flags.in_test + else xhiveframework.db.get_value("User", user, user_fields, as_dict=True) + ) + ip_list = get_restricted_ip_list(user_info) + if not ip_list: + return + + system_settings = ( + xhiveframework.get_cached_doc("System Settings") + if not xhiveframework.flags.in_test + else xhiveframework.get_single("System Settings") + ) + # check if bypass restrict ip is enabled for all users + bypass_restrict_ip_check = system_settings.bypass_restrict_ip_check_if_2fa_enabled + + # check if two factor auth is enabled + if system_settings.enable_two_factor_auth and not bypass_restrict_ip_check: + # check if bypass restrict ip is enabled for login user + bypass_restrict_ip_check = user_info.bypass_restrict_ip_check_if_2fa_enabled + + for ip in ip_list: + if xhiveframework.local.request_ip.startswith(ip) or bypass_restrict_ip_check: + return + + xhiveframework.throw(_("Access not allowed from this IP Address"), xhiveframework.AuthenticationError) + + +def get_login_attempt_tracker(key: str, raise_locked_exception: bool = True): + """Get login attempt tracker instance. + + :param user_name: Name of the loggedin user + :param raise_locked_exception: If set, raises an exception incase of user not allowed to login + """ + sys_settings = xhiveframework.get_doc("System Settings") + track_login_attempts = sys_settings.allow_consecutive_login_attempts > 0 + tracker_kwargs = {} + + if track_login_attempts: + tracker_kwargs["lock_interval"] = sys_settings.allow_login_after_fail + tracker_kwargs["max_consecutive_login_attempts"] = sys_settings.allow_consecutive_login_attempts + + tracker = LoginAttemptTracker(key, **tracker_kwargs) + + if raise_locked_exception and track_login_attempts and not tracker.is_user_allowed(): + xhiveframework.throw( + _("Your account has been locked and will resume after {0} seconds").format( + sys_settings.allow_login_after_fail + ), + xhiveframework.SecurityException, + ) + return tracker + + +class LoginAttemptTracker: + """Track login attemts of a user. + + Lock the account for s number of seconds if there have been n consecutive unsuccessful attempts to log in. + """ + + def __init__( + self, + key: str, + max_consecutive_login_attempts: int = 3, + lock_interval: int = 5 * 60, + *, + user_name: str | None = None, + ): + """Initialize the tracker. + + :param user_name: Name of the loggedin user + :param max_consecutive_login_attempts: Maximum allowed consecutive failed login attempts + :param lock_interval: Locking interval incase of maximum failed attempts + """ + if user_name: + deprecation_warning("`username` parameter is deprecated, use `key` instead.") + self.key = key or user_name + self.lock_interval = datetime.timedelta(seconds=lock_interval) + self.max_failed_logins = max_consecutive_login_attempts + + @property + def login_failed_count(self): + return xhiveframework.cache.hget("login_failed_count", self.key) + + @login_failed_count.setter + def login_failed_count(self, count): + xhiveframework.cache.hset("login_failed_count", self.key, count) + + @login_failed_count.deleter + def login_failed_count(self): + xhiveframework.cache.hdel("login_failed_count", self.key) + + @property + def login_failed_time(self): + """First failed login attempt time within lock interval. + + For every user we track only First failed login attempt time within lock interval of time. + """ + return xhiveframework.cache.hget("login_failed_time", self.key) + + @login_failed_time.setter + def login_failed_time(self, timestamp): + xhiveframework.cache.hset("login_failed_time", self.key, timestamp) + + @login_failed_time.deleter + def login_failed_time(self): + xhiveframework.cache.hdel("login_failed_time", self.key) + + def add_failure_attempt(self): + """Log user failure attempts into the system. + + Increase the failure count if new failure is with in current lock interval time period, if not reset the login failure count. + """ + login_failed_time = self.login_failed_time + login_failed_count = self.login_failed_count # Consecutive login failure count + current_time = get_datetime() + + if not (login_failed_time and login_failed_count): + login_failed_time, login_failed_count = current_time, 0 + + if login_failed_time + self.lock_interval > current_time: + login_failed_count += 1 + else: + login_failed_time, login_failed_count = current_time, 1 + + self.login_failed_time = login_failed_time + self.login_failed_count = login_failed_count + + def add_success_attempt(self): + """Reset login failures.""" + del self.login_failed_count + del self.login_failed_time + + def is_user_allowed(self) -> bool: + """Is user allowed to login + + User is not allowed to login if login failures are greater than threshold within in lock interval from first login failure. + """ + login_failed_time = self.login_failed_time + login_failed_count = self.login_failed_count or 0 + current_time = get_datetime() + + if ( + login_failed_time + and login_failed_time + self.lock_interval > current_time + and login_failed_count > self.max_failed_logins + ): + return False + return True + + +def validate_auth(): + """ + Authenticate and sets user for the request. + """ + authorization_header = xhiveframework.get_request_header("Authorization", "").split(" ") + + if len(authorization_header) == 2: + validate_oauth(authorization_header) + validate_auth_via_api_keys(authorization_header) + + validate_auth_via_hooks() + + # If login via bearer, basic or keypair didn't work then authentication failed and we + # should terminate here. + if len(authorization_header) == 2 and xhiveframework.session.user in ("", "Guest"): + raise xhiveframework.AuthenticationError + + +def validate_oauth(authorization_header): + """ + Authenticate request using OAuth and set session user + + Args: + authorization_header (list of str): The 'Authorization' header containing the prefix and token + """ + + from xhiveframework.integrations.oauth2 import get_oauth_server + from xhiveframework.oauth import get_url_delimiter + + if authorization_header[0].lower() != "bearer": + return + + form_dict = xhiveframework.local.form_dict + token = authorization_header[1] + req = xhiveframework.request + parsed_url = urlparse(req.url) + access_token = {"access_token": token} + uri = parsed_url.scheme + "://" + parsed_url.netloc + parsed_url.path + "?" + urlencode(access_token) + http_method = req.method + headers = req.headers + body = req.get_data() + if req.content_type and "multipart/form-data" in req.content_type: + body = None + + try: + required_scopes = xhiveframework.db.get_value("OAuth Bearer Token", token, "scopes").split( + get_url_delimiter() + ) + valid, oauthlib_request = get_oauth_server().verify_request( + uri, http_method, body, headers, required_scopes + ) + if valid: + xhiveframework.set_user(xhiveframework.db.get_value("OAuth Bearer Token", token, "user")) + xhiveframework.local.form_dict = form_dict + except AttributeError: + pass + + +def validate_auth_via_api_keys(authorization_header): + """ + Authenticate request using API keys and set session user + + Args: + authorization_header (list of str): The 'Authorization' header containing the prefix and token + """ + + try: + auth_type, auth_token = authorization_header + authorization_source = xhiveframework.get_request_header("XhiveFramework-Authorization-Source") + if auth_type.lower() == "basic": + api_key, api_secret = xhiveframework.safe_decode(base64.b64decode(auth_token)).split(":") + validate_api_key_secret(api_key, api_secret, authorization_source) + elif auth_type.lower() == "token": + api_key, api_secret = auth_token.split(":") + validate_api_key_secret(api_key, api_secret, authorization_source) + except binascii.Error: + xhiveframework.throw( + _("Failed to decode token, please provide a valid base64-encoded token."), + xhiveframework.InvalidAuthorizationToken, + ) + except (AttributeError, TypeError, ValueError): + pass + + +def validate_api_key_secret(api_key, api_secret, xhiveframework_authorization_source=None): + """xhiveframework_authorization_source to provide api key and secret for a doctype apart from User""" + doctype = xhiveframework_authorization_source or "User" + doc = xhiveframework.db.get_value(doctype=doctype, filters={"api_key": api_key}, fieldname=["name"]) + if not doc: + raise xhiveframework.AuthenticationError + form_dict = xhiveframework.local.form_dict + doc_secret = get_decrypted_password(doctype, doc, fieldname="api_secret") + if api_secret == doc_secret: + if doctype == "User": + user = xhiveframework.db.get_value(doctype="User", filters={"api_key": api_key}, fieldname=["name"]) + else: + user = xhiveframework.db.get_value(doctype, doc, "user") + if xhiveframework.local.login_manager.user in ("", "Guest"): + xhiveframework.set_user(user) + xhiveframework.local.form_dict = form_dict + else: + raise xhiveframework.AuthenticationError + + +def validate_auth_via_hooks(): + for auth_hook in xhiveframework.get_hooks("auth_hooks", []): + xhiveframework.get_attr(auth_hook)() diff --git a/xhiveframework/automation/__init__.py b/xhiveframework/automation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/__init__.py b/xhiveframework/automation/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/assignment_rule/__init__.py b/xhiveframework/automation/doctype/assignment_rule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/assignment_rule/assignment_rule.js b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.js new file mode 100644 index 0000000..ae83efc --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.js @@ -0,0 +1,77 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Assignment Rule", { + refresh: function (frm) { + frm.trigger("setup_assignment_days_buttons"); + frm.trigger("set_options"); + // refresh description + frm.events.rule(frm); + }, + + setup: function (frm) { + frm.set_query("document_type", () => { + return { + filters: { + name: ["!=", "ToDo"], + }, + }; + }); + }, + + document_type: function (frm) { + frm.trigger("set_options"); + }, + + setup_assignment_days_buttons: function (frm) { + const labels = ["Weekends", "Weekdays", "All Days"]; + let get_days = (label) => { + const weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]; + const weekends = ["Saturday", "Sunday"]; + return { + "All Days": weekdays.concat(weekends), + Weekdays: weekdays, + Weekends: weekends, + }[label]; + }; + + let set_days = (e) => { + frm.clear_table("assignment_days"); + const label = $(e.currentTarget).text(); + get_days(label).forEach((day) => frm.add_child("assignment_days", { day: day })); + frm.refresh_field("assignment_days"); + }; + + labels.forEach((label) => + frm.fields_dict["assignment_days"].grid.add_custom_button(label, set_days, "top") + ); + }, + + rule: function (frm) { + const description_map = { + "Round Robin": __("Assign one by one, in sequence"), + "Load Balancing": __("Assign to the one who has the least assignments"), + "Based on Field": __("Assign to the user set in this field"), + }; + frm.get_field("rule").set_description(description_map[frm.doc.rule]); + }, + + set_options(frm) { + const doctype = frm.doc.document_type; + frm.set_fields_as_options( + "field", + doctype, + (df) => + ["Dynamic Link", "Data"].includes(df.fieldtype) || + (df.fieldtype == "Link" && df.options == "User"), + [{ label: "Owner", value: "owner" }] + ); + if (doctype) { + frm.set_fields_as_options("due_date_based_on", doctype, (df) => + ["Date", "Datetime"].includes(df.fieldtype) + ).then((options) => + frm.set_df_property("due_date_based_on", "hidden", !options.length) + ); + } + }, +}); diff --git a/xhiveframework/automation/doctype/assignment_rule/assignment_rule.json b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.json new file mode 100644 index 0000000..541d176 --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.json @@ -0,0 +1,179 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2019-02-28 17:12:18.815830", + "description": "Automatically Assign Documents to Users", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "due_date_based_on", + "priority", + "disabled", + "column_break_4", + "description", + "assignment_rules_section", + "assign_condition", + "column_break_6", + "unassign_condition", + "section_break_10", + "close_condition", + "sb", + "assignment_days", + "assign_to_users_section", + "rule", + "field", + "users", + "last_user" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "description": "Higher priority rule will be applied first", + "fieldname": "priority", + "fieldtype": "Int", + "label": "Priority" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "Automatic Assignment", + "description": "Example: {{ subject }}", + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "reqd": 1 + }, + { + "fieldname": "assignment_rules_section", + "fieldtype": "Section Break", + "label": "Assignment Rules" + }, + { + "description": "Simple Python Expression, Example: status == 'Open' and type == 'Bug'", + "fieldname": "assign_condition", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Assign Condition", + "options": "PythonExpression", + "reqd": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "description": "Simple Python Expression, Example: Status in (\"Closed\", \"Cancelled\")", + "fieldname": "unassign_condition", + "fieldtype": "Code", + "label": "Unassign Condition", + "options": "PythonExpression" + }, + { + "fieldname": "assign_to_users_section", + "fieldtype": "Section Break", + "label": "Assign To Users" + }, + { + "fieldname": "rule", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Rule", + "options": "Round Robin\nLoad Balancing\nBased on Field", + "reqd": 1 + }, + { + "depends_on": "eval: doc.rule !== 'Based on Field'", + "fieldname": "users", + "fieldtype": "Table MultiSelect", + "label": "Users", + "mandatory_depends_on": "eval: doc.rule !== 'Based on Field'", + "options": "Assignment Rule User" + }, + { + "fieldname": "last_user", + "fieldtype": "Link", + "label": "Last User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "description": "Simple Python Expression, Example: Status in (\"Invalid\")", + "fieldname": "close_condition", + "fieldtype": "Code", + "label": "Close Condition", + "options": "PythonExpression" + }, + { + "fieldname": "sb", + "fieldtype": "Section Break", + "label": "Assignment Days" + }, + { + "fieldname": "assignment_days", + "fieldtype": "Table", + "label": "Assignment Days", + "options": "Assignment Rule Day", + "reqd": 1 + }, + { + "depends_on": "document_type", + "description": "Value from this field will be set as the due date in the ToDo", + "fieldname": "due_date_based_on", + "fieldtype": "Select", + "label": "Due Date Based On" + }, + { + "depends_on": "eval: doc.rule == 'Based on Field'", + "fieldname": "field", + "fieldtype": "Select", + "label": "Field", + "mandatory_depends_on": "eval: doc.rule == 'Based on Field'" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-07-16 22:51:35.505575", + "modified_by": "Administrator", + "module": "Automation", + "name": "Assignment Rule", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/assignment_rule/assignment_rule.py b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.py new file mode 100644 index 0000000..e9fa91b --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule/assignment_rule.py @@ -0,0 +1,399 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from collections.abc import Iterable + +import xhiveframework +from xhiveframework import _ +from xhiveframework.cache_manager import clear_doctype_map, get_doctype_map +from xhiveframework.desk.form import assign_to +from xhiveframework.model import log_types +from xhiveframework.model.document import Document +from xhiveframework.utils.data import comma_and + + +class AssignmentRule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.automation.doctype.assignment_rule_day.assignment_rule_day import AssignmentRuleDay + from xhiveframework.automation.doctype.assignment_rule_user.assignment_rule_user import ( + AssignmentRuleUser, + ) + from xhiveframework.types import DF + + assign_condition: DF.Code + assignment_days: DF.Table[AssignmentRuleDay] + close_condition: DF.Code | None + description: DF.SmallText + disabled: DF.Check + document_type: DF.Link + due_date_based_on: DF.Literal[None] + field: DF.Literal[None] + last_user: DF.Link | None + priority: DF.Int + rule: DF.Literal["Round Robin", "Load Balancing", "Based on Field"] + unassign_condition: DF.Code | None + users: DF.TableMultiSelect[AssignmentRuleUser] + + # end: auto-generated types + def validate(self): + self.validate_document_types() + self.validate_assignment_days() + + def clear_cache(self): + super().clear_cache() + clear_doctype_map(self.doctype, self.document_type) + clear_doctype_map(self.doctype, f"due_date_rules_for_{self.document_type}") + + def validate_document_types(self): + if self.document_type == "ToDo": + xhiveframework.throw(_("Assignment Rule is not allowed on {0} document type").format(xhiveframework.bold("ToDo"))) + + def validate_assignment_days(self): + assignment_days = self.get_assignment_days() + if len(set(assignment_days)) != len(assignment_days): + xhiveframework.throw( + _("The following Assignment Days have been repeated: {0}").format( + comma_and([_(day) for day in get_repeated(assignment_days)], add_quotes=False) + ) + ) + + def apply_unassign(self, doc, assignments): + if self.unassign_condition and self.name in [d.assignment_rule for d in assignments]: + return self.clear_assignment(doc) + + return False + + def apply_assign(self, doc): + if self.safe_eval("assign_condition", doc): + return self.do_assignment(doc) + + def do_assignment(self, doc): + # clear existing assignment, to reassign + assign_to.clear(doc.get("doctype"), doc.get("name"), ignore_permissions=True) + + user = self.get_user(doc) + + if user: + assign_to.add( + dict( + assign_to=[user], + doctype=doc.get("doctype"), + name=doc.get("name"), + description=xhiveframework.render_template(self.description, doc), + assignment_rule=self.name, + notify=True, + date=doc.get(self.due_date_based_on) if self.due_date_based_on else None, + ), + ignore_permissions=True, + ) + + # set for reference in round robin + self.db_set("last_user", user) + return True + + return False + + def clear_assignment(self, doc): + """Clear assignments""" + if self.safe_eval("unassign_condition", doc): + return assign_to.clear(doc.get("doctype"), doc.get("name"), ignore_permissions=True) + + def close_assignments(self, doc): + """Close assignments""" + if self.safe_eval("close_condition", doc): + return assign_to.close_all_assignments( + doc.get("doctype"), doc.get("name"), ignore_permissions=True + ) + + def get_user(self, doc): + """ + Get the next user for assignment + """ + if self.rule == "Round Robin": + return self.get_user_round_robin() + elif self.rule == "Load Balancing": + return self.get_user_load_balancing() + elif self.rule == "Based on Field": + return self.get_user_based_on_field(doc) + + def get_user_round_robin(self): + """ + Get next user based on round robin + """ + + # first time, or last in list, pick the first + if not self.last_user or self.last_user == self.users[-1].user: + return self.users[0].user + + # find out the next user in the list + for i, d in enumerate(self.users): + if self.last_user == d.user: + return self.users[i + 1].user + + # bad last user, assign to the first one + return self.users[0].user + + def get_user_load_balancing(self): + """Assign to the user with least number of open assignments""" + counts = [ + dict( + user=d.user, + count=xhiveframework.db.count( + "ToDo", + dict( + reference_type=self.document_type, + allocated_to=d.user, + status="Open", + ), + ), + ) + for d in self.users + ] + # sort by dict value + sorted_counts = sorted(counts, key=lambda k: k["count"]) + + # pick the first user + return sorted_counts[0].get("user") + + def get_user_based_on_field(self, doc): + val = doc.get(self.field) + if xhiveframework.db.exists("User", val): + return val + + def safe_eval(self, fieldname, doc): + try: + if self.get(fieldname): + return xhiveframework.safe_eval(self.get(fieldname), None, doc) + except Exception as e: + # when assignment fails, don't block the document as it may be + # a part of the email pulling + xhiveframework.msgprint(xhiveframework._("Auto assignment failed: {0}").format(str(e)), indicator="orange") + + return False + + def get_assignment_days(self): + return [d.day for d in self.get("assignment_days", [])] + + def is_rule_not_applicable_today(self): + today = xhiveframework.flags.assignment_day or xhiveframework.utils.get_weekday() + assignment_days = self.get_assignment_days() + return assignment_days and today not in assignment_days + + +def get_assignments(doc) -> list[dict]: + return xhiveframework.get_all( + "ToDo", + fields=["name", "assignment_rule"], + filters=dict( + reference_type=doc.get("doctype"), reference_name=doc.get("name"), status=("!=", "Cancelled") + ), + limit=5, + ) + + +@xhiveframework.whitelist() +def bulk_apply(doctype, docnames): + docnames = xhiveframework.parse_json(docnames) + background = len(docnames) > 5 + + for name in docnames: + if background: + xhiveframework.enqueue( + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.apply", + doc=None, + doctype=doctype, + name=name, + ) + else: + apply(doctype=doctype, name=name) + + +def reopen_closed_assignment(doc): + todo_list = xhiveframework.get_all( + "ToDo", + filters={ + "reference_type": doc.doctype, + "reference_name": doc.name, + "status": "Closed", + }, + pluck="name", + ) + + for todo in todo_list: + todo_doc = xhiveframework.get_doc("ToDo", todo) + todo_doc.status = "Open" + todo_doc.save(ignore_permissions=True) + + return bool(todo_list) + + +def apply(doc=None, method=None, doctype=None, name=None): + doctype = doctype or doc.doctype + + skip_assignment_rules = ( + xhiveframework.flags.in_patch + or xhiveframework.flags.in_install + or xhiveframework.flags.in_setup_wizard + or doctype in log_types + ) + + if skip_assignment_rules: + return + + if not doc and doctype and name: + doc = xhiveframework.get_doc(doctype, name) + + assignment_rules = get_doctype_map( + "Assignment Rule", + doc.doctype, + filters={"document_type": doc.doctype, "disabled": 0}, + order_by="priority desc", + ) + + # multiple auto assigns + assignment_rule_docs: list[AssignmentRule] = [ + xhiveframework.get_cached_doc("Assignment Rule", d.get("name")) for d in assignment_rules + ] + + if not assignment_rule_docs: + return + + doc = doc.as_dict() + assignments = get_assignments(doc) + + clear = True # are all assignments cleared + new_apply = False # are new assignments applied + + if assignments: + # first unassign + # use case, there are separate groups to be assigned for say L1 and L2, + # so when the value switches from L1 to L2, L1 team must be unassigned, then L2 can be assigned. + clear = False + for assignment_rule in assignment_rule_docs: + if assignment_rule.is_rule_not_applicable_today(): + continue + + clear = assignment_rule.apply_unassign(doc, assignments) + if clear: + break + + # apply rule only if there are no existing assignments + if clear: + for assignment_rule in assignment_rule_docs: + if assignment_rule.is_rule_not_applicable_today(): + continue + + new_apply = assignment_rule.apply_assign(doc) + if new_apply: + break + + # apply close rule only if assignments exists + assignments = get_assignments(doc) + + if assignments: + for assignment_rule in assignment_rule_docs: + if assignment_rule.is_rule_not_applicable_today(): + continue + + if not new_apply: + # only reopen if close condition is not satisfied + to_close_todos = assignment_rule.safe_eval("close_condition", doc) + + if to_close_todos: + # close todo status + todos_to_close = xhiveframework.get_all( + "ToDo", + filters={ + "reference_type": doc.doctype, + "reference_name": doc.name, + }, + pluck="name", + ) + + for todo in todos_to_close: + _todo = xhiveframework.get_doc("ToDo", todo) + _todo.status = "Closed" + _todo.save(ignore_permissions=True) + break + + else: + reopened = reopen_closed_assignment(doc) + if reopened: + break + + assignment_rule.close_assignments(doc) + + +def update_due_date(doc, state=None): + """Run on_update on every Document (via hooks.py)""" + skip_document_update = ( + xhiveframework.flags.in_migrate + or xhiveframework.flags.in_patch + or xhiveframework.flags.in_import + or xhiveframework.flags.in_setup_wizard + or xhiveframework.flags.in_install + ) + + if skip_document_update: + return + + assignment_rules = get_doctype_map( + doctype="Assignment Rule", + name=f"due_date_rules_for_{doc.doctype}", + filters={ + "due_date_based_on": ["is", "set"], + "document_type": doc.doctype, + "disabled": 0, + }, + ) + + for rule in assignment_rules: + rule_doc = xhiveframework.get_cached_doc("Assignment Rule", rule.get("name")) + due_date_field = rule_doc.due_date_based_on + field_updated = ( + doc.meta.has_field(due_date_field) and doc.has_value_changed(due_date_field) and rule.get("name") + ) + + if field_updated: + assignment_todos = xhiveframework.get_all( + "ToDo", + filters={ + "assignment_rule": rule.get("name"), + "reference_type": doc.doctype, + "reference_name": doc.name, + "status": "Open", + }, + pluck="name", + ) + + for todo in assignment_todos: + todo_doc = xhiveframework.get_doc("ToDo", todo) + todo_doc.date = doc.get(due_date_field) + todo_doc.flags.updater_reference = { + "doctype": "Assignment Rule", + "docname": rule.get("name"), + "label": _("via Assignment Rule"), + } + todo_doc.save(ignore_permissions=True) + + +def get_assignment_rules() -> list[str]: + return xhiveframework.get_all("Assignment Rule", filters={"disabled": 0}, pluck="document_type") + + +def get_repeated(values: Iterable) -> list: + unique = set() + repeated = set() + + for value in values: + if value in unique: + repeated.add(value) + else: + unique.add(value) + + return [str(x) for x in repeated] diff --git a/xhiveframework/automation/doctype/assignment_rule/test_assignment_rule.py b/xhiveframework/automation/doctype/assignment_rule/test_assignment_rule.py new file mode 100644 index 0000000..23bdd77 --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule/test_assignment_rule.py @@ -0,0 +1,452 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.test_runner import make_test_records +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +TEST_DOCTYPE = "Assignment Test" + + +class TestAutoAssign(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + xhiveframework.db.delete("Assignment Rule") + create_test_doctype(TEST_DOCTYPE) + + @classmethod + def tearDownClass(cls): + xhiveframework.db.rollback() + + def setUp(self): + xhiveframework.set_user("Administrator") + make_test_records("User") + days = [ + dict(day="Sunday"), + dict(day="Monday"), + dict(day="Tuesday"), + dict(day="Wednesday"), + dict(day="Thursday"), + dict(day="Friday"), + dict(day="Saturday"), + ] + self.days = days + self.assignment_rule = get_assignment_rule([days, days]) + clear_assignments() + + def test_round_robin(self): + # check if auto assigned to first user + record = _make_test_record(public=1) + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"), + "allocated_to", + ), + "test@example.com", + ) + + # check if auto assigned to second user + record = _make_test_record(public=1) + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"), + "allocated_to", + ), + "test1@example.com", + ) + + clear_assignments() + + # check if auto assigned to third user, even if + # previous assignments where closed + record = _make_test_record(public=1) + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"), + "allocated_to", + ), + "test2@example.com", + ) + + # check loop back to first user + record = _make_test_record(public=1) + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"), + "allocated_to", + ), + "test@example.com", + ) + + def test_load_balancing(self): + self.assignment_rule.rule = "Load Balancing" + self.assignment_rule.save() + + for _ in range(30): + _make_test_record(public=1) + + # check if each user has 10 assignments (?) + for user in ("test@example.com", "test1@example.com", "test2@example.com"): + self.assertEqual( + len(xhiveframework.get_all("ToDo", dict(allocated_to=user, reference_type=TEST_DOCTYPE))), 10 + ) + + # clear 5 assignments for first user + # can't do a limit in "delete" since postgres does not support it + for d in xhiveframework.get_all( + "ToDo", dict(reference_type=TEST_DOCTYPE, allocated_to="test@example.com"), limit=5 + ): + xhiveframework.db.delete("ToDo", {"name": d.name}) + + # add 5 more assignments + for _ in range(5): + _make_test_record(public=1) + + # check if each user still has 10 assignments + for user in ("test@example.com", "test1@example.com", "test2@example.com"): + self.assertEqual( + len(xhiveframework.get_all("ToDo", dict(allocated_to=user, reference_type=TEST_DOCTYPE))), 10 + ) + + def test_assingment_on_guest_submissions(self): + """Sometimes documents are inserted as guest, check if assignment rules run on them. Use case: Web Forms""" + with self.set_user("Guest"): + doc = _make_test_record(ignore_permissions=True, public=1) + + # check assignment to *anyone* + self.assertTrue( + xhiveframework.db.get_value( + "ToDo", + {"reference_type": TEST_DOCTYPE, "reference_name": doc.name, "status": "Open"}, + "allocated_to", + ), + ) + + def test_based_on_field(self): + self.assignment_rule.rule = "Based on Field" + self.assignment_rule.field = "owner" + self.assignment_rule.save() + + for test_user in ("test1@example.com", "test2@example.com"): + xhiveframework.set_user(test_user) + note = _make_test_record(public=1) + # check if auto assigned to doc owner, test1@example.com + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), + "owner", + ), + test_user, + ) + + def test_assign_condition(self): + # check condition + note = _make_test_record(public=0) + + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), + "allocated_to", + ), + None, + ) + + def test_clear_assignment(self): + note = _make_test_record(public=1) + + # check if auto assigned to first user + todo = xhiveframework.get_list( + "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), limit=1 + )[0] + + todo = xhiveframework.get_doc("ToDo", todo["name"]) + self.assertEqual(todo.allocated_to, "test@example.com") + + # test auto unassign + note.public = 0 + note.save() + + todo.load_from_db() + + # check if todo is cancelled + self.assertEqual(todo.status, "Cancelled") + + def test_close_assignment(self): + note = _make_test_record(public=1, content="valid") + + # check if auto assigned + todo = xhiveframework.get_list( + "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), limit=1 + )[0] + + todo = xhiveframework.get_doc("ToDo", todo["name"]) + self.assertEqual(todo.allocated_to, "test@example.com") + + note.content = "Closed" + note.save() + + todo.load_from_db() + + # check if todo is closed + self.assertEqual(todo.status, "Closed") + # check if closed todo retained assignment + self.assertEqual(todo.allocated_to, "test@example.com") + + def check_multiple_rules(self): + note = _make_test_record(public=1, notify_on_login=1) + + # check if auto assigned to test3 (2nd rule is applied, as it has higher priority) + self.assertEqual( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), + "allocated_to", + ), + "test@example.com", + ) + + def check_assignment_rule_scheduling(self): + xhiveframework.db.delete("Assignment Rule") + + days_1 = [dict(day="Sunday"), dict(day="Monday"), dict(day="Tuesday")] + + days_2 = [dict(day="Wednesday"), dict(day="Thursday"), dict(day="Friday"), dict(day="Saturday")] + + get_assignment_rule([days_1, days_2], ["public == 1", "public == 1"]) + + xhiveframework.flags.assignment_day = "Monday" + note = _make_test_record(public=1) + + self.assertIn( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), + "allocated_to", + ), + ["test@example.com", "test1@example.com", "test2@example.com"], + ) + + xhiveframework.flags.assignment_day = "Friday" + note = _make_test_record(public=1) + + self.assertIn( + xhiveframework.db.get_value( + "ToDo", + dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), + "allocated_to", + ), + ["test3@example.com"], + ) + + def test_assignment_rule_condition(self): + xhiveframework.db.delete("Assignment Rule") + + assignment_rule = xhiveframework.get_doc( + dict( + name="Assignment with Due Date", + doctype="Assignment Rule", + document_type=TEST_DOCTYPE, + assign_condition="public == 0", + due_date_based_on="expiry_date", + assignment_days=self.days, + users=[ + dict(user="test@example.com"), + ], + ) + ).insert() + + expiry_date = xhiveframework.utils.add_days(xhiveframework.utils.nowdate(), 2) + note1 = _make_test_record(expiry_date=expiry_date) + note2 = _make_test_record(expiry_date=expiry_date) + + note1_todo = xhiveframework.get_all( + "ToDo", filters=dict(reference_type=TEST_DOCTYPE, reference_name=note1.name, status="Open") + )[0] + + note1_todo_doc = xhiveframework.get_doc("ToDo", note1_todo.name) + self.assertEqual(xhiveframework.utils.get_date_str(note1_todo_doc.date), expiry_date) + + # due date should be updated if the reference doc's date is updated. + note1.expiry_date = xhiveframework.utils.add_days(expiry_date, 2) + note1.save() + note1_todo_doc.reload() + self.assertEqual(xhiveframework.utils.get_date_str(note1_todo_doc.date), note1.expiry_date) + + # saving one note's expiry should not update other note todo's due date + note2_todo = xhiveframework.get_all( + "ToDo", + filters=dict(reference_type=TEST_DOCTYPE, reference_name=note2.name, status="Open"), + fields=["name", "date"], + )[0] + self.assertNotEqual(xhiveframework.utils.get_date_str(note2_todo.date), note1.expiry_date) + self.assertEqual(xhiveframework.utils.get_date_str(note2_todo.date), expiry_date) + assignment_rule.delete() + xhiveframework.db.commit() # undo changes commited by DDL + + def test_submittable_assignment(self): + # create a submittable doctype + submittable_doctype = "Assignment Test Submittable" + create_test_doctype(submittable_doctype) + dt = xhiveframework.get_doc("DocType", submittable_doctype) + dt.is_submittable = 1 + dt.save() + + # create a rule for the submittable doctype + assignment_rule = xhiveframework.new_doc("Assignment Rule") + assignment_rule.name = f"For {submittable_doctype}" + assignment_rule.document_type = submittable_doctype + assignment_rule.rule = "Round Robin" + assignment_rule.extend("assignment_days", self.days) + assignment_rule.append("users", {"user": "test@example.com"}) + assignment_rule.assign_condition = "docstatus == 1" + assignment_rule.unassign_condition = "docstatus == 2" + assignment_rule.save() + + # create a submittable doc + doc = xhiveframework.new_doc(submittable_doctype) + doc.save() + doc.submit() + + # check if todo is created + todos = xhiveframework.get_all( + "ToDo", + filters={ + "reference_type": submittable_doctype, + "reference_name": doc.name, + "status": "Open", + "allocated_to": "test@example.com", + }, + ) + self.assertEqual(len(todos), 1) + + # check if todo is closed on cancel + doc.cancel() + todos = xhiveframework.get_all( + "ToDo", + filters={ + "reference_type": submittable_doctype, + "reference_name": doc.name, + "status": "Cancelled", + "allocated_to": "test@example.com", + }, + ) + self.assertEqual(len(todos), 1) + + +def clear_assignments(): + xhiveframework.db.delete("ToDo", {"reference_type": TEST_DOCTYPE}) + + +def get_assignment_rule(days, assign=None): + xhiveframework.delete_doc_if_exists("Assignment Rule", f"For {TEST_DOCTYPE} 1") + + if not assign: + assign = ["public == 1", "notify_on_login == 1"] + + assignment_rule = xhiveframework.get_doc( + dict( + name=f"For {TEST_DOCTYPE} 1", + doctype="Assignment Rule", + priority=0, + document_type=TEST_DOCTYPE, + assign_condition=assign[0], + unassign_condition="public == 0 or notify_on_login == 1", + close_condition='"Closed" in content', + rule="Round Robin", + assignment_days=days[0], + users=[ + dict(user="test@example.com"), + dict(user="test1@example.com"), + dict(user="test2@example.com"), + ], + ) + ).insert() + + xhiveframework.delete_doc_if_exists("Assignment Rule", f"For {TEST_DOCTYPE} 2") + + # 2nd rule + xhiveframework.get_doc( + dict( + name=f"For {TEST_DOCTYPE} 2", + doctype="Assignment Rule", + priority=1, + document_type=TEST_DOCTYPE, + assign_condition=assign[1], + unassign_condition="notify_on_login == 0", + rule="Round Robin", + assignment_days=days[1], + users=[dict(user="test3@example.com")], + ) + ).insert() + + return assignment_rule + + +def _make_test_record( + *, + ignore_permissions=False, + **kwargs, +): + doc = xhiveframework.new_doc(TEST_DOCTYPE) + + if kwargs: + doc.update(kwargs) + + return doc.insert(ignore_permissions=ignore_permissions) + + +def create_test_doctype(doctype: str): + """Create custom doctype.""" + xhiveframework.delete_doc("DocType", doctype) + + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": doctype, + "module": "Custom", + "custom": 1, + "fields": [ + { + "fieldname": "expiry_date", + "label": "Expiry Date", + "fieldtype": "Date", + }, + { + "fieldname": "notify_on_login", + "label": "Notify on Login", + "fieldtype": "Check", + }, + { + "fieldname": "public", + "label": "Public", + "fieldtype": "Check", + }, + { + "fieldname": "content", + "label": "Content", + "fieldtype": "Text", + }, + ], + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1, + }, + ], + } + ).insert() diff --git a/xhiveframework/automation/doctype/assignment_rule_day/__init__.py b/xhiveframework/automation/doctype/assignment_rule_day/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.json b/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.json new file mode 100644 index 0000000..2a41879 --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.json @@ -0,0 +1,28 @@ +{ + "creation": "2019-09-21 16:52:01.705351", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day" + ], + "fields": [ + { + "fieldname": "day", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day", + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday" + } + ], + "istable": 1, + "modified": "2019-09-21 16:55:09.376291", + "modified_by": "Administrator", + "module": "Automation", + "name": "Assignment Rule Day", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.py b/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.py new file mode 100644 index 0000000..00d548b --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule_day/assignment_rule_day.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class AssignmentRuleDay(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + day: DF.Literal["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/automation/doctype/assignment_rule_user/__init__.py b/xhiveframework/automation/doctype/assignment_rule_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.json b/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.json new file mode 100644 index 0000000..5a159c8 --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "allow_read": 1, + "creation": "2019-02-27 11:41:46.602400", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-29 20:12:14.456785", + "modified_by": "Administrator", + "module": "Automation", + "name": "Assignment Rule User", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.py b/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.py new file mode 100644 index 0000000..72936bc --- /dev/null +++ b/xhiveframework/automation/doctype/assignment_rule_user/assignment_rule_user.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class AssignmentRuleUser(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + user: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/automation/doctype/auto_repeat/__init__.py b/xhiveframework/automation/doctype/auto_repeat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/auto_repeat/auto_repeat.js b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.js new file mode 100644 index 0000000..a26a7ef --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.js @@ -0,0 +1,122 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt +xhiveframework.provide("xhiveframework.auto_repeat"); + +xhiveframework.ui.form.on("Auto Repeat", { + setup: function (frm) { + frm.fields_dict["reference_doctype"].get_query = function () { + return { + query: "xhiveframework.automation.doctype.auto_repeat.auto_repeat.get_auto_repeat_doctypes", + }; + }; + + frm.fields_dict["reference_document"].get_query = function () { + return { + filters: { + auto_repeat: "", + }, + }; + }; + + frm.fields_dict["print_format"].get_query = function () { + return { + filters: { + doc_type: frm.doc.reference_doctype, + }, + }; + }; + }, + + refresh: function (frm) { + // auto repeat message + if (frm.is_new()) { + let customize_form_link = `${__("Customize Form")}`; + frm.dashboard.set_headline( + __('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [ + customize_form_link, + ]) + ); + } + + // view document button + if (!frm.is_dirty()) { + let label = __("View {0}", [__(frm.doc.reference_doctype)]); + frm.add_custom_button(label, () => + xhiveframework.set_route("List", frm.doc.reference_doctype, { auto_repeat: frm.doc.name }) + ); + } + + // auto repeat schedule + xhiveframework.auto_repeat.render_schedule(frm); + + frm.trigger("toggle_submit_on_creation"); + }, + + reference_doctype: function (frm) { + frm.trigger("toggle_submit_on_creation"); + }, + + toggle_submit_on_creation: function (frm) { + // submit on creation checkbox + if (frm.doc.reference_doctype) { + xhiveframework.model.with_doctype(frm.doc.reference_doctype, () => { + let meta = xhiveframework.get_meta(frm.doc.reference_doctype); + frm.toggle_display("submit_on_creation", meta.is_submittable); + }); + } + }, + + template: function (frm) { + if (frm.doc.template) { + xhiveframework.model.with_doc("Email Template", frm.doc.template, () => { + let email_template = xhiveframework.get_doc("Email Template", frm.doc.template); + frm.set_value("subject", email_template.subject); + frm.set_value("message", email_template.response); + frm.refresh_field("subject"); + frm.refresh_field("message"); + }); + } + }, + + get_contacts: function (frm) { + frm.call("fetch_linked_contacts"); + }, + + preview_message: function (frm) { + if (frm.doc.message) { + xhiveframework.call({ + method: "xhiveframework.automation.doctype.auto_repeat.auto_repeat.generate_message_preview", + args: { + reference_dt: frm.doc.reference_doctype, + reference_doc: frm.doc.reference_document, + subject: frm.doc.subject, + message: frm.doc.message, + }, + callback: function (r) { + if (r.message) { + xhiveframework.msgprint(r.message.message, r.message.subject); + } + }, + }); + } else { + xhiveframework.msgprint(__("Please setup a message first"), __("Message not setup")); + } + }, +}); + +xhiveframework.auto_repeat.render_schedule = function (frm) { + if (!frm.is_dirty() && frm.doc.status !== "Disabled") { + frm.call("get_auto_repeat_schedule").then((r) => { + frm.dashboard.reset(); + frm.dashboard.add_section( + xhiveframework.render_template("auto_repeat_schedule", { + schedule_details: r.message || [], + }), + __("Auto Repeat Schedule") + ); + frm.dashboard.show(); + }); + } else { + frm.dashboard.hide(); + } +}; diff --git a/xhiveframework/automation/doctype/auto_repeat/auto_repeat.json b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.json new file mode 100644 index 0000000..7496534 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.json @@ -0,0 +1,262 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "format:AUT-AR-{#####}", + "creation": "2018-03-09 11:22:31.192349", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "disabled", + "section_break_3", + "reference_doctype", + "reference_document", + "submit_on_creation", + "column_break_5", + "start_date", + "end_date", + "section_break_10", + "frequency", + "repeat_on_day", + "repeat_on_last_day", + "column_break_12", + "next_schedule_date", + "section_break_16", + "repeat_on_days", + "notification", + "notify_by_email", + "recipients", + "get_contacts", + "template", + "subject", + "message", + "preview_message", + "print_format", + "status" + ], + "fields": [ + { + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "reference_document", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "no_copy": 1, + "options": "reference_doctype", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "no_copy": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Frequency", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: in_list([\"Monthly\", \"Quarterly\", \"Half-yearly\", \"Yearly\"], doc.frequency) && !doc.repeat_on_last_day\n", + "fieldname": "repeat_on_day", + "fieldtype": "Int", + "label": "Repeat on Day" + }, + { + "fieldname": "next_schedule_date", + "fieldtype": "Date", + "label": "Next Schedule Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "collapsible": 1, + "fieldname": "notification", + "fieldtype": "Section Break", + "label": "Notification" + }, + { + "default": "0", + "fieldname": "notify_by_email", + "fieldtype": "Check", + "label": "Notify by Email" + }, + { + "depends_on": "notify_by_email", + "fieldname": "recipients", + "fieldtype": "Small Text", + "label": "Recipients" + }, + { + "depends_on": "eval: doc.notify_by_email && doc.reference_doctype && doc.reference_document", + "fieldname": "get_contacts", + "fieldtype": "Button", + "label": "Get Contacts" + }, + { + "depends_on": "eval: doc.notify_by_email", + "fieldname": "template", + "fieldtype": "Link", + "label": "Template", + "options": "Email Template" + }, + { + "depends_on": "eval: doc.notify_by_email", + "description": "To add dynamic subject, use jinja tags like\n\n
    New {{ doc.doctype }} #{{ doc.name }}
    ", + "fieldname": "subject", + "fieldtype": "Data", + "label": "Subject" + }, + { + "default": "Please find attached {{ doc.doctype }} #{{ doc.name }}", + "depends_on": "eval: doc.notify_by_email", + "fieldname": "message", + "fieldtype": "Text", + "label": "Message" + }, + { + "depends_on": "eval: doc.notify_by_email && doc.reference_doctype && doc.reference_document", + "fieldname": "preview_message", + "fieldtype": "Button", + "label": "Preview Message" + }, + { + "depends_on": "notify_by_email", + "fieldname": "print_format", + "fieldtype": "Link", + "label": "Print Format", + "options": "Print Format" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Status", + "options": "\nActive\nDisabled\nCompleted", + "read_only": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "default": "0", + "depends_on": "eval:doc.frequency === 'Monthly'", + "fieldname": "repeat_on_last_day", + "fieldtype": "Check", + "label": "Repeat on Last Day of the Month" + }, + { + "depends_on": "eval:doc.frequency==='Weekly';", + "fieldname": "repeat_on_days", + "fieldtype": "Table", + "label": "Repeat on Days", + "options": "Auto Repeat Day" + }, + { + "default": "0", + "fieldname": "submit_on_creation", + "fieldtype": "Check", + "label": "Submit on Creation" + }, + { + "depends_on": "eval:doc.frequency==='Weekly';", + "fieldname": "section_break_16", + "fieldtype": "Section Break" + } + ], + "links": [], + "modified": "2021-01-12 09:24:49.719611", + "modified_by": "Administrator", + "module": "Automation", + "name": "Auto Repeat", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + } + ], + "search_fields": "reference_document", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "reference_document", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/auto_repeat/auto_repeat.py b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.py new file mode 100644 index 0000000..8c3687f --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/auto_repeat.py @@ -0,0 +1,575 @@ +# Copyright (c) 2018, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +from datetime import timedelta + +from dateutil.relativedelta import relativedelta + +import xhiveframework +from xhiveframework import _ +from xhiveframework.automation.doctype.assignment_rule.assignment_rule import get_repeated +from xhiveframework.contacts.doctype.contact.contact import ( + get_contacts_linked_from, + get_contacts_linking_to, +) +from xhiveframework.core.doctype.communication.email import make +from xhiveframework.desk.form import assign_to +from xhiveframework.model.document import Document +from xhiveframework.utils import ( + add_days, + cstr, + get_first_day, + get_last_day, + getdate, + month_diff, + split_emails, + today, +) +from xhiveframework.utils.background_jobs import get_jobs +from xhiveframework.utils.jinja import validate_template +from xhiveframework.utils.user import get_system_managers + +month_map = {"Monthly": 1, "Quarterly": 3, "Half-yearly": 6, "Yearly": 12} +week_map = { + "Monday": 0, + "Tuesday": 1, + "Wednesday": 2, + "Thursday": 3, + "Friday": 4, + "Saturday": 5, + "Sunday": 6, +} + + +class AutoRepeat(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.automation.doctype.auto_repeat_day.auto_repeat_day import AutoRepeatDay + from xhiveframework.types import DF + + disabled: DF.Check + end_date: DF.Date | None + frequency: DF.Literal["", "Daily", "Weekly", "Monthly", "Quarterly", "Half-yearly", "Yearly"] + message: DF.Text | None + next_schedule_date: DF.Date | None + notify_by_email: DF.Check + print_format: DF.Link | None + recipients: DF.SmallText | None + reference_doctype: DF.Link + reference_document: DF.DynamicLink + repeat_on_day: DF.Int + repeat_on_days: DF.Table[AutoRepeatDay] + repeat_on_last_day: DF.Check + start_date: DF.Date + status: DF.Literal["", "Active", "Disabled", "Completed"] + subject: DF.Data | None + submit_on_creation: DF.Check + template: DF.Link | None + + # end: auto-generated types + def validate(self): + self.update_status() + self.validate_reference_doctype() + self.validate_submit_on_creation() + self.validate_dates() + self.validate_email_id() + self.validate_auto_repeat_days() + self.set_dates() + self.update_auto_repeat_id() + self.unlink_if_applicable() + + validate_template(self.subject or "") + validate_template(self.message or "") + + def before_insert(self): + if not xhiveframework.flags.in_test: + start_date = getdate(self.start_date) + today_date = getdate(today()) + if start_date <= today_date: + self.start_date = today_date + + def after_save(self): + xhiveframework.get_doc(self.reference_doctype, self.reference_document).notify_update() + + def on_trash(self): + xhiveframework.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", "") + xhiveframework.get_doc(self.reference_doctype, self.reference_document).notify_update() + + def set_dates(self): + if self.disabled: + self.next_schedule_date = None + else: + self.next_schedule_date = self.get_next_schedule_date(schedule_date=self.start_date) + + def unlink_if_applicable(self): + if self.status == "Completed" or self.disabled: + xhiveframework.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", "") + + def validate_reference_doctype(self): + if xhiveframework.flags.in_test or xhiveframework.flags.in_patch: + return + if not xhiveframework.get_meta(self.reference_doctype).allow_auto_repeat: + xhiveframework.throw( + _("Enable Allow Auto Repeat for the doctype {0} in Customize Form").format( + self.reference_doctype + ) + ) + + def validate_submit_on_creation(self): + if self.submit_on_creation and not xhiveframework.get_meta(self.reference_doctype).is_submittable: + xhiveframework.throw( + _("Cannot enable {0} for a non-submittable doctype").format(xhiveframework.bold("Submit on Creation")) + ) + + def validate_dates(self): + if xhiveframework.flags.in_patch: + return + + if self.end_date: + self.validate_from_to_dates("start_date", "end_date") + + if self.end_date == self.start_date: + xhiveframework.throw( + _("{0} should not be same as {1}").format(xhiveframework.bold("End Date"), xhiveframework.bold("Start Date")) + ) + + def validate_email_id(self): + if self.notify_by_email: + if self.recipients: + email_list = split_emails(self.recipients.replace("\n", "")) + from xhiveframework.utils import validate_email_address + + for email in email_list: + if not validate_email_address(email): + xhiveframework.throw(_("{0} is an invalid email address in 'Recipients'").format(email)) + else: + xhiveframework.throw(_("'Recipients' not specified")) + + def validate_auto_repeat_days(self): + auto_repeat_days = self.get_auto_repeat_days() + if not len(set(auto_repeat_days)) == len(auto_repeat_days): + repeated_days = get_repeated(auto_repeat_days) + plural = "s" if len(repeated_days) > 1 else "" + + xhiveframework.throw( + _("Auto Repeat Day{0} {1} has been repeated.").format( + plural, xhiveframework.bold(", ".join(repeated_days)) + ) + ) + + def update_auto_repeat_id(self): + # check if document is already on auto repeat + auto_repeat = xhiveframework.db.get_value(self.reference_doctype, self.reference_document, "auto_repeat") + if auto_repeat and auto_repeat != self.name and not xhiveframework.flags.in_patch: + xhiveframework.throw( + _("The {0} is already on auto repeat {1}").format(self.reference_document, auto_repeat) + ) + else: + xhiveframework.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", self.name) + + def update_status(self): + if self.disabled: + self.status = "Disabled" + elif self.is_completed(): + self.status = "Completed" + else: + self.status = "Active" + + def is_completed(self): + return self.end_date and getdate(self.end_date) < getdate(today()) + + @xhiveframework.whitelist() + def get_auto_repeat_schedule(self): + schedule_details = [] + start_date = getdate(self.start_date) + end_date = getdate(self.end_date) + + if not self.end_date: + next_date = self.get_next_schedule_date(schedule_date=start_date) + row = { + "reference_document": self.reference_document, + "frequency": self.frequency, + "next_scheduled_date": next_date, + } + schedule_details.append(row) + + if self.end_date: + next_date = self.get_next_schedule_date(schedule_date=start_date, for_full_schedule=True) + + while getdate(next_date) < getdate(end_date): + row = { + "reference_document": self.reference_document, + "frequency": self.frequency, + "next_scheduled_date": next_date, + } + schedule_details.append(row) + next_date = self.get_next_schedule_date(schedule_date=next_date, for_full_schedule=True) + + return schedule_details + + def create_documents(self): + try: + new_doc = self.make_new_document() + if self.notify_by_email and self.recipients: + self.send_notification(new_doc) + except Exception: + error_log = self.log_error("Auto repeat failed") + + self.disable_auto_repeat() + + if self.reference_document and not xhiveframework.flags.in_test: + self.notify_error_to_user(error_log) + + def make_new_document(self): + reference_doc = xhiveframework.get_doc(self.reference_doctype, self.reference_document) + new_doc = xhiveframework.copy_doc(reference_doc, ignore_no_copy=False) + self.update_doc(new_doc, reference_doc) + new_doc.insert(ignore_permissions=True) + + if self.submit_on_creation: + new_doc.submit() + + return new_doc + + def update_doc(self, new_doc, reference_doc): + new_doc.docstatus = 0 + if new_doc.meta.get_field("set_posting_time"): + new_doc.set("set_posting_time", 1) + + if new_doc.meta.get_field("auto_repeat"): + new_doc.set("auto_repeat", self.name) + + for fieldname in [ + "naming_series", + "ignore_pricing_rule", + "posting_time", + "select_print_heading", + "user_remark", + "remarks", + "owner", + ]: + if new_doc.meta.get_field(fieldname): + new_doc.set(fieldname, reference_doc.get(fieldname)) + + for data in new_doc.meta.fields: + if data.fieldtype == "Date" and data.reqd: + new_doc.set(data.fieldname, self.next_schedule_date) + + self.set_auto_repeat_period(new_doc) + + auto_repeat_doc = xhiveframework.get_doc("Auto Repeat", self.name) + + # for any action that needs to take place after the recurring document creation + # on recurring method of that doctype is triggered + new_doc.run_method("on_recurring", reference_doc=reference_doc, auto_repeat_doc=auto_repeat_doc) + + def set_auto_repeat_period(self, new_doc): + mcount = month_map.get(self.frequency) + if mcount and new_doc.meta.get_field("from_date") and new_doc.meta.get_field("to_date"): + last_ref_doc = xhiveframework.get_all( + doctype=self.reference_doctype, + fields=["name", "from_date", "to_date"], + filters=[ + ["auto_repeat", "=", self.name], + ["docstatus", "<", 2], + ], + order_by="creation desc", + limit=1, + ) + + if not last_ref_doc: + return + + from_date = get_next_date(last_ref_doc[0].from_date, mcount) + + if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and ( + cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date) + ): + to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount)) + else: + to_date = get_next_date(last_ref_doc[0].to_date, mcount) + + new_doc.set("from_date", from_date) + new_doc.set("to_date", to_date) + + def get_next_schedule_date(self, schedule_date, for_full_schedule=False): + """ + Returns the next schedule date for auto repeat after a recurring document has been created. + Adds required offset to the schedule_date param and returns the next schedule date. + + :param schedule_date: The date when the last recurring document was created. + :param for_full_schedule: If True, returns the immediate next schedule date, else the full schedule. + """ + if month_map.get(self.frequency): + month_count = month_map.get(self.frequency) + month_diff(schedule_date, self.start_date) - 1 + else: + month_count = 0 + + day_count = 0 + if month_count and self.repeat_on_last_day: + day_count = 31 + next_date = get_next_date(self.start_date, month_count, day_count) + elif month_count and self.repeat_on_day: + day_count = self.repeat_on_day + next_date = get_next_date(self.start_date, month_count, day_count) + elif month_count: + next_date = get_next_date(self.start_date, month_count) + else: + days = self.get_days(schedule_date) + next_date = add_days(schedule_date, days) + + # next schedule date should be after or on current date + if not for_full_schedule: + while getdate(next_date) < getdate(today()): + if month_count: + month_count += month_map.get(self.frequency, 0) + next_date = get_next_date(self.start_date, month_count, day_count) + else: + days = self.get_days(next_date) + next_date = add_days(next_date, days) + + return next_date + + def get_days(self, schedule_date): + if self.frequency == "Weekly": + days = self.get_offset_for_weekly_frequency(schedule_date) + else: + # daily frequency + days = 1 + + return days + + def get_offset_for_weekly_frequency(self, schedule_date): + # if weekdays are not set, offset is 7 from current schedule date + if not self.repeat_on_days: + return 7 + + repeat_on_days = self.get_auto_repeat_days() + current_schedule_day = getdate(schedule_date).weekday() + weekdays = list(week_map.keys()) + + # if repeats on more than 1 day or + # start date's weekday is not in repeat days, then get next weekday + # else offset is 7 + if len(repeat_on_days) > 1 or weekdays[current_schedule_day] not in repeat_on_days: + weekday = get_next_weekday(current_schedule_day, repeat_on_days) + next_weekday_number = week_map.get(weekday, 0) + # offset for upcoming weekday + return timedelta((7 + next_weekday_number - current_schedule_day) % 7).days + return 7 + + def get_auto_repeat_days(self): + return [d.day for d in self.get("repeat_on_days", [])] + + def send_notification(self, new_doc): + """Notify concerned people about recurring document generation""" + subject = self.subject or "" + message = self.message or "" + + if not self.subject: + subject = _("New {0}: {1}").format(new_doc.doctype, new_doc.name) + elif "{" in self.subject: + subject = xhiveframework.render_template(self.subject, {"doc": new_doc}) + + print_format = self.print_format or "Standard" + error_string = None + + try: + attachments = [ + xhiveframework.attach_print( + new_doc.doctype, new_doc.name, file_name=new_doc.name, print_format=print_format + ) + ] + + except xhiveframework.PermissionError: + error_string = _("A recurring {0} {1} has been created for you via Auto Repeat {2}.").format( + new_doc.doctype, new_doc.name, self.name + ) + error_string += "

    " + + error_string += _( + "{0}: Failed to attach new recurring document. To enable attaching document in the auto repeat notification email, enable {1} in Print Settings" + ).format(xhiveframework.bold(_("Note")), xhiveframework.bold(_("Allow Print for Draft"))) + attachments = None + + if error_string: + message = error_string + elif not self.message: + message = _("Please find attached {0}: {1}").format(new_doc.doctype, new_doc.name) + elif "{" in self.message: + message = xhiveframework.render_template(self.message, {"doc": new_doc}) + + make( + doctype=new_doc.doctype, + name=new_doc.name, + recipients=self.recipients, + subject=subject, + content=message, + attachments=attachments, + send_email=1, + ) + + @xhiveframework.whitelist() + def fetch_linked_contacts(self): + if self.reference_doctype and self.reference_document: + res = get_contacts_linking_to( + self.reference_doctype, self.reference_document, fields=["email_id"] + ) + res += get_contacts_linked_from( + self.reference_doctype, self.reference_document, fields=["email_id"] + ) + email_ids = {d.email_id for d in res} + if not email_ids: + xhiveframework.msgprint(_("No contacts linked to document"), alert=True) + else: + self.recipients = ", ".join(email_ids) + + def disable_auto_repeat(self): + xhiveframework.db.set_value("Auto Repeat", self.name, "disabled", 1) + + def notify_error_to_user(self, error_log): + recipients = list(get_system_managers(only_name=True)) + recipients.append(self.owner) + subject = _("Auto Repeat Document Creation Failed") + + form_link = xhiveframework.utils.get_link_to_form(self.reference_doctype, self.reference_document) + auto_repeat_failed_for = _("Auto Repeat failed for {0}").format(form_link) + + error_log_link = xhiveframework.utils.get_link_to_form("Error Log", error_log.name) + error_log_message = _("Check the Error Log for more information: {0}").format(error_log_link) + + xhiveframework.sendmail( + recipients=recipients, + subject=subject, + template="auto_repeat_fail", + args={"auto_repeat_failed_for": auto_repeat_failed_for, "error_log_message": error_log_message}, + header=[subject, "red"], + ) + + +def get_next_date(dt, mcount, day=None): + dt = getdate(dt) + dt += relativedelta(months=mcount, day=day) + return dt + + +def get_next_weekday(current_schedule_day, weekdays): + days = list(week_map.keys()) + if current_schedule_day > 0: + days = days[(current_schedule_day + 1) :] + days[:current_schedule_day] + else: + days = days[(current_schedule_day + 1) :] + + for entry in days: + if entry in weekdays: + return entry + + +# called through hooks +def make_auto_repeat_entry(): + enqueued_method = "xhiveframework.automation.doctype.auto_repeat.auto_repeat.create_repeated_entries" + jobs = get_jobs() + + if not jobs or enqueued_method not in jobs[xhiveframework.local.site]: + date = getdate(today()) + data = get_auto_repeat_entries(date) + xhiveframework.enqueue(enqueued_method, data=data) + + +def create_repeated_entries(data): + for d in data: + doc = xhiveframework.get_doc("Auto Repeat", d.name) + + current_date = getdate(today()) + schedule_date = getdate(doc.next_schedule_date) + + if schedule_date == current_date and not doc.disabled: + doc.create_documents() + schedule_date = doc.get_next_schedule_date(schedule_date=schedule_date) + if schedule_date and not doc.disabled: + xhiveframework.db.set_value("Auto Repeat", doc.name, "next_schedule_date", schedule_date) + + +def get_auto_repeat_entries(date=None): + if not date: + date = getdate(today()) + return xhiveframework.get_all( + "Auto Repeat", filters=[["next_schedule_date", "<=", date], ["status", "=", "Active"]] + ) + + +# called through hooks +def set_auto_repeat_as_completed(): + auto_repeat = xhiveframework.get_all("Auto Repeat", filters={"status": ["!=", "Disabled"]}) + for entry in auto_repeat: + doc = xhiveframework.get_doc("Auto Repeat", entry.name) + if doc.is_completed(): + doc.status = "Completed" + doc.save() + + +@xhiveframework.whitelist() +def make_auto_repeat(doctype, docname, frequency="Daily", start_date=None, end_date=None): + if not start_date: + start_date = getdate(today()) + doc = xhiveframework.new_doc("Auto Repeat") + doc.reference_doctype = doctype + doc.reference_document = docname + doc.frequency = frequency + doc.start_date = start_date + if end_date: + doc.end_date = end_date + doc.save() + return doc + + +# method for reference_doctype filter +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_auto_repeat_doctypes(doctype, txt, searchfield, start, page_len, filters): + res = xhiveframework.get_all( + "Property Setter", + { + "property": "allow_auto_repeat", + "value": "1", + }, + ["doc_type"], + ) + docs = [r.doc_type for r in res] + + res = xhiveframework.get_all( + "DocType", + { + "allow_auto_repeat": 1, + }, + ["name"], + ) + docs += [r.name for r in res] + docs = set(list(docs)) + + return [[d] for d in docs] + + +@xhiveframework.whitelist() +def update_reference(docname: str, reference: str): + doc = xhiveframework.get_doc("Auto Repeat", str(docname)) + doc.check_permission("write") + doc.db_set("reference_document", str(reference)) + return "success" # backward compatbility + + +@xhiveframework.whitelist() +def generate_message_preview(reference_dt, reference_doc, message=None, subject=None): + xhiveframework.has_permission("Auto Repeat", "write", throw=True) + doc = xhiveframework.get_doc(reference_dt, reference_doc) + doc.check_permission() + subject_preview = _("Please add a subject to your email") + msg_preview = xhiveframework.render_template(message, {"doc": doc}) + if subject: + subject_preview = xhiveframework.render_template(subject, {"doc": doc}) + + return {"message": msg_preview, "subject": subject_preview} diff --git a/xhiveframework/automation/doctype/auto_repeat/auto_repeat_list.js b/xhiveframework/automation/doctype/auto_repeat/auto_repeat_list.js new file mode 100644 index 0000000..dbd54e3 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/auto_repeat_list.js @@ -0,0 +1,11 @@ +xhiveframework.listview_settings["Auto Repeat"] = { + add_fields: ["next_schedule_date"], + get_indicator: function (doc) { + var colors = { + Active: "green", + Disabled: "red", + Completed: "blue", + }; + return [__(doc.status), colors[doc.status], "status,=," + doc.status]; + }, +}; diff --git a/xhiveframework/automation/doctype/auto_repeat/auto_repeat_schedule.html b/xhiveframework/automation/doctype/auto_repeat/auto_repeat_schedule.html new file mode 100644 index 0000000..1b66e53 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/auto_repeat_schedule.html @@ -0,0 +1,19 @@ + + + + + + + + + + + {% for(var i=0; i < schedule_details.length; i++) { %} + + + + + + {% } %} + +
    {{ __("Reference Document") }}{{ __("Frequency") }}{{ __("Next Scheduled Date") }}
    {{ schedule_details[i].reference_document }} {{ __(schedule_details[i].frequency) }} {{ xhiveframework.datetime.str_to_user(schedule_details[i].next_scheduled_date) }}
    diff --git a/xhiveframework/automation/doctype/auto_repeat/test_auto_repeat.py b/xhiveframework/automation/doctype/auto_repeat/test_auto_repeat.py new file mode 100644 index 0000000..4bf9bb7 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat/test_auto_repeat.py @@ -0,0 +1,283 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework.automation.doctype.auto_repeat.auto_repeat import ( + create_repeated_entries, + get_auto_repeat_entries, + week_map, +) +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_field +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import add_days, add_months, getdate, today + +if TYPE_CHECKING: + from xhiveframework.custom.doctype.custom_field.custom_field import CustomField + + +def add_custom_fields() -> "CustomField": + df = dict( + fieldname="auto_repeat", + label="Auto Repeat", + fieldtype="Link", + insert_after="sender", + options="Auto Repeat", + hidden=1, + print_hide=1, + read_only=1, + ) + return create_custom_field("ToDo", df) or xhiveframework.get_doc( + "Custom Field", dict(fieldname=df["fieldname"], dt="ToDo") + ) + + +class TestAutoRepeat(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + cls.custom_field = add_custom_fields() + cls.addClassCleanup(cls.custom_field.delete) + return super().setUpClass() + + def test_daily_auto_repeat(self): + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test recurring todo", assigned_by="Administrator") + ).insert() + + doc = make_auto_repeat(reference_document=todo.name) + self.assertEqual(doc.next_schedule_date, today()) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + xhiveframework.db.commit() + + todo = xhiveframework.get_doc(doc.reference_doctype, doc.reference_document) + self.assertEqual(todo.auto_repeat, doc.name) + + new_todo = xhiveframework.db.get_value("ToDo", {"auto_repeat": doc.name, "name": ("!=", todo.name)}, "name") + + new_todo = xhiveframework.get_doc("ToDo", new_todo) + + self.assertEqual(todo.get("description"), new_todo.get("description")) + + def test_weekly_auto_repeat(self): + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test weekly todo", assigned_by="Administrator") + ).insert() + + doc = make_auto_repeat( + reference_doctype="ToDo", + frequency="Weekly", + reference_document=todo.name, + start_date=add_days(today(), -7), + ) + + self.assertEqual(doc.next_schedule_date, today()) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + xhiveframework.db.commit() + + todo = xhiveframework.get_doc(doc.reference_doctype, doc.reference_document) + self.assertEqual(todo.auto_repeat, doc.name) + + new_todo = xhiveframework.db.get_value("ToDo", {"auto_repeat": doc.name, "name": ("!=", todo.name)}, "name") + + new_todo = xhiveframework.get_doc("ToDo", new_todo) + + self.assertEqual(todo.get("description"), new_todo.get("description")) + + def test_weekly_auto_repeat_with_weekdays(self): + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test auto repeat with weekdays", assigned_by="Administrator") + ).insert() + + weekdays = list(week_map.keys()) + current_weekday = getdate().weekday() + days = [{"day": weekdays[current_weekday]}, {"day": weekdays[(current_weekday + 2) % 7]}] + doc = make_auto_repeat( + reference_doctype="ToDo", + frequency="Weekly", + reference_document=todo.name, + start_date=add_days(today(), -7), + days=days, + ) + + self.assertEqual(doc.next_schedule_date, today()) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + xhiveframework.db.commit() + + todo = xhiveframework.get_doc(doc.reference_doctype, doc.reference_document) + self.assertEqual(todo.auto_repeat, doc.name) + + doc.reload() + self.assertEqual(doc.next_schedule_date, add_days(getdate(), 2)) + + def test_monthly_auto_repeat(self): + start_date = today() + end_date = add_months(start_date, 12) + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test recurring todo", assigned_by="Administrator") + ).insert() + + self.monthly_auto_repeat("ToDo", todo.name, start_date, end_date) + # test without end_date + todo = xhiveframework.get_doc( + dict( + doctype="ToDo", + description="test recurring todo without end_date", + assigned_by="Administrator", + ) + ).insert() + self.monthly_auto_repeat("ToDo", todo.name, start_date) + + def monthly_auto_repeat(self, doctype, docname, start_date, end_date=None): + def get_months(start, end): + diff = (12 * end.year + end.month) - (12 * start.year + start.month) + return diff + 1 + + doc = make_auto_repeat( + reference_doctype=doctype, + frequency="Monthly", + reference_document=docname, + start_date=start_date, + end_date=end_date, + ) + + doc.disable_auto_repeat() + + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + docnames = xhiveframework.get_all(doc.reference_doctype, {"auto_repeat": doc.name}) + self.assertEqual(len(docnames), 1) + + doc = xhiveframework.get_doc("Auto Repeat", doc.name) + doc.db_set("disabled", 0) + + months = get_months(getdate(start_date), getdate(today())) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + + docnames = xhiveframework.get_all(doc.reference_doctype, {"auto_repeat": doc.name}) + self.assertEqual(len(docnames), months) + + def test_email_notification(self): + todo = xhiveframework.get_doc( + dict( + doctype="ToDo", + description="Test recurring notification attachment", + assigned_by="Administrator", + ) + ).insert() + + doc = make_auto_repeat( + reference_document=todo.name, + notify=1, + recipients="test@domain.com", + subject="New ToDo", + message="A new ToDo has just been created for you", + ) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) + xhiveframework.db.commit() + + new_todo = xhiveframework.db.get_value("ToDo", {"auto_repeat": doc.name, "name": ("!=", todo.name)}, "name") + + email_queue = xhiveframework.db.exists("Email Queue", dict(reference_doctype="ToDo", reference_name=new_todo)) + self.assertTrue(email_queue) + + def test_next_schedule_date(self): + current_date = getdate(today()) + todo = xhiveframework.get_doc( + dict( + doctype="ToDo", description="test next schedule date for monthly", assigned_by="Administrator" + ) + ).insert() + doc = make_auto_repeat( + frequency="Monthly", reference_document=todo.name, start_date=add_months(today(), -2) + ) + + # next_schedule_date is set as on or after current date + # it should not be a previous month's date + self.assertTrue(doc.next_schedule_date >= current_date) + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test next schedule date for daily", assigned_by="Administrator") + ).insert() + doc = make_auto_repeat( + frequency="Daily", reference_document=todo.name, start_date=add_days(today(), -2) + ) + self.assertEqual(getdate(doc.next_schedule_date), current_date) + + def test_submit_on_creation(self): + doctype = "Test Submittable DocType" + create_submittable_doctype(doctype) + + current_date = getdate() + submittable_doc = xhiveframework.get_doc(dict(doctype=doctype, test="test submit on creation")).insert() + submittable_doc.submit() + doc = make_auto_repeat( + frequency="Daily", + reference_doctype=doctype, + reference_document=submittable_doc.name, + start_date=add_days(current_date, -1), + submit_on_creation=1, + ) + + data = get_auto_repeat_entries(current_date) + create_repeated_entries(data) + docnames = xhiveframework.get_all( + doc.reference_doctype, filters={"auto_repeat": doc.name}, fields=["docstatus"], limit=1 + ) + self.assertEqual(docnames[0].docstatus, 1) + + +def make_auto_repeat(**args): + args = xhiveframework._dict(args) + return xhiveframework.get_doc( + { + "doctype": "Auto Repeat", + "reference_doctype": args.reference_doctype or "ToDo", + "reference_document": args.reference_document or xhiveframework.db.get_value("ToDo", "name"), + "submit_on_creation": args.submit_on_creation or 0, + "frequency": args.frequency or "Daily", + "start_date": args.start_date or add_days(today(), -1), + "end_date": args.end_date or "", + "notify_by_email": args.notify or 0, + "recipients": args.recipients or "", + "subject": args.subject or "", + "message": args.message or "", + "repeat_on_days": args.days or [], + } + ).insert(ignore_permissions=True) + + +def create_submittable_doctype(doctype, submit_perms=1): + if xhiveframework.db.exists("DocType", doctype): + return + else: + doc = xhiveframework.get_doc( + { + "doctype": "DocType", + "__newname": doctype, + "module": "Custom", + "custom": 1, + "is_submittable": 1, + "fields": [{"fieldname": "test", "label": "Test", "fieldtype": "Data"}], + "permissions": [ + { + "role": "System Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 1, + "submit": submit_perms, + "cancel": submit_perms, + "amend": submit_perms, + } + ], + } + ).insert() + + doc.allow_auto_repeat = 1 + doc.save() diff --git a/xhiveframework/automation/doctype/auto_repeat_day/__init__.py b/xhiveframework/automation/doctype/auto_repeat_day/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.json b/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.json new file mode 100644 index 0000000..6f5c306 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2020-11-10 22:30:53.690228", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day" + ], + "fields": [ + { + "fieldname": "day", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day", + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-11-10 22:30:53.690228", + "modified_by": "Administrator", + "module": "Automation", + "name": "Auto Repeat Day", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.py b/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.py new file mode 100644 index 0000000..72adeb4 --- /dev/null +++ b/xhiveframework/automation/doctype/auto_repeat_day/auto_repeat_day.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class AutoRepeatDay(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + day: DF.Literal["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/automation/doctype/milestone/__init__.py b/xhiveframework/automation/doctype/milestone/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/milestone/milestone.js b/xhiveframework/automation/doctype/milestone/milestone.js new file mode 100644 index 0000000..a5ad489 --- /dev/null +++ b/xhiveframework/automation/doctype/milestone/milestone.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Milestone", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/automation/doctype/milestone/milestone.json b/xhiveframework/automation/doctype/milestone/milestone.json new file mode 100644 index 0000000..db3d61b --- /dev/null +++ b/xhiveframework/automation/doctype/milestone/milestone.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "creation": "2019-04-17 09:39:15.647817", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "reference_type", + "reference_name", + "track_field", + "value", + "milestone_tracker" + ], + "fields": [ + { + "fieldname": "reference_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Document", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "track_field", + "fieldtype": "Data", + "label": "Track Field", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Value", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "milestone_tracker", + "fieldtype": "Link", + "label": "Milestone Tracker", + "options": "Milestone Tracker" + } + ], + "in_create": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Automation", + "name": "Milestone", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "reference_type", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/milestone/milestone.py b/xhiveframework/automation/doctype/milestone/milestone.py new file mode 100644 index 0000000..deb735c --- /dev/null +++ b/xhiveframework/automation/doctype/milestone/milestone.py @@ -0,0 +1,27 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class Milestone(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + milestone_tracker: DF.Link | None + reference_name: DF.Data + reference_type: DF.Link + track_field: DF.Data + value: DF.Data + # end: auto-generated types + pass + + +def on_doctype_update(): + xhiveframework.db.add_index("Milestone", ["reference_type", "reference_name"]) diff --git a/xhiveframework/automation/doctype/milestone/test_milestone.py b/xhiveframework/automation/doctype/milestone/test_milestone.py new file mode 100644 index 0000000..5e5694b --- /dev/null +++ b/xhiveframework/automation/doctype/milestone/test_milestone.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestMilestone(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/automation/doctype/milestone_tracker/__init__.py b/xhiveframework/automation/doctype/milestone_tracker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.js b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.js new file mode 100644 index 0000000..106b2d6 --- /dev/null +++ b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.js @@ -0,0 +1,31 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Milestone Tracker", { + refresh: function (frm) { + frm.trigger("update_options"); + }, + document_type: function (frm) { + frm.trigger("update_options"); + }, + update_options: function (frm) { + // update select options for `track_field` + let doctype = frm.doc.document_type; + let track_fields = []; + + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => { + // get all date and datetime fields + xhiveframework.get_meta(doctype).fields.map((df) => { + if (["Link", "Select"].includes(df.fieldtype)) { + track_fields.push({ label: df.label, value: df.fieldname }); + } + }); + frm.set_df_property("track_field", "options", track_fields); + }); + } else { + // update select options + frm.set_df_property("track_field", "options", []); + } + }, +}); diff --git a/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.json b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.json new file mode 100644 index 0000000..f0dc452 --- /dev/null +++ b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "autoname": "format:{document_type}-{track_field}", + "creation": "2019-04-17 09:36:41.774774", + "description": "Track milestones for any document", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "document_type", + "track_field", + "disabled" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type to Track", + "options": "DocType", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "track_field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Field to Track", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + } + ], + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Automation", + "name": "Milestone Tracker", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.py b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.py new file mode 100644 index 0000000..decef5d --- /dev/null +++ b/xhiveframework/automation/doctype/milestone_tracker/milestone_tracker.py @@ -0,0 +1,64 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +import xhiveframework.cache_manager +from xhiveframework.model import log_types +from xhiveframework.model.document import Document + + +class MilestoneTracker(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + disabled: DF.Check + document_type: DF.Link + track_field: DF.Literal[None] + # end: auto-generated types + + def on_update(self): + xhiveframework.cache_manager.clear_doctype_map("Milestone Tracker", self.document_type) + + def on_trash(self): + xhiveframework.cache_manager.clear_doctype_map("Milestone Tracker", self.document_type) + + def apply(self, doc): + before_save = doc.get_doc_before_save() + from_value = before_save and before_save.get(self.track_field) or None + if from_value != doc.get(self.track_field): + xhiveframework.get_doc( + dict( + doctype="Milestone", + reference_type=doc.doctype, + reference_name=doc.name, + track_field=self.track_field, + from_value=from_value, + value=doc.get(self.track_field), + milestone_tracker=self.name, + ) + ).insert(ignore_permissions=True) + + +def evaluate_milestone(doc, event): + if ( + xhiveframework.flags.in_install + or xhiveframework.flags.in_migrate + or xhiveframework.flags.in_setup_wizard + or doc.doctype in log_types + ): + return + + # track milestones related to this doctype + for d in get_milestone_trackers(doc.doctype): + xhiveframework.get_doc("Milestone Tracker", d.get("name")).apply(doc) + + +def get_milestone_trackers(doctype): + return xhiveframework.cache_manager.get_doctype_map( + "Milestone Tracker", doctype, dict(document_type=doctype, disabled=0) + ) diff --git a/xhiveframework/automation/doctype/milestone_tracker/test_milestone_tracker.py b/xhiveframework/automation/doctype/milestone_tracker/test_milestone_tracker.py new file mode 100644 index 0000000..45f779c --- /dev/null +++ b/xhiveframework/automation/doctype/milestone_tracker/test_milestone_tracker.py @@ -0,0 +1,46 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +import xhiveframework.cache_manager +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestMilestoneTracker(XhiveFrameworkTestCase): + def test_milestone(self): + xhiveframework.db.delete("Milestone Tracker") + + xhiveframework.cache.delete_key("milestone_tracker_map") + + milestone_tracker = xhiveframework.get_doc( + dict(doctype="Milestone Tracker", document_type="ToDo", track_field="status") + ).insert() + + todo = xhiveframework.get_doc(dict(doctype="ToDo", description="test milestone", status="Open")).insert() + + milestones = xhiveframework.get_all( + "Milestone", + fields=["track_field", "value", "milestone_tracker"], + filters=dict(reference_type=todo.doctype, reference_name=todo.name), + ) + + self.assertEqual(len(milestones), 1) + self.assertEqual(milestones[0].track_field, "status") + self.assertEqual(milestones[0].value, "Open") + + todo.status = "Closed" + todo.save() + + milestones = xhiveframework.get_all( + "Milestone", + fields=["track_field", "value", "milestone_tracker"], + filters=dict(reference_type=todo.doctype, reference_name=todo.name), + order_by="modified desc", + ) + + self.assertEqual(len(milestones), 2) + self.assertEqual(milestones[0].track_field, "status") + self.assertEqual(milestones[0].value, "Closed") + + # cleanup + xhiveframework.db.delete("Milestone") + milestone_tracker.delete() diff --git a/xhiveframework/automation/doctype/reminder/__init__.py b/xhiveframework/automation/doctype/reminder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/automation/doctype/reminder/reminder.js b/xhiveframework/automation/doctype/reminder/reminder.js new file mode 100644 index 0000000..ef7e92e --- /dev/null +++ b/xhiveframework/automation/doctype/reminder/reminder.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +// xhiveframework.ui.form.on("Reminder", { +// refresh(frm) { + +// }, +// }); diff --git a/xhiveframework/automation/doctype/reminder/reminder.json b/xhiveframework/automation/doctype/reminder/reminder.json new file mode 100644 index 0000000..07dbe77 --- /dev/null +++ b/xhiveframework/automation/doctype/reminder/reminder.json @@ -0,0 +1,90 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2023-02-22 11:23:58.183276", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "remind_at", + "description", + "reminder_doctype", + "reminder_docname", + "notified" + ], + "fields": [ + { + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "hidden": 1, + "label": "User", + "options": "User", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "reminder_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "reminder_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Document Name", + "options": "reminder_doctype", + "read_only": 1 + }, + { + "default": "now", + "fieldname": "remind_at", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Remind At", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "notified", + "fieldtype": "Check", + "hidden": 1, + "label": "notified" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 22:17:18.351025", + "modified_by": "Administrator", + "module": "Automation", + "name": "Reminder", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "if_owner": 1, + "read": 1, + "role": "Desk User", + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "description" +} \ No newline at end of file diff --git a/xhiveframework/automation/doctype/reminder/reminder.py b/xhiveframework/automation/doctype/reminder/reminder.py new file mode 100644 index 0000000..73168e6 --- /dev/null +++ b/xhiveframework/automation/doctype/reminder/reminder.py @@ -0,0 +1,94 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import cint +from xhiveframework.utils.data import add_to_date, get_datetime, now_datetime + + +class Reminder(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + description: DF.SmallText + notified: DF.Check + remind_at: DF.Datetime + reminder_docname: DF.DynamicLink | None + reminder_doctype: DF.Link | None + user: DF.Link + + # end: auto-generated types + @staticmethod + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Reminder") + xhiveframework.db.delete(table, filters=(table.remind_at < (Now() - Interval(days=days)))) + + def validate(self): + self.user = xhiveframework.session.user + if get_datetime(self.remind_at) < now_datetime(): + xhiveframework.throw(_("Reminder cannot be created in past.")) + + def send_reminder(self): + if self.notified: + return + + self.db_set("notified", 1, update_modified=False) + + try: + notification = xhiveframework.new_doc("Notification Log") + notification.for_user = self.user + notification.set("type", "Alert") + notification.document_type = self.reminder_doctype + notification.document_name = self.reminder_docname + notification.subject = self.description + notification.insert() + except Exception: + self.log_error("Failed to send reminder") + + +@xhiveframework.whitelist() +def create_new_reminder( + remind_at: str, + description: str, + reminder_doctype: str | None = None, + reminder_docname: str | None = None, +): + reminder = xhiveframework.new_doc("Reminder") + + reminder.description = description + reminder.remind_at = remind_at + reminder.reminder_doctype = reminder_doctype + reminder.reminder_docname = reminder_docname + + return reminder.insert() + + +def send_reminders(): + # Ensure that we send all reminders that might be before next job execution. + job_freq = cint(xhiveframework.get_conf().scheduler_interval) or 240 + upper_threshold = add_to_date(now_datetime(), seconds=job_freq, as_string=True, as_datetime=True) + + lower_threshold = add_to_date(now_datetime(), hours=-8, as_string=True, as_datetime=True) + + pending_reminders = xhiveframework.get_all( + "Reminder", + filters=[ + ("remind_at", "<=", upper_threshold), + ("remind_at", ">=", lower_threshold), # dont send too old reminders if failed to send + ("notified", "=", 0), + ], + pluck="name", + ) + + for reminder in pending_reminders: + xhiveframework.get_doc("Reminder", reminder).send_reminder() diff --git a/xhiveframework/automation/doctype/reminder/test_reminder.py b/xhiveframework/automation/doctype/reminder/test_reminder.py new file mode 100644 index 0000000..079d9ab --- /dev/null +++ b/xhiveframework/automation/doctype/reminder/test_reminder.py @@ -0,0 +1,27 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +import xhiveframework +from xhiveframework.automation.doctype.reminder.reminder import create_new_reminder, send_reminders +from xhiveframework.desk.doctype.notification_log.notification_log import get_notification_logs +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import add_to_date, now_datetime + + +class TestReminder(XhiveFrameworkTestCase): + def test_reminder(self): + description = "TEST_REMINDER" + + create_new_reminder( + remind_at=add_to_date(now_datetime(), minutes=1, as_datetime=True, as_string=True), + description=description, + ) + + send_reminders() + + notifications = get_notification_logs()["notification_logs"] + self.assertIn( + description, + [n.subject for n in notifications], + msg=f"Failed to find reminder notification \n{notifications}", + ) diff --git a/xhiveframework/automation/workspace/tools/tools.json b/xhiveframework/automation/workspace/tools/tools.json new file mode 100644 index 0000000..2c46414 --- /dev/null +++ b/xhiveframework/automation/workspace/tools/tools.json @@ -0,0 +1,360 @@ +{ + "charts": [], + "content": "[{\"id\":\"-P-RG1wVHg\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"sR-UFcO7II\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Import Data\",\"col\":3}},{\"id\":\"IkcVmgWb3z\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"ToDo\",\"col\":3}},{\"id\":\"6wir-jZFRE\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"File\",\"col\":3}},{\"id\":\"45a1jzQkTm\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Assignment Rule\",\"col\":3}},{\"id\":\"LdZrgvxxo7\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yNSSTIaDWZ\",\"type\":\"header\",\"data\":{\"text\":\"Documents\",\"col\":12}},{\"id\":\"0yceBIfhHM\",\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"id\":\"42WbBA9rpj\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"wE9n7TIrAc\",\"type\":\"card\",\"data\":{\"card_name\":\"Alerts and Notifications\",\"col\":4}},{\"id\":\"7_U7_xCOos\",\"type\":\"card\",\"data\":{\"card_name\":\"Email\",\"col\":4}},{\"id\":\"3imoh2oqsJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"id\":\"SlYKJZj5r3\",\"type\":\"card\",\"data\":{\"card_name\":\"Automation\",\"col\":4}},{\"id\":\"O7jrc2YQTN\",\"type\":\"card\",\"data\":{\"card_name\":\"Newsletter\",\"col\":4}}]", + "creation": "2020-03-02 14:53:24.980279", + "custom_blocks": [], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "tool", + "idx": 0, + "is_hidden": 0, + "label": "Tools", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Automation", + "link_count": 3, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Assignment Rule", + "link_count": 0, + "link_to": "Assignment Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Milestone", + "link_count": 0, + "link_to": "Milestone", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Auto Repeat", + "link_count": 0, + "link_to": "Auto Repeat", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Tools", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "To Do", + "link_count": 0, + "link_to": "ToDo", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Calendar", + "link_count": 0, + "link_to": "Event", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Note", + "link_count": 0, + "link_to": "Note", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Files", + "link_count": 0, + "link_to": "File", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Data", + "link_count": 5, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Import Data", + "link_count": 0, + "link_to": "Data Import", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Export Data", + "link_count": 0, + "link_to": "Data Export", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Bulk Update", + "link_count": 0, + "link_to": "Bulk Update", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Download Backups", + "link_count": 0, + "link_to": "backups", + "link_type": "Page", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Deleted Documents", + "link_count": 0, + "link_to": "Deleted Document", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email", + "link_count": 3, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Account", + "link_count": 0, + "link_to": "Email Account", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Domain", + "link_count": 0, + "link_to": "Email Domain", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Template", + "link_count": 0, + "link_to": "Email Template", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Newsletter", + "link_count": 2, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Newsletter", + "link_count": 0, + "link_to": "Newsletter", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Email Group", + "link_count": 0, + "link_to": "Email Group", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Printing", + "link_count": 4, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Print Format Builder", + "link_count": 0, + "link_to": "print-format-builder", + "link_type": "Page", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Print Format Builder (New)", + "link_count": 0, + "link_to": "print-format-builder-beta", + "link_type": "Page", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Print Settings", + "link_count": 0, + "link_to": "Print Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Print Heading", + "link_count": 0, + "link_to": "Print Heading", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Alerts and Notifications", + "link_count": 3, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Notification", + "link_count": 0, + "link_to": "Notification", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Auto Email Report", + "link_count": 0, + "link_to": "Auto Email Report", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Notification Settings", + "link_count": 0, + "link_to": "Notification Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2024-01-02 15:47:12.939478", + "modified_by": "Administrator", + "module": "Automation", + "name": "Tools", + "number_cards": [], + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 17.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Import Data", + "link_to": "Data Import", + "type": "DocType" + }, + { + "label": "ToDo", + "link_to": "ToDo", + "type": "DocType" + }, + { + "label": "File", + "link_to": "File", + "type": "DocType" + }, + { + "label": "Assignment Rule", + "link_to": "Assignment Rule", + "type": "DocType" + } + ], + "title": "Tools" +} \ No newline at end of file diff --git a/xhiveframework/boot.py b/xhiveframework/boot.py new file mode 100644 index 0000000..02c77e8 --- /dev/null +++ b/xhiveframework/boot.py @@ -0,0 +1,470 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +""" +bootstrap client session +""" + +import xhiveframework +import xhiveframework.defaults +import xhiveframework.desk.desk_page +from xhiveframework.core.doctype.navbar_settings.navbar_settings import get_app_logo, get_navbar_settings +from xhiveframework.desk.doctype.form_tour.form_tour import get_onboarding_ui_tours +from xhiveframework.desk.doctype.route_history.route_history import frequently_visited_links +from xhiveframework.desk.form.load import get_meta_bundle +from xhiveframework.email.inbox import get_email_accounts +from xhiveframework.model.base_document import get_controller +from xhiveframework.permissions import has_permission +from xhiveframework.query_builder import DocType +from xhiveframework.query_builder.functions import Count +from xhiveframework.query_builder.terms import ParameterizedValueWrapper, SubQuery +from xhiveframework.social.doctype.energy_point_log.energy_point_log import get_energy_points +from xhiveframework.social.doctype.energy_point_settings.energy_point_settings import ( + is_energy_point_enabled, +) +from xhiveframework.utils import add_user_info, cstr, get_system_timezone +from xhiveframework.utils.change_log import get_versions +from xhiveframework.website.doctype.web_page_view.web_page_view import is_tracking_enabled + + +def get_bootinfo(): + """build and return boot info""" + from xhiveframework.translate import get_lang_dict, get_translated_doctypes + + xhiveframework.set_user_lang(xhiveframework.session.user) + bootinfo = xhiveframework._dict() + hooks = xhiveframework.get_hooks() + doclist = [] + + # user + get_user(bootinfo) + + # system info + bootinfo.sitename = xhiveframework.local.site + bootinfo.sysdefaults = xhiveframework.defaults.get_defaults() + bootinfo.server_date = xhiveframework.utils.nowdate() + + if xhiveframework.session["user"] != "Guest": + bootinfo.user_info = get_user_info() + bootinfo.sid = xhiveframework.session["sid"] + + bootinfo.modules = {} + bootinfo.module_list = [] + load_desktop_data(bootinfo) + bootinfo.letter_heads = get_letter_heads() + bootinfo.active_domains = xhiveframework.get_active_domains() + bootinfo.all_domains = [d.get("name") for d in xhiveframework.get_all("Domain")] + add_layouts(bootinfo) + + bootinfo.module_app = xhiveframework.local.module_app + bootinfo.single_types = [d.name for d in xhiveframework.get_all("DocType", {"issingle": 1})] + bootinfo.nested_set_doctypes = [ + d.parent for d in xhiveframework.get_all("DocField", {"fieldname": "lft"}, ["parent"]) + ] + add_home_page(bootinfo, doclist) + bootinfo.page_info = get_allowed_pages() + load_translations(bootinfo) + add_timezone_info(bootinfo) + load_conf_settings(bootinfo) + load_print(bootinfo, doclist) + doclist.extend(get_meta_bundle("Page")) + bootinfo.home_folder = xhiveframework.db.get_value("File", {"is_home_folder": 1}) + bootinfo.navbar_settings = get_navbar_settings() + bootinfo.notification_settings = get_notification_settings() + bootinfo.onboarding_tours = get_onboarding_ui_tours() + set_time_zone(bootinfo) + + # ipinfo + if xhiveframework.session.data.get("ipinfo"): + bootinfo.ipinfo = xhiveframework.session["data"]["ipinfo"] + + # add docs + bootinfo.docs = doclist + load_country_doc(bootinfo) + load_currency_docs(bootinfo) + + for method in hooks.boot_session or []: + xhiveframework.get_attr(method)(bootinfo) + + if bootinfo.lang: + bootinfo.lang = str(bootinfo.lang) + bootinfo.versions = {k: v["version"] for k, v in get_versions().items()} + + bootinfo.error_report_email = xhiveframework.conf.error_report_email + bootinfo.calendars = sorted(xhiveframework.get_hooks("calendars")) + bootinfo.treeviews = xhiveframework.get_hooks("treeviews") or [] + bootinfo.lang_dict = get_lang_dict() + bootinfo.success_action = get_success_action() + bootinfo.update(get_email_accounts(user=xhiveframework.session.user)) + bootinfo.energy_points_enabled = is_energy_point_enabled() + bootinfo.website_tracking_enabled = is_tracking_enabled() + bootinfo.points = get_energy_points(xhiveframework.session.user) + bootinfo.frequently_visited_links = frequently_visited_links() + bootinfo.link_preview_doctypes = get_link_preview_doctypes() + bootinfo.additional_filters_config = get_additional_filters_from_hooks() + bootinfo.desk_settings = get_desk_settings() + bootinfo.app_logo_url = get_app_logo() + bootinfo.link_title_doctypes = get_link_title_doctypes() + bootinfo.translated_doctypes = get_translated_doctypes() + bootinfo.subscription_conf = add_subscription_conf() + bootinfo.marketplace_apps = get_marketplace_apps() + + return bootinfo + + +def get_letter_heads(): + letter_heads = {} + for letter_head in xhiveframework.get_all("Letter Head", fields=["name", "content", "footer"]): + letter_heads.setdefault( + letter_head.name, {"header": letter_head.content, "footer": letter_head.footer} + ) + + return letter_heads + + +def load_conf_settings(bootinfo): + from xhiveframework.core.api.file import get_max_file_size + + bootinfo.max_file_size = get_max_file_size() + for key in ("developer_mode", "socketio_port", "file_watcher_port"): + if key in xhiveframework.conf: + bootinfo[key] = xhiveframework.conf.get(key) + + +def load_desktop_data(bootinfo): + from xhiveframework.desk.desktop import get_workspace_sidebar_items + + bootinfo.allowed_workspaces = get_workspace_sidebar_items().get("pages") + bootinfo.module_wise_workspaces = get_controller("Workspace").get_module_wise_workspaces() + bootinfo.dashboards = xhiveframework.get_all("Dashboard") + + +def get_allowed_pages(cache=False): + return get_user_pages_or_reports("Page", cache=cache) + + +def get_allowed_reports(cache=False): + return get_user_pages_or_reports("Report", cache=cache) + + +def get_allowed_report_names(cache=False) -> set[str]: + return {cstr(report) for report in get_allowed_reports(cache).keys() if report} + + +def get_user_pages_or_reports(parent, cache=False): + if cache: + has_role = xhiveframework.cache.get_value("has_role:" + parent, user=xhiveframework.session.user) + if has_role: + return has_role + + roles = xhiveframework.get_roles() + has_role = {} + + page = DocType("Page") + report = DocType("Report") + + if parent == "Report": + columns = (report.name.as_("title"), report.ref_doctype, report.report_type) + else: + columns = (page.title.as_("title"),) + + customRole = DocType("Custom Role") + hasRole = DocType("Has Role") + parentTable = DocType(parent) + + # get pages or reports set on custom role + pages_with_custom_roles = ( + xhiveframework.qb.from_(customRole) + .from_(hasRole) + .from_(parentTable) + .select(customRole[parent.lower()].as_("name"), customRole.modified, customRole.ref_doctype, *columns) + .where( + (hasRole.parent == customRole.name) + & (parentTable.name == customRole[parent.lower()]) + & (customRole[parent.lower()].isnotnull()) + & (hasRole.role.isin(roles)) + ) + ).run(as_dict=True) + + for p in pages_with_custom_roles: + has_role[p.name] = {"modified": p.modified, "title": p.title, "ref_doctype": p.ref_doctype} + + subq = ( + xhiveframework.qb.from_(customRole) + .select(customRole[parent.lower()]) + .where(customRole[parent.lower()].isnotnull()) + ) + + pages_with_standard_roles = ( + xhiveframework.qb.from_(hasRole) + .from_(parentTable) + .select(parentTable.name.as_("name"), parentTable.modified, *columns) + .where( + (hasRole.role.isin(roles)) & (hasRole.parent == parentTable.name) & (parentTable.name.notin(subq)) + ) + .distinct() + ) + + if parent == "Report": + pages_with_standard_roles = pages_with_standard_roles.where(report.disabled == 0) + + pages_with_standard_roles = pages_with_standard_roles.run(as_dict=True) + + for p in pages_with_standard_roles: + if p.name not in has_role: + has_role[p.name] = {"modified": p.modified, "title": p.title} + if parent == "Report": + has_role[p.name].update({"ref_doctype": p.ref_doctype}) + + no_of_roles = SubQuery( + xhiveframework.qb.from_(hasRole).select(Count("*")).where(hasRole.parent == parentTable.name) + ) + + # pages with no role are allowed + if parent == "Page": + pages_with_no_roles = ( + xhiveframework.qb.from_(parentTable) + .select(parentTable.name, parentTable.modified, *columns) + .where(no_of_roles == 0) + ).run(as_dict=True) + + for p in pages_with_no_roles: + if p.name not in has_role: + has_role[p.name] = {"modified": p.modified, "title": p.title} + + elif parent == "Report": + if not has_permission("Report", raise_exception=False): + return {} + + reports = xhiveframework.get_list( + "Report", + fields=["name", "report_type"], + filters={"name": ("in", has_role.keys())}, + ignore_ifnull=True, + ) + for report in reports: + has_role[report.name]["report_type"] = report.report_type + + non_permitted_reports = set(has_role.keys()) - {r.name for r in reports} + for r in non_permitted_reports: + has_role.pop(r, None) + + # Expire every six hours + xhiveframework.cache.set_value("has_role:" + parent, has_role, xhiveframework.session.user, 21600) + return has_role + + +def load_translations(bootinfo): + from xhiveframework.translate import get_messages_for_boot + + bootinfo["lang"] = xhiveframework.lang + bootinfo["__messages"] = get_messages_for_boot() + + +def get_user_info(): + # get info for current user + user_info = xhiveframework._dict() + add_user_info(xhiveframework.session.user, user_info) + + if xhiveframework.session.user == "Administrator" and user_info.Administrator.email: + user_info[user_info.Administrator.email] = user_info.Administrator + + return user_info + + +def get_user(bootinfo): + """get user info""" + bootinfo.user = xhiveframework.get_user().load_user() + + +def add_home_page(bootinfo, docs): + """load home page""" + if xhiveframework.session.user == "Guest": + return + home_page = xhiveframework.db.get_default("desktop:home_page") + + if home_page == "setup-wizard": + bootinfo.setup_wizard_requires = xhiveframework.get_hooks("setup_wizard_requires") + + try: + page = xhiveframework.desk.desk_page.get(home_page) + docs.append(page) + bootinfo["home_page"] = page.name + except (xhiveframework.DoesNotExistError, xhiveframework.PermissionError): + xhiveframework.clear_last_message() + bootinfo["home_page"] = "Workspaces" + + +def add_timezone_info(bootinfo): + system = bootinfo.sysdefaults.get("time_zone") + import xhiveframework.utils.momentjs + + bootinfo.timezone_info = {"zones": {}, "rules": {}, "links": {}} + xhiveframework.utils.momentjs.update(system, bootinfo.timezone_info) + + +def load_print(bootinfo, doclist): + print_settings = xhiveframework.db.get_singles_dict("Print Settings") + print_settings.doctype = ":Print Settings" + doclist.append(print_settings) + load_print_css(bootinfo, print_settings) + + +def load_print_css(bootinfo, print_settings): + import xhiveframework.www.printview + + bootinfo.print_css = xhiveframework.www.printview.get_print_style( + print_settings.print_style or "Redesign", for_legacy=True + ) + + +def get_unseen_notes(): + note = DocType("Note") + nsb = DocType("Note Seen By").as_("nsb") + + return ( + xhiveframework.qb.from_(note) + .select(note.name, note.title, note.content, note.notify_on_every_login) + .where( + (note.notify_on_login == 1) + & (note.expire_notification_on > xhiveframework.utils.now()) + & ( + ParameterizedValueWrapper(xhiveframework.session.user).notin( + SubQuery(xhiveframework.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name)) + ) + ) + ) + ).run(as_dict=1) + + +def get_success_action(): + return xhiveframework.get_all("Success Action", fields=["*"]) + + +def get_link_preview_doctypes(): + from xhiveframework.utils import cint + + link_preview_doctypes = [d.name for d in xhiveframework.get_all("DocType", {"show_preview_popup": 1})] + customizations = xhiveframework.get_all( + "Property Setter", fields=["doc_type", "value"], filters={"property": "show_preview_popup"} + ) + + for custom in customizations: + if not cint(custom.value) and custom.doc_type in link_preview_doctypes: + link_preview_doctypes.remove(custom.doc_type) + else: + link_preview_doctypes.append(custom.doc_type) + + return link_preview_doctypes + + +def get_additional_filters_from_hooks(): + filter_config = xhiveframework._dict() + filter_hooks = xhiveframework.get_hooks("filters_config") + for hook in filter_hooks: + filter_config.update(xhiveframework.get_attr(hook)()) + + return filter_config + + +def add_layouts(bootinfo): + # add routes for readable doctypes + bootinfo.doctype_layouts = xhiveframework.get_all("DocType Layout", ["name", "route", "document_type"]) + + +def get_desk_settings(): + role_list = xhiveframework.get_all("Role", fields=["*"], filters=dict(name=["in", xhiveframework.get_roles()])) + desk_settings = {} + + from xhiveframework.core.doctype.role.role import desk_properties + + for role in role_list: + for key in desk_properties: + desk_settings[key] = desk_settings.get(key) or role.get(key) + + return desk_settings + + +def get_notification_settings(): + return xhiveframework.get_cached_doc("Notification Settings", xhiveframework.session.user) + + +def get_link_title_doctypes(): + dts = xhiveframework.get_all("DocType", {"show_title_field_in_link": 1}) + custom_dts = xhiveframework.get_all( + "Property Setter", + {"property": "show_title_field_in_link", "value": "1"}, + ["doc_type as name"], + ) + return [d.name for d in dts + custom_dts if d] + + +def set_time_zone(bootinfo): + bootinfo.time_zone = { + "system": get_system_timezone(), + "user": bootinfo.get("user_info", {}).get(xhiveframework.session.user, {}).get("time_zone", None) + or get_system_timezone(), + } + + +def load_country_doc(bootinfo): + country = xhiveframework.db.get_default("country") + if not country: + return + try: + bootinfo.docs.append(xhiveframework.get_cached_doc("Country", country)) + except Exception: + pass + + +def load_currency_docs(bootinfo): + currency = xhiveframework.qb.DocType("Currency") + + currency_docs = ( + xhiveframework.qb.from_(currency) + .select( + currency.name, + currency.fraction, + currency.fraction_units, + currency.number_format, + currency.smallest_currency_fraction_value, + currency.symbol, + currency.symbol_on_right, + ) + .where(currency.enabled == 1) + .run(as_dict=1, update={"doctype": ":Currency"}) + ) + + bootinfo.docs += currency_docs + + +def get_marketplace_apps(): + import requests + + apps = [] + cache_key = "xhiveframework_marketplace_apps" + + if xhiveframework.conf.developer_mode: + return apps + + def get_apps_from_fc(): + remote_site = xhiveframework.conf.xhiveframeworkcloud_url or "xhiveframeworkcloud.com" + request_url = f"https://{remote_site}/api/method/press.api.marketplace.get_marketplace_apps" + request = requests.get(request_url, timeout=2.0) + return request.json()["message"] + + try: + apps = xhiveframework.cache().get_value(cache_key, get_apps_from_fc, shared=True) + installed_apps = set(xhiveframework.get_installed_apps()) + apps = [app for app in apps if app["name"] not in installed_apps] + except Exception: + # Don't retry for a day + xhiveframework.cache().set_value(cache_key, apps, shared=True, expires_in_sec=24 * 60 * 60) + + return apps + + +def add_subscription_conf(): + try: + return xhiveframework.conf.subscription + except Exception: + return "" diff --git a/xhiveframework/build.py b/xhiveframework/build.py new file mode 100644 index 0000000..6b6a77c --- /dev/null +++ b/xhiveframework/build.py @@ -0,0 +1,419 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import os +import re +import shutil +import subprocess +from subprocess import getoutput +from tempfile import mkdtemp +from urllib.parse import urlparse + +import click +from semantic_version import Version + +import xhiveframework + +timestamps = {} +app_paths = None +sites_path = os.path.abspath(os.getcwd()) +WHITESPACE_PATTERN = re.compile(r"\s+") +HTML_COMMENT_PATTERN = re.compile(r"()") + + +class AssetsNotDownloadedError(Exception): + pass + + +class AssetsDontExistError(Exception): + pass + + +def download_file(url, prefix): + from requests import get + + filename = urlparse(url).path.split("/")[-1] + local_filename = os.path.join(prefix, filename) + with get(url, stream=True, allow_redirects=True) as r: + r.raise_for_status() + with open(local_filename, "wb") as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + return local_filename + + +def build_missing_files(): + """Check which files dont exist yet from the assets.json and run build for those files""" + + missing_assets = [] + current_asset_files = [] + + for type in ["css", "js"]: + folder = os.path.join(sites_path, "assets", "xhiveframework", "dist", type) + current_asset_files.extend(os.listdir(folder)) + + development = xhiveframework.local.conf.developer_mode or xhiveframework.local.dev_server + build_mode = "development" if development else "production" + + assets_json = xhiveframework.read_file("assets/assets.json") + if assets_json: + assets_json = xhiveframework.parse_json(assets_json) + + for bundle_file, output_file in assets_json.items(): + if not output_file.startswith("/assets/xhiveframework"): + continue + + if os.path.basename(output_file) not in current_asset_files: + missing_assets.append(bundle_file) + + if missing_assets: + click.secho("\nBuilding missing assets...\n", fg="yellow") + files_to_build = ["xhiveframework/" + name for name in missing_assets] + bundle(build_mode, files=files_to_build) + else: + # no assets.json, run full build + bundle(build_mode, apps="xhiveframework") + + +def get_assets_link(xhiveframework_head) -> str: + import requests + + tag = getoutput( + r"cd ../apps/xhiveframework && git show-ref --tags -d | grep %s | sed -e 's,.*" + r" refs/tags/,,' -e 's/\^{}//'" % xhiveframework_head + ) + + if tag: + # if tag exists, download assets from github release + url = f"https://lab.membtech.com/xhiveframework/xhiveframework15/releases/download/{tag}/assets.tar.gz" + else: + url = f"http://assets.xhiveframework.com/{xhiveframework_head}.tar.gz" + + if not requests.head(url): + reference = f"Release {tag}" if tag else f"Commit {xhiveframework_head}" + raise AssetsDontExistError(f"Assets for {reference} don't exist") + + return url + + +def fetch_assets(url, xhiveframework_head): + click.secho("Retrieving assets...", fg="yellow") + + prefix = mkdtemp(prefix="xhiveframework-assets-", suffix=xhiveframework_head) + assets_archive = download_file(url, prefix) + + if not assets_archive: + raise AssetsNotDownloadedError(f"Assets could not be retrived from {url}") + + click.echo(click.style("✔", fg="green") + f" Downloaded XhiveFramework assets from {url}") + + return assets_archive + + +def setup_assets(assets_archive): + import tarfile + + directories_created = set() + + click.secho("\nExtracting assets...\n", fg="yellow") + with tarfile.open(assets_archive) as tar: + for file in tar: + if not file.isdir(): + dest = "." + file.name.replace("./xhiveframework-bench/sites", "") + asset_directory = os.path.dirname(dest) + show = dest.replace("./assets/", "") + + if asset_directory not in directories_created: + if not os.path.exists(asset_directory): + os.makedirs(asset_directory, exist_ok=True) + directories_created.add(asset_directory) + + tar.makefile(file, dest) + click.echo(click.style("✔", fg="green") + f" Restored {show}") + + return directories_created + + +def download_xhiveframework_assets(verbose=True): + """Downloads and sets up XhiveFramework assets if they exist based on the current + commit HEAD. + Returns True if correctly setup else returns False. + """ + xhiveframework_head = getoutput("cd ../apps/xhiveframework && git rev-parse HEAD") + + if not xhiveframework_head: + return False + + try: + url = get_assets_link(xhiveframework_head) + assets_archive = fetch_assets(url, xhiveframework_head) + setup_assets(assets_archive) + build_missing_files() + return True + + except AssetsDontExistError as e: + click.secho(str(e), fg="yellow") + + except Exception as e: + # TODO: log traceback in bench.log + click.secho(str(e), fg="red") + + finally: + try: + shutil.rmtree(os.path.dirname(assets_archive)) + except Exception: + pass + + return False + + +def symlink(target, link_name, overwrite=False): + """ + Create a symbolic link named link_name pointing to target. + If link_name exists then FileExistsError is raised, unless overwrite=True. + When trying to overwrite a directory, IsADirectoryError is raised. + + Source: https://stackoverflow.com/a/55742015/10309266 + """ + + if not overwrite: + return os.symlink(target, link_name) + + # Create link to target with temporary filename + while True: + temp_link_name = f"tmp{xhiveframework.generate_hash()}" + + # os.* functions mimic as closely as possible system functions + # The POSIX symlink() returns EEXIST if link_name already exists + # https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html + try: + os.symlink(target, temp_link_name) + break + except FileExistsError: + pass + + # Replace link_name with temp_link_name + try: + # Pre-empt os.replace on a directory with a nicer message + if os.path.isdir(link_name): + raise IsADirectoryError(f"Cannot symlink over existing directory: '{link_name}'") + try: + os.replace(temp_link_name, link_name) + except AttributeError: + os.renames(temp_link_name, link_name) + except Exception: + if os.path.islink(temp_link_name): + os.remove(temp_link_name) + raise + + +def setup(): + global app_paths, assets_path + + pymodules = [] + for app in xhiveframework.get_all_apps(True): + try: + pymodules.append(xhiveframework.get_module(app)) + except ImportError: + pass + app_paths = [os.path.dirname(pymodule.__file__) for pymodule in pymodules] + assets_path = os.path.join(xhiveframework.local.sites_path, "assets") + + +def bundle( + mode, + apps=None, + hard_link=False, + verbose=False, + skip_xhiveframework=False, + files=None, + save_metafiles=False, +): + """concat / minify js files""" + setup() + make_asset_dirs(hard_link=hard_link) + + mode = "production" if mode == "production" else "build" + command = f"yarn run {mode}" + + if apps: + command += f" --apps {apps}" + + if skip_xhiveframework: + command += " --skip_xhiveframework" + + if files: + command += " --files {files}".format(files=",".join(files)) + + command += " --run-build-command" + + if save_metafiles: + command += " --save-metafiles" + + check_node_executable() + xhiveframework_app_path = xhiveframework.get_app_source_path("xhiveframework") + xhiveframework.commands.popen(command, cwd=xhiveframework_app_path, env=get_node_env(), raise_err=True) + + +def watch(apps=None): + """watch and rebuild if necessary""" + setup() + + command = "yarn run watch" + if apps: + command += f" --apps {apps}" + + live_reload = xhiveframework.utils.cint(os.environ.get("LIVE_RELOAD", xhiveframework.conf.live_reload)) + + if live_reload: + command += " --live-reload" + + check_node_executable() + xhiveframework_app_path = xhiveframework.get_app_source_path("xhiveframework") + xhiveframework.commands.popen(command, cwd=xhiveframework_app_path, env=get_node_env()) + + +def check_node_executable(): + node_version = Version(subprocess.getoutput("node -v")[1:]) + warn = "⚠️ " + if node_version.major < 18: + click.echo(f"{warn} Please update your node version to 18") + if not shutil.which("yarn"): + click.echo(f"{warn} Please install yarn using below command and try again.\nnpm install -g yarn") + click.echo() + + +def get_node_env(): + return {"NODE_OPTIONS": f"--max_old_space_size={get_safe_max_old_space_size()}"} + + +def get_safe_max_old_space_size(): + import psutil + + safe_max_old_space_size = 0 + try: + total_memory = psutil.virtual_memory().total / (1024 * 1024) + # reference for the safe limit assumption + # https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_megabytes + # set minimum value 1GB + safe_max_old_space_size = max(1024, int(total_memory * 0.75)) + except Exception: + pass + + return safe_max_old_space_size + + +def generate_assets_map(): + symlinks = {} + + for app_name in xhiveframework.get_all_apps(): + app_doc_path = None + + pymodule = xhiveframework.get_module(app_name) + app_base_path = os.path.abspath(os.path.dirname(pymodule.__file__)) + app_public_path = os.path.join(app_base_path, "public") + app_node_modules_path = os.path.join(app_base_path, "..", "node_modules") + app_docs_path = os.path.join(app_base_path, "docs") + app_www_docs_path = os.path.join(app_base_path, "www", "docs") + + app_assets = os.path.abspath(app_public_path) + app_node_modules = os.path.abspath(app_node_modules_path) + + # {app}/public > assets/{app} + if os.path.isdir(app_assets): + symlinks[app_assets] = os.path.join(assets_path, app_name) + + # {app}/node_modules > assets/{app}/node_modules + if os.path.isdir(app_node_modules): + symlinks[app_node_modules] = os.path.join(assets_path, app_name, "node_modules") + + # {app}/docs > assets/{app}_docs + if os.path.isdir(app_docs_path): + app_doc_path = os.path.join(app_base_path, "docs") + elif os.path.isdir(app_www_docs_path): + app_doc_path = os.path.join(app_base_path, "www", "docs") + if app_doc_path: + app_docs = os.path.abspath(app_doc_path) + symlinks[app_docs] = os.path.join(assets_path, app_name + "_docs") + + return symlinks + + +def setup_assets_dirs(): + for dir_path in (os.path.join(assets_path, x) for x in ("js", "css")): + os.makedirs(dir_path, exist_ok=True) + + +def clear_broken_symlinks(): + for path in os.listdir(assets_path): + path = os.path.join(assets_path, path) + if os.path.islink(path) and not os.path.exists(path): + os.remove(path) + + +def unstrip(message: str) -> str: + """Pads input string on the right side until the last available column in the terminal""" + _len = len(message) + try: + max_str = os.get_terminal_size().columns + except Exception: + max_str = 80 + + if _len < max_str: + _rem = max_str - _len + else: + _rem = max_str % _len + + return f"{message}{' ' * _rem}" + + +def make_asset_dirs(hard_link=False): + setup_assets_dirs() + clear_broken_symlinks() + symlinks = generate_assets_map() + + for source, target in symlinks.items(): + start_message = unstrip(f"{'Copying assets from' if hard_link else 'Linking'} {source} to {target}") + fail_message = unstrip(f"Cannot {'copy' if hard_link else 'link'} {source} to {target}") + + # Used '\r' instead of '\x1b[1K\r' to print entire lines in smaller terminal sizes + try: + print(start_message, end="\r") + link_assets_dir(source, target, hard_link=hard_link) + except Exception: + print(fail_message, end="\r") + + click.echo(unstrip(click.style("✔", fg="green") + " Application Assets Linked") + "\n") + + +def link_assets_dir(source, target, hard_link=False): + if not os.path.exists(source): + return + + if os.path.exists(target): + if os.path.islink(target): + os.unlink(target) + else: + shutil.rmtree(target) + + if hard_link: + shutil.copytree(source, target, dirs_exist_ok=True) + else: + symlink(source, target, overwrite=True) + + +def scrub_html_template(content): + """Returns HTML content with removed whitespace and comments""" + # remove whitespace to a single space + content = WHITESPACE_PATTERN.sub(" ", content) + + # strip comments + content = HTML_COMMENT_PATTERN.sub("", content) + + return content.replace("'", "'") + + +def html_to_js_template(path, content): + """returns HTML template content as Javascript code, adding it to `xhiveframework.templates`""" + return """xhiveframework.templates["{key}"] = '{content}';\n""".format( + key=path.rsplit("/", 1)[-1][:-5], content=scrub_html_template(content) + ) diff --git a/xhiveframework/cache_manager.py b/xhiveframework/cache_manager.py new file mode 100644 index 0000000..6261d89 --- /dev/null +++ b/xhiveframework/cache_manager.py @@ -0,0 +1,241 @@ +# Copyright (c) 2018, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + +common_default_keys = ["__default", "__global"] + +doctypes_for_mapping = { + "Energy Point Rule", + "Assignment Rule", + "Milestone Tracker", + "Document Naming Rule", +} + + +def get_doctype_map_key(doctype): + return xhiveframework.scrub(doctype) + "_map" + + +doctype_map_keys = tuple(map(get_doctype_map_key, doctypes_for_mapping)) + +bench_cache_keys = ("assets_json",) + +global_cache_keys = ( + "app_hooks", + "installed_apps", + "all_apps", + "app_modules", + "installed_app_modules", + "module_app", + "module_installed_app", + "system_settings", + "scheduler_events", + "time_zone", + "webhooks", + "active_domains", + "active_modules", + "assignment_rule", + "server_script_map", + "wkhtmltopdf_version", + "domain_restricted_doctypes", + "domain_restricted_pages", + "information_schema:counts", + "db_tables", + "server_script_autocompletion_items", + *doctype_map_keys, +) + +user_cache_keys = ( + "bootinfo", + "user_recent", + "roles", + "user_doc", + "lang", + "defaults", + "user_permissions", + "home_page", + "linked_with", + "desktop_icons", + "portal_menu_items", + "user_perm_can_read", + "has_role:Page", + "has_role:Report", + "desk_sidebar_items", + "contacts", +) + +doctype_cache_keys = ( + "doctype_meta", + "doctype_form_meta", + "table_columns", + "last_modified", + "linked_doctypes", + "notifications", + "workflow", + "data_import_column_header_map", +) + + +def clear_user_cache(user=None): + from xhiveframework.desk.notifications import clear_notifications + + # this will automatically reload the global cache + # so it is important to clear this first + clear_notifications(user) + + if user: + for name in user_cache_keys: + xhiveframework.cache.hdel(name, user) + xhiveframework.cache.delete_keys("user:" + user) + clear_defaults_cache(user) + else: + for name in user_cache_keys: + xhiveframework.cache.delete_key(name) + clear_defaults_cache() + clear_global_cache() + + +def clear_domain_cache(user=None): + domain_cache_keys = ("domain_restricted_doctypes", "domain_restricted_pages") + xhiveframework.cache.delete_value(domain_cache_keys) + + +def clear_global_cache(): + from xhiveframework.website.utils import clear_website_cache + + clear_doctype_cache() + clear_website_cache() + xhiveframework.cache.delete_value(global_cache_keys) + xhiveframework.cache.delete_value(bench_cache_keys) + xhiveframework.setup_module_map() + + +def clear_defaults_cache(user=None): + if user: + for p in [user, *common_default_keys]: + xhiveframework.cache.hdel("defaults", p) + elif xhiveframework.flags.in_install != "xhiveframework": + xhiveframework.cache.delete_key("defaults") + + +def clear_doctype_cache(doctype=None): + clear_controller_cache(doctype) + + _clear_doctype_cache_from_redis(doctype) + if hasattr(xhiveframework.db, "after_commit"): + xhiveframework.db.after_commit.add(lambda: _clear_doctype_cache_from_redis(doctype)) + xhiveframework.db.after_rollback.add(lambda: _clear_doctype_cache_from_redis(doctype)) + + +def _clear_doctype_cache_from_redis(doctype: str | None = None): + from xhiveframework.desk.notifications import delete_notification_count_for + + for key in ("is_table", "doctype_modules"): + xhiveframework.cache.delete_value(key) + + def clear_single(dt): + xhiveframework.clear_document_cache(dt) + for name in doctype_cache_keys: + xhiveframework.cache.hdel(name, dt) + + if doctype: + clear_single(doctype) + + # clear all parent doctypes + for dt in xhiveframework.get_all( + "DocField", "parent", dict(fieldtype=["in", xhiveframework.model.table_fields], options=doctype) + ): + clear_single(dt.parent) + + # clear all parent doctypes + if not xhiveframework.flags.in_install: + for dt in xhiveframework.get_all( + "Custom Field", "dt", dict(fieldtype=["in", xhiveframework.model.table_fields], options=doctype) + ): + clear_single(dt.dt) + + # clear all notifications + delete_notification_count_for(doctype) + + else: + # clear all + for name in doctype_cache_keys: + xhiveframework.cache.delete_value(name) + xhiveframework.cache.delete_keys("document_cache::") + + +def clear_controller_cache(doctype=None): + if not doctype: + xhiveframework.controllers.pop(xhiveframework.local.site, None) + return + + if site_controllers := xhiveframework.controllers.get(xhiveframework.local.site): + site_controllers.pop(doctype, None) + + +def get_doctype_map(doctype, name, filters=None, order_by=None): + return xhiveframework.cache.hget( + get_doctype_map_key(doctype), + name, + lambda: xhiveframework.get_all(doctype, filters=filters, order_by=order_by, ignore_ddl=True), + ) + + +def clear_doctype_map(doctype, name): + xhiveframework.cache.hdel(xhiveframework.scrub(doctype) + "_map", name) + + +def build_table_count_cache(): + if ( + xhiveframework.flags.in_patch + or xhiveframework.flags.in_install + or xhiveframework.flags.in_migrate + or xhiveframework.flags.in_import + or xhiveframework.flags.in_setup_wizard + ): + return + + table_name = xhiveframework.qb.Field("table_name").as_("name") + table_rows = xhiveframework.qb.Field("table_rows").as_("count") + information_schema = xhiveframework.qb.Schema("information_schema") + + data = (xhiveframework.qb.from_(information_schema.tables).select(table_name, table_rows)).run(as_dict=True) + counts = {d.get("name").replace("tab", "", 1): d.get("count", None) for d in data} + xhiveframework.cache.set_value("information_schema:counts", counts) + + return counts + + +def build_domain_restriced_doctype_cache(*args, **kwargs): + if ( + xhiveframework.flags.in_patch + or xhiveframework.flags.in_install + or xhiveframework.flags.in_migrate + or xhiveframework.flags.in_import + or xhiveframework.flags.in_setup_wizard + ): + return + active_domains = xhiveframework.get_active_domains() + doctypes = xhiveframework.get_all("DocType", filters={"restrict_to_domain": ("IN", active_domains)}) + doctypes = [doc.name for doc in doctypes] + xhiveframework.cache.set_value("domain_restricted_doctypes", doctypes) + + return doctypes + + +def build_domain_restriced_page_cache(*args, **kwargs): + if ( + xhiveframework.flags.in_patch + or xhiveframework.flags.in_install + or xhiveframework.flags.in_migrate + or xhiveframework.flags.in_import + or xhiveframework.flags.in_setup_wizard + ): + return + active_domains = xhiveframework.get_active_domains() + pages = xhiveframework.get_all("Page", filters={"restrict_to_domain": ("IN", active_domains)}) + pages = [page.name for page in pages] + xhiveframework.cache.set_value("domain_restricted_pages", pages) + + return pages diff --git a/xhiveframework/change_log/__init__.py b/xhiveframework/change_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/change_log/current/readme.md b/xhiveframework/change_log/current/readme.md new file mode 100644 index 0000000..e93bb75 --- /dev/null +++ b/xhiveframework/change_log/current/readme.md @@ -0,0 +1,3 @@ +Leave change log files in this folder for user release notes. + +(this file is just a place holder, don't delete it) diff --git a/xhiveframework/change_log/v10/v10_0_0.md b/xhiveframework/change_log/v10/v10_0_0.md new file mode 100644 index 0000000..1610b9f --- /dev/null +++ b/xhiveframework/change_log/v10/v10_0_0.md @@ -0,0 +1,10 @@ +- Enhanced Data Import Tool + - Data Import Tool is now a normal form, you can maintain records for each Data Import. + - Better error handling + - Background processing for large files + +- XhiveFramework now has a github connector + +- Any doctype can have a calendar view + +- XhiveFramework has a new simple, responsive, modern SVG [charts library](https://lab.membtech.com/xhiveframework/charts), developed by us diff --git a/xhiveframework/change_log/v11/v11_1_0.md b/xhiveframework/change_log/v11/v11_1_0.md new file mode 100644 index 0000000..210766e --- /dev/null +++ b/xhiveframework/change_log/v11/v11_1_0.md @@ -0,0 +1,19 @@ +- Dynamic [XhiveFramework Charts](https://lab.membtech.com/xhiveframework/charts) with Report Builder (built by @pratu16x7) +- New XhiveFramework Chat for easier internal communication (built by @achillesrasquinha) +- [XhiveFramework DataTable](https://lab.membtech.com/xhiveframework/datatable) for better reports (built by @netchampfaris) +- Google Calendar can now be integrated with XhiveFramework +- Files can now be uploaded using drag-and-drop +- Enhanced List View and Tree View +- Bulk Actions from List View +- Quill editor has been introduced in place of Summernote +- HTML Editor has been introduced +- New User Permissions +- Subscriptions in XhiveERP now moved to Auto Repeat in XhiveFramework +- Support for Razorpay and PayPal subscriptions +- Better Social login, Workflow +- Messages for when user goes online/offline +- Logout from all sessions on password change +- Desktop icons can now be selected from a dialog box +- Changes have been made to ensure XhiveFramework is compatible with Python 3 +- Better documentation is now available with support for more languages +- A lot of other fixes have been done to ensure a better overall user experience diff --git a/xhiveframework/change_log/v12/v12_0_0.md b/xhiveframework/change_log/v12/v12_0_0.md new file mode 100644 index 0000000..69f86bf --- /dev/null +++ b/xhiveframework/change_log/v12/v12_0_0.md @@ -0,0 +1,29 @@ +# Version 12 Release Notes + +### UI/UX Enhancements +1. [New Desktop](https://xhiveerp.com/docs/user/manual/en/using-xhiveerp/desktop) +1. [Keyboard Navigation](https://xhiveerp.com/docs/user/manual/en/using-xhiveerp/articles/keyboard-shortcuts) +1. [Link Preview](https://xhiveerp.com/version-12/release-notes/features#link-preview) +1. [New Upload Dialog](https://xhiveerp.com/version-12/release-notes/features#new-upload-dialog) +1. [Frequently visited links appear in Awesomebar results](https://xhiveerp.com/version-12/release-notes/features#frequently-visited-links-appear-in-awesomebar-results) +1. [Full Width Container]((https://xhiveerp.com/version-12/release-notes/features#full-width-container)) +1. [List View Enhancements](https://xhiveerp.com/version-12/release-notes/features#list-view-enhancements) + +### New Automation Module +1. [Assignment Rule](https://xhiveerp.com/docs/user/manual/en/setting-up/automation/assignment-rule) +1. [Milestones](https://xhiveerp.com/docs/user/manual/en/setting-up/automation/milestone-tracker) +1. [Auto Repeat](https://xhiveerp.com/docs/user/manual/en/setting-up/automation/auto-repeat) + +### Other Changes & Enhancements +1. [Document Follow](https://xhiveerp.com/docs/user/manual/en/setting-up/email/document-follow) +1. [Energy Points](https://xhiveerp.com/docs/user/manual/en/setting-up/energy-point-system) +1. [Dashboards](https://xhiveerp.com/docs/user/manual/en/customize-xhiveerp/dashboard) +1. [Disable customization for single doctypes](https://xhiveerp.com/version-12/release-notes/features#disable-customization-for-single-doctypes) +1. [Email Linking](https://xhiveerp.com/docs/user/manual/en/setting-up/email/linking-emails-to-document) +1. [Google Contacts](https://xhiveerp.com/docs/user/manual/en/xhiveerp_integration/google_contacts) +1. [PDF Encryption](https://xhiveerp.com/version-12/release-notes/features#pdf-encryption) +1. [Raw Printing](https://xhiveerp.com/docs/user/manual/en/setting-up/print/raw-printing) +1. [Web Form Refactor](https://xhiveerp.com/version-12/release-notes/features#web-form-refactor) +1. [Website Refactor](https://xhiveerp.com/docs/user/manual/en/website) +1. [Added Track Views field to Customize Form](https://xhiveerp.com/version-12/release-notes/features#added-track-views-field-to-customize-form) +1. [Add custom columns to any report](https://xhiveerp.com/version-12/release-notes/features#add-custom-columns-to-any-report) diff --git a/xhiveframework/change_log/v13/v13_0_0.md b/xhiveframework/change_log/v13/v13_0_0.md new file mode 100644 index 0000000..e84fec3 --- /dev/null +++ b/xhiveframework/change_log/v13/v13_0_0.md @@ -0,0 +1,54 @@ +# Version 13.0.0 Release Notes + +## Highlights + +- Re-branded UI 💎 ✨🎊 ([#12277](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12277)) +- New Page Builder in Web Page ([#10035](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10035)) +- Customizable desk ([#9617](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9617)) +- Custom Dashboard for DocTypes ([#9872](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9872)) +- Widgets to make dashboards ([#9693](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9693)) +- Events Streaming ([#8567](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/8567)) +- Contextual translation and Translation Tool ([#9636](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9636)) + +### Other Features & Enhancements + +- Added permission to grant only `Select` access ([#12063](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12063)) +- Add columns and filters for reports via configuration ([#11287](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11287)) +- Configurable Navbar logo and dropdowns ([#11213](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11213)) +- Rule based naming of documents ([#11439](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11439)) +- New routing style, not using hashes, also /desk -> /app ([#11917](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11917)) +- Web Page tracking ([#9959](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9959)) +- Introduced "Yesterday" and "Tomorrow" options for Timespan filter ([12179](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12179)) +- Child table pagination ([#8786](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/8786)) +- Introduced Duration Control ([#10248](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10248)) +- Form Tour feature ([#10287](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10287)) +
    +More + +- Introduced Map View ([#11202](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11202)) +- Custom JS & CSS support in Web Form ([#9121](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9121)) ([#9610](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9610)) +- Ability to attach photo from webcam ([#12160](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12160)) +- Added a System Console to help in debugging ([#11306](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11306)) +- Introduced System Settings to automatically delete old Prepared Reports ([#9751](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9751)) +- "Mandatory Depends On" and "Read Only Depends On" option for document fields ([#8820](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/8820)) +- Added 2FA for LDAP users ([#10001](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10001)) +- Introduced Help Article Feedback system ([#10260](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10260)) +- Introduced Razorpay client ([#11418](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11418)) +- Rate Limiting ([#10310](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10310)) +- Introduced Log Settings ([#11699](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11699)) +- Enhancements in notifications ([#11398](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11398)) ([#11409](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11409)) +- Added a field-level permission check for report data ([12163](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12163)) +- Ability to cancel all linked document with a single click ([#8905](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/8905)) +- Made checkboxes navigable via tab key ([#11030](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11030)) +- Renamed "Custom Script" to "Client Script" ([#12324](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12324)) + +
    + +### Performance + +- Faster application load ([#12364](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12364)) ([#10229](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10229)) ([#10147](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10147)) ([#9930](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9930)) +- Theme files will now be compressed to make the website load faster ([#11048](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/11048)) +- Confirmation emails will be sent instantly ([#10790](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/10790)) +- Faster scheduled job processing ([#9928](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/9928)) +- Faster data imports ([#12565](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12565)) +- Faster CLI commands ([#12447](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12447)) diff --git a/xhiveframework/change_log/v13/v13_1_0.md b/xhiveframework/change_log/v13/v13_1_0.md new file mode 100644 index 0000000..091c9e1 --- /dev/null +++ b/xhiveframework/change_log/v13/v13_1_0.md @@ -0,0 +1,22 @@ +# Version 13.1.0 Release Notes + +### Features & Enhancements + +- Automated mail notifications will be shown in timeline ([#12693](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12693)) +- Introduced Client Script for List views ([#12590](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12590)) +- Introduced language switcher for guest users on website navbar ([#12813](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12813)) +- Option to give submit permission while sharing a document ([#12799](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12799)) +- Added option to set `autoname` in Customize Form ([#12413](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12413)) +- Virtual DocType ([#12121](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12121)) + +### Fixes + +- Workspace fixes ([#12650](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12650)) ([#12655](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12655)) ([#12869](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12869)) +- Fixed an issue where select options were not getting updated in Grid ([#12839](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12839)) +- Webform Fixes ([#12630](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12630)) ([#12756](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12756)) ([#12819](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12819)) +- Fixed timespan filter for next and last timespans ([#12509](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12509)) +- System Notification fixes ([#12719](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12719)) +- Design Fixes ([#12669](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12669)) ([#12591](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12591)) ([#12557](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12557)) ([#12751](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12751)) ([#12864](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12864)) +- Fixed Multi-column paste in grid ([#12861](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12861)) +- Fixed grid validation ([#12744](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12744)) +- Fixed currency value formatting in dashboard chart ([#12613](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12613)) diff --git a/xhiveframework/change_log/v13/v13_2_0.md b/xhiveframework/change_log/v13/v13_2_0.md new file mode 100644 index 0000000..714e2ad --- /dev/null +++ b/xhiveframework/change_log/v13/v13_2_0.md @@ -0,0 +1,32 @@ +# Version 13.2.0 Release Notes + +### Features & Enhancements + +- Add option to mention a group of users ([#12844](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12844)) +- Copy DocType / documents across sites ([#12872](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12872)) +- Scheduler log in notifications ([#1135](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/1135)) +- Add Enable/Disable Webhook via Check Field ([#12842](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12842)) +- Allow query/custom reports to save custom data in the json field ([#12534](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12534)) + +### Fixes + +- Load server translations in boot (backport #12848) ([#12852](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12852)) +- Allow to override dashboard chart properties type/color ([#12846](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12846)) +- Multi-column paste in grid ([#12861](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12861)) +- Add log_error and XhiveFrameworkClient to restricted python ([#12857](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12857)) +- Redirect Web Form user directly to success URL, if no amount is due ([#12661](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12661)) +- Attachment pill lock icon redirects to File ([#12864](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12864)) +- Redirect Web Form user directly to success URL, if no amount is due (backport #12661) ([#12856](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12856)) +- Remove events to redraw charts ([#12973](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12973)) +- Don't allow user to remove/change data source file in data import ([#12827](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12827)) +- Load server translations in boot ([#12848](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12848)) +- Newly created Workspace not being accessible unless a shortcut u… ([#12866](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12866)) +- Currency labels in grids ([#12974](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12974)) +- Handle error while session start ([#12933](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12933)) +- Add field type check in custom field validation ([#12858](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12858)) +- Make language select optional and fix breakpoint issues ([#12860](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12860)) +- Form Dashboard reference link ([#12945](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12945)) +- Invalid HTML generated by the base template ([#12953](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12953)) +- Default values were not triggering change event ([#12975](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12975)) +- Make strings translatable ([#12877](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12877)) +- Added build-message-files command ([#12950](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/12950)) diff --git a/xhiveframework/change_log/v13/v13_3_0.md b/xhiveframework/change_log/v13/v13_3_0.md new file mode 100644 index 0000000..bc334cc --- /dev/null +++ b/xhiveframework/change_log/v13/v13_3_0.md @@ -0,0 +1,49 @@ +# Version 13.3.0 Release Notes + +### Features & Enhancements + +- Deletion Steps in Data Deletion Tool ([#13124](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13124)) +- Format Option for list-apps in bench CLI ([#13125](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13125)) +- Add password fieldtype option for Web Form ([#13093](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13093)) +- Add simple __repr__ for DocTypes ([#13151](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13151)) +- Switch theme with left/right keys ([#13077](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13077)) +- sourceURL for injected javascript ([#13022](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13022)) + +### Fixes + +- Decode uri before importing file via weblink ([#13026](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13026)) +- Respond to /api requests as JSON by default ([#13053](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13053)) +- Disabled checkbox should be disabled ([#13021](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13021)) +- Moving Site folder across different FileSystems failed ([#13038](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13038)) +- Freeze screen till the background request is complete ([#13078](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13078)) +- Added conditional rendering for content field in split section w… ([#13075](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13075)) +- Show delete button on portal if user has permission to delete document ([#13149](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13149)) +- Dont disable dialog scroll on focusing a Link/Autocomplete field ([#13119](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13119)) +- Typo in RecorderDetail.vue ([#13086](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13086)) +- Error for bench drop-site. Added missing import. ([#13064](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13064)) +- Report column context ([#13090](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13090)) +- Different service name for push and pull request events ([#13094](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13094)) +- Moving Site folder across different FileSystems failed ([#13033](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13033)) +- Consistent checkboxes on all browsers ([#13042](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13042)) +- Changed shorcut widgets color picker to dropdown ([#13073](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13073)) +- Error while exporting reports with duration field ([#13118](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13118)) +- Add margin to download backup card ([#13079](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13079)) +- Move mention list generation logic to server-side ([#13074](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13074)) +- Make strings translatable ([#13046](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13046)) +- Don't evaluate dynamic properties to check if conflicts exist ([#13186](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13186)) +- Add __ function in vue global for translation in recorder ([#13089](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13089)) +- Make strings translatable ([#13076](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13076)) +- Show config in bench CLI ([#13128](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13128)) +- Add breadcrumbs for list view ([#13091](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13091)) +- Do not skip data in save while using shortcut ([#13182](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13182)) +- Use docfields from options if no docfields are returned from meta ([#13188](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13188)) +- Disable reloading files in `__pycache__` directory ([#13109](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13109)) +- RTL stylesheet route to load RTL style on demand. ([#13007](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13007)) +- Do not show messsage when exception is handled ([#13111](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13111)) +- Replace parseFloat by Number ([#13082](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13082)) +- Add margin to download backup card ([#13050](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13050)) +- Translate report column labels ([#13083](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13083)) +- Grid row color picker field not working ([#13040](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13040)) +- Improve oauthlib implementation ([#13045](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13045)) +- Replace filter_by like with full text filter ([#13126](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13126)) +- Focus jumps to first field ([#13067](https://lab.membtech.com/xhiveframework/xhiveframework15/pull/13067)) diff --git a/xhiveframework/change_log/v5/v5_0_18.md b/xhiveframework/change_log/v5/v5_0_18.md new file mode 100644 index 0000000..95e07f3 --- /dev/null +++ b/xhiveframework/change_log/v5/v5_0_18.md @@ -0,0 +1,6 @@ +#### Updates to Web Forms + +- Web Forms list now is a standard portal list and includes paging and other extensions +- Section, Column Breaks in Web Forms +- Consistent User Interface +- Cleanup of Portal Pages diff --git a/xhiveframework/change_log/v5/v5_0_20.md b/xhiveframework/change_log/v5/v5_0_20.md new file mode 100644 index 0000000..18e9f43 --- /dev/null +++ b/xhiveframework/change_log/v5/v5_0_20.md @@ -0,0 +1 @@ +- Ability to send yourself a copy of the outgoing email added back. diff --git a/xhiveframework/change_log/v5/v5_0_32.md b/xhiveframework/change_log/v5/v5_0_32.md new file mode 100644 index 0000000..17a7cb3 --- /dev/null +++ b/xhiveframework/change_log/v5/v5_0_32.md @@ -0,0 +1,5 @@ +- Reports are now searchable from awesome bar +- Show currect label for title in list views +- Datepicker now sets default value as Today +- Map child table as per meta, if not mentioned in table_map via mapper +- Re-enable save button on error \ No newline at end of file diff --git a/xhiveframework/change_log/v5/v5_1_0.md b/xhiveframework/change_log/v5/v5_1_0.md new file mode 100644 index 0000000..3036acc --- /dev/null +++ b/xhiveframework/change_log/v5/v5_1_0.md @@ -0,0 +1,3 @@ +- Change print font from Setup > Print Settings or set it for each Print Format. Font options are "Default", "Arial", "Helvetica", "Verdana", "Monospace". +- Print and full-page print preview in user's language +- Fixed inconsistent visibility of a logged-in user's image in website diff --git a/xhiveframework/change_log/v5/v5_1_1.md b/xhiveframework/change_log/v5/v5_1_1.md new file mode 100644 index 0000000..befa5be --- /dev/null +++ b/xhiveframework/change_log/v5/v5_1_1.md @@ -0,0 +1 @@ +- Ability to **Share with Everyone** (except Guest) using **Share With** diff --git a/xhiveframework/change_log/v5/v5_3_0.md b/xhiveframework/change_log/v5/v5_3_0.md new file mode 100644 index 0000000..1f70645 --- /dev/null +++ b/xhiveframework/change_log/v5/v5_3_0.md @@ -0,0 +1,40 @@ +- Added Language Support for Following languages + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    bo*ལྷ་སའི་སྐད་
    fisuomalainen
    kmភាសាខ្មែរ
    mkмакедонски
    myMelayu
    nonorsk
    svSvenska
    sqshqiptar
    + +* Unable to find translations for Tibetian via Google + +- To contribute to translations, please login to [https://translate.xhiveerp.com](https://translate.xhiveerp.com) diff --git a/xhiveframework/change_log/v5/v5_4_0.md b/xhiveframework/change_log/v5/v5_4_0.md new file mode 100644 index 0000000..5bf04bb --- /dev/null +++ b/xhiveframework/change_log/v5/v5_4_0.md @@ -0,0 +1 @@ +- Moved Backup Manager and Social Login Keys to the new **Integrations** module diff --git a/xhiveframework/change_log/v6/v6_0_0.md b/xhiveframework/change_log/v6/v6_0_0.md new file mode 100644 index 0000000..6f9c8fc --- /dev/null +++ b/xhiveframework/change_log/v6/v6_0_0.md @@ -0,0 +1,3 @@ +- **Realtime updates** for new comments and list view +- Get warned if someone else modified the document that you are working on +- You can now quickly assign a document to yourself by clicking on "Assign to me" diff --git a/xhiveframework/change_log/v6/v6_0_8.md b/xhiveframework/change_log/v6/v6_0_8.md new file mode 100644 index 0000000..6a2fba5 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_0_8.md @@ -0,0 +1 @@ +- Set HTML in Website Settings. This is usually used for website verification and SEO. diff --git a/xhiveframework/change_log/v6/v6_12_0.md b/xhiveframework/change_log/v6/v6_12_0.md new file mode 100644 index 0000000..abcdd16 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_12_0.md @@ -0,0 +1 @@ +- Extract emails using IMAP. Contributed by Gangadhar Kadam ([New Indictrans](http://indictranstech.com/)) diff --git a/xhiveframework/change_log/v6/v6_13_0.md b/xhiveframework/change_log/v6/v6_13_0.md new file mode 100644 index 0000000..661e04f --- /dev/null +++ b/xhiveframework/change_log/v6/v6_13_0.md @@ -0,0 +1,4 @@ +- Attachments can now be marked as **Private** + - Private files cannot be accessed unless you are logged in + - To access a private file, you need to have read permission on the file or read permission on the document to which the file is attached + - All attachments in a new incoming email are private diff --git a/xhiveframework/change_log/v6/v6_14_1.md b/xhiveframework/change_log/v6/v6_14_1.md new file mode 100644 index 0000000..4aed877 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_14_1.md @@ -0,0 +1 @@ +- Added language support for Malayalam: **ml - മലയാളം** diff --git a/xhiveframework/change_log/v6/v6_15_0.md b/xhiveframework/change_log/v6/v6_15_0.md new file mode 100644 index 0000000..9d1e6f9 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_15_0.md @@ -0,0 +1,4 @@ +- **For Developers:** Automatic logging of request errors and its context in **Error Snapshot** + - Thank you [Maxwell Morais](https://discuss.xhiveerp.com/users/max_morais_dmm/activity) for this useful feature + - You can access it from *Developer > Logs > Error Snapshot* +- Added language support for [Gujarati](https://translate.xhiveerp.com/view?lang=gu): **gu - ગુજરાતી** diff --git a/xhiveframework/change_log/v6/v6_16_1.md b/xhiveframework/change_log/v6/v6_16_1.md new file mode 100644 index 0000000..b638724 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_16_1.md @@ -0,0 +1 @@ +- Mention users in comments using `@username`. Mentioned users will receive an email with the comment. \ No newline at end of file diff --git a/xhiveframework/change_log/v6/v6_16_4.md b/xhiveframework/change_log/v6/v6_16_4.md new file mode 100644 index 0000000..63ae316 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_16_4.md @@ -0,0 +1 @@ +- Increased uploaded file size limit upto 10MB \ No newline at end of file diff --git a/xhiveframework/change_log/v6/v6_17_0.md b/xhiveframework/change_log/v6/v6_17_0.md new file mode 100644 index 0000000..f660660 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_17_0.md @@ -0,0 +1,4 @@ +- Ability to **Like** a document, comment or communication + - See notifications about likes that you received + - View it on Activity feed + - *Stars* have been converted to Likes diff --git a/xhiveframework/change_log/v6/v6_1_0.md b/xhiveframework/change_log/v6/v6_1_0.md new file mode 100644 index 0000000..cc628ba --- /dev/null +++ b/xhiveframework/change_log/v6/v6_1_0.md @@ -0,0 +1,7 @@ +- Sections can now be set as **Collapsible**. +- Collapsible sections can be shown as collapsed based on certain rules defined in the **Collapsible Depends On** property of the document field (DocField). +- Title is now editable from the form if the `fieldname` of the title field is **title**. +- Document can now be renamed by clicking the page heading. +- Fields can be set as **Bold** so that they can be easily identified in long forms. +- Fixed mobile views +- See Data Import progress in realtime diff --git a/xhiveframework/change_log/v6/v6_20_0.md b/xhiveframework/change_log/v6/v6_20_0.md new file mode 100644 index 0000000..162e43b --- /dev/null +++ b/xhiveframework/change_log/v6/v6_20_0.md @@ -0,0 +1,5 @@ +- Fixed **Export** for large reports +- Added language support for: + - [Estonian](https://translate.xhiveerp.com/view?lang=et): **et - eesti** + - [Telugu](https://translate.xhiveerp.com/view?lang=te): **te - తెలుగు** + - [Urdu](https://translate.xhiveerp.com/view?lang=ur): **ur - اردو** diff --git a/xhiveframework/change_log/v6/v6_21_0.md b/xhiveframework/change_log/v6/v6_21_0.md new file mode 100644 index 0000000..48e0ef8 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_21_0.md @@ -0,0 +1 @@ +- Repeating Letter Head and Footer in PDF \ No newline at end of file diff --git a/xhiveframework/change_log/v6/v6_22_0.md b/xhiveframework/change_log/v6/v6_22_0.md new file mode 100644 index 0000000..a74cbc1 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_22_0.md @@ -0,0 +1,2 @@ +- **Comment**, **Feed** and **Communication** are merged into one table, **Communication** +- Ability to turn-off repeating headers and footers in PDF via **Print Settings** diff --git a/xhiveframework/change_log/v6/v6_23_0.md b/xhiveframework/change_log/v6/v6_23_0.md new file mode 100644 index 0000000..8566425 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_23_0.md @@ -0,0 +1,2 @@ +- Autosuggest email address in **CC** when composing emails - contributed by [Robert Schouten](https://github.com/robertschouten) +- Fix: Attach signature with Email Account's auto-reply diff --git a/xhiveframework/change_log/v6/v6_25_0.md b/xhiveframework/change_log/v6/v6_25_0.md new file mode 100644 index 0000000..99f694b --- /dev/null +++ b/xhiveframework/change_log/v6/v6_25_0.md @@ -0,0 +1,4 @@ +- **Custom Translations** via Setup > Customize > Custom Translations +- Print multiple documents from list view + - Check documents and click on Menu > Print +- PDF printing for Query Reports diff --git a/xhiveframework/change_log/v6/v6_26_0.md b/xhiveframework/change_log/v6/v6_26_0.md new file mode 100644 index 0000000..8da70c3 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_26_0.md @@ -0,0 +1,2 @@ +- **Don't allow user to move standard fields.** You can still move any Custom Field to a desired position. +- Landscape orientation for Report PDF diff --git a/xhiveframework/change_log/v6/v6_26_6.md b/xhiveframework/change_log/v6/v6_26_6.md new file mode 100644 index 0000000..98bdf8e --- /dev/null +++ b/xhiveframework/change_log/v6/v6_26_6.md @@ -0,0 +1 @@ +- Check permissions on printing or making pdf of report \ No newline at end of file diff --git a/xhiveframework/change_log/v6/v6_27_1.md b/xhiveframework/change_log/v6/v6_27_1.md new file mode 100644 index 0000000..ebacf19 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_27_1.md @@ -0,0 +1,6 @@ +- Configurable Desktop + - Add any Documents, Reports, Modules, Pages to the desktop + - Remove all the unwanted icons +- **Module Page New Design** + - New module design now shows all documents in a module together + - [Read the Details](https://xhiveframework.io/blog/xhiveerp-features/configurable-desktop) diff --git a/xhiveframework/change_log/v6/v6_27_11.md b/xhiveframework/change_log/v6/v6_27_11.md new file mode 100644 index 0000000..4d67135 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_27_11.md @@ -0,0 +1,2 @@ +- Get [email sending status](https://discuss.xhiveerp.com/t/communication-delivery-status-bulk-email-status/11941) in document timeline +- Ability to disable a Role diff --git a/xhiveframework/change_log/v6/v6_2_0.md b/xhiveframework/change_log/v6/v6_2_0.md new file mode 100644 index 0000000..0525324 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_2_0.md @@ -0,0 +1,3 @@ +- **Permissions:** + - If User Permissions are missing for a DocType, don't show non-matching records. + - If **Ignore User Permissions If Missing** is checked in System Settings, show records even if User Permissions are not defined. diff --git a/xhiveframework/change_log/v6/v6_3_0.md b/xhiveframework/change_log/v6/v6_3_0.md new file mode 100644 index 0000000..f605c46 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_3_0.md @@ -0,0 +1,2 @@ +- You can now add **CC** in Email +- Show checkboxes in Print diff --git a/xhiveframework/change_log/v6/v6_4_0.md b/xhiveframework/change_log/v6/v6_4_0.md new file mode 100644 index 0000000..ae56802 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_4_0.md @@ -0,0 +1 @@ +- **File Manager:** A Document Management System for your organisation. Add files, organize them in folders and share it with a few users or everyone in the company. diff --git a/xhiveframework/change_log/v6/v6_4_8.md b/xhiveframework/change_log/v6/v6_4_8.md new file mode 100644 index 0000000..afc5e45 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_4_8.md @@ -0,0 +1,20 @@ +- Added Language Support for Following languages + + + + + + + + + + + + + + + + + + +
    bnবাংলা
    da-DKdansk (Danmark)
    es-PEEspañol (Perú)
    sislovenščina
    diff --git a/xhiveframework/change_log/v6/v6_5_0.md b/xhiveframework/change_log/v6/v6_5_0.md new file mode 100644 index 0000000..62afbd3 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_5_0.md @@ -0,0 +1,2 @@ +- **Linked With** will now show links from Dynamic Links +- **Data** field-type size truncated to 140 characters from 255 (by default). Can be changed by setting the **length** property from **Customize Form View** diff --git a/xhiveframework/change_log/v6/v6_6_0.md b/xhiveframework/change_log/v6/v6_6_0.md new file mode 100644 index 0000000..6655897 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_6_0.md @@ -0,0 +1 @@ +- Added language support for Ukranian: **uk - українська** diff --git a/xhiveframework/change_log/v6/v6_7_0.md b/xhiveframework/change_log/v6/v6_7_0.md new file mode 100644 index 0000000..ffb546d --- /dev/null +++ b/xhiveframework/change_log/v6/v6_7_0.md @@ -0,0 +1,3 @@ +- See who is currently viewing the document +- Sounds for various actions +- Added language support for Slovene: **sl - slovenščina (Slovene)** diff --git a/xhiveframework/change_log/v6/v6_8_0.md b/xhiveframework/change_log/v6/v6_8_0.md new file mode 100644 index 0000000..012b406 --- /dev/null +++ b/xhiveframework/change_log/v6/v6_8_0.md @@ -0,0 +1,2 @@ +- Pre-configured Email Account for Yandex.Mail +- Fixed inline images in received emails diff --git a/xhiveframework/change_log/v7/v7_0_0.md b/xhiveframework/change_log/v7/v7_0_0.md new file mode 100644 index 0000000..3521047 --- /dev/null +++ b/xhiveframework/change_log/v7/v7_0_0.md @@ -0,0 +1,36 @@ +#### UI +- Editable Grids +- Image Field in DocType, form and list +- Dashboard, Heatmap, Graphs on Form View +- Document Flow in forms +- List views: remembers user settings + +#### Celery to RQ + +#### Quick Entry + +#### Razorpay Integration + +#### Passwords + +#### Portals +- Statics (`www` folder): directly served from templates +- New Routing +- Web Forms + +#### Limits +- Expiry, space etc +- Usage Info Page + +#### Minor +- **Rename:** Bulk Email is now Email Queue +- `xhiveframework.require` is async +- `flot.js` replaced by `c3.js` +- Most popular links on the top +- Standard Replies configurable +- Timeline permisions (not based on user permissions) +- "Track Seen" feature in doctypes +- Moved: "Edit Profile" page is now in xhiveframework (moved from XhiveERP) +- Cleanup UI for chat +- New default user icons (based on initials) +- Multiple assign (add a document to multiple users) diff --git a/xhiveframework/change_log/v7/v7_0_18.md b/xhiveframework/change_log/v7/v7_0_18.md new file mode 100644 index 0000000..5e59c02 --- /dev/null +++ b/xhiveframework/change_log/v7/v7_0_18.md @@ -0,0 +1,2 @@ +- New Feature: Ability to add multiple sessions to users. Edit **User** record and edit "Simultaneous Sessions" +- New Feature: Select columns to export and import in **Data Import Tool** \ No newline at end of file diff --git a/xhiveframework/change_log/v7/v7_1_0.md b/xhiveframework/change_log/v7/v7_1_0.md new file mode 100644 index 0000000..7d2187a --- /dev/null +++ b/xhiveframework/change_log/v7/v7_1_0.md @@ -0,0 +1,24 @@ +#### Gantt View +- New Gantt view for documents where date range is available + +#### In-App Help +- Search for help from within the app. Click on "Help" + +#### Web Form +- Add grids (child tables) +- Add page breaks (for long forms) +- Add payment gateway +- Add attachments + +#### Auto Email Report +- Email reports automatically on daily / weekly / monthly basis + +#### Other Fixes +- Send a popup to all users on login for a new Note by checking on "Notify users with a popup when they log in" +- Portal Users (Customers, Supplier, Students) can now have roles +- Sidebar in portal view will be rendered as per roles and can be configured from Portal Settings +- Restrict the number of backups to be saved in System Settings +- Scheduler log is now error log and as MyISAM +- A better way to export customzations and Email Alert directly from Customize Form +- Option to send email from Data Import Tool where applicable +- Integration Broker \ No newline at end of file diff --git a/xhiveframework/change_log/v7/v7_2_0.md b/xhiveframework/change_log/v7/v7_2_0.md new file mode 100644 index 0000000..9968248 --- /dev/null +++ b/xhiveframework/change_log/v7/v7_2_0.md @@ -0,0 +1,18 @@ +- Filters Dashboard + - Dashboard with pre-defined filters in List/Report View +- Tag Category + - Show/Group tags based on category +- Updated Font Awesome version to 4.x.x +- Checkboxes in grid + - Delete selected rows + - Map selected rows from one document to another. +- Show Totals button in report +- OpenID Connect for XhiveFramework +- Threading based on message id in Email Queue +- New control object daterangepicker for filtering +- Orientation selection in PDF +- Expand/Collapse All buttons in tree view reports +- Add attachment from email and copy attachments to Communication Record +- Bulk Upload from zip file +- Tree view decoration +- Custom menu for report view diff --git a/xhiveframework/change_log/v8/v8_0_0.md b/xhiveframework/change_log/v8/v8_0_0.md new file mode 100644 index 0000000..31d375d --- /dev/null +++ b/xhiveframework/change_log/v8/v8_0_0.md @@ -0,0 +1,41 @@ +#### Global Search +- Now, from awesome bar, you can search all the documents related to a specific keyword. +- For example, you can get all Quotations, Sales Orders and Sales Invoices related to a Customer, by searching with customer's email address / mobile no. + +#### Kanban View +- Kanban Board allows you to identify a field based on which documents will be categorised and viewed together. +- From the board itself, you can update the status of teh document, comment on it or assign it someone. + +#### Document Versioning +- Now the system maintains all the changes of a document with the information of user and timestamp. + +#### Delete and Restore +- In version 8, all the deleted documents are stored in the "Deleted Documents" table, which can be restored later +- To permanently delete a record, you need to delete it from "Deleted Documents". + +#### Email Inbox +- We have introduced an Inbox View for all the communications. +- It has all the basic functionalities of an Email Client like Inbox, Sent Mails, Trash and Spam folders, Contact list, Read/Unread, forwarding of an email etc. +- Issue, Lead and Opportunity can be created directly from the Communication. + +#### Custom Permissions +- Now, all the customised permissions are stored separately in a Custom DocPerm table. + +#### Permissions for Reports and Page +- Report and Page have it's own dedicated permission table now, it's no more dependent on the reference Doctype for permissions. +- There is a page "Role Permission for Page and Report", from where you will be able to customize the permissions for reports and pages. + +#### Newsletter Enhancements +- In the new version, newsletter can be sent to multiple email groups. +- You will also be able to send the attachments with the newsletter. +- Unsubscribe link is optional now + +#### New Calendar and Date Picker +- Get a more crisp view of calendar and events. +- Date picker also has been updated in the version. + +#### Summernote Text Editor +- We have also integrated Summernote text editor in the XhiveERP. It has many tools to create better texts. + +#### Export report in Excel format +- Reports can now also be exported in Excel format. diff --git a/xhiveframework/change_log/v8/v8_7_0.md b/xhiveframework/change_log/v8/v8_7_0.md new file mode 100644 index 0000000..47b3b84 --- /dev/null +++ b/xhiveframework/change_log/v8/v8_7_0.md @@ -0,0 +1,2 @@ +### User Permissions +- User Permission is now a DocType, a new UX for the existing Role and User Permission managers to make it easy to enter permissions. For more details please check User Permissions diff --git a/xhiveframework/change_log/v8/v8_8_0.md b/xhiveframework/change_log/v8/v8_8_0.md new file mode 100644 index 0000000..e93c0ad --- /dev/null +++ b/xhiveframework/change_log/v8/v8_8_0.md @@ -0,0 +1,2 @@ +### Two Factor Authentication +- Now you can authenticate user with two factor authentication. You can enable the Two Factor Authentication from System Settings. \ No newline at end of file diff --git a/xhiveframework/client.py b/xhiveframework/client.py new file mode 100644 index 0000000..979384c --- /dev/null +++ b/xhiveframework/client.py @@ -0,0 +1,506 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import json +import os +from typing import TYPE_CHECKING + +import xhiveframework +import xhiveframework.model +import xhiveframework.utils +from xhiveframework import _ +from xhiveframework.desk.reportview import validate_args +from xhiveframework.model.db_query import check_parent_permission +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.utils import get_safe_filters +from xhiveframework.utils.deprecations import deprecated + +if TYPE_CHECKING: + from xhiveframework.model.document import Document + +""" +Handle RESTful requests that are mapped to the `/api/resource` route. + +Requests via XhiveFrameworkClient are also handled here. +""" + + +@xhiveframework.whitelist() +def get_list( + doctype, + fields=None, + filters=None, + group_by=None, + order_by=None, + limit_start=None, + limit_page_length=20, + parent=None, + debug: bool = False, + as_dict: bool = True, + or_filters=None, +): + """Returns a list of records by filters, fields, ordering and limit + + :param doctype: DocType of the data to be queried + :param fields: fields to be returned. Default is `name` + :param filters: filter list by this dict + :param order_by: Order by this fieldname + :param limit_start: Start at this index + :param limit_page_length: Number of records to be returned (default 20)""" + if xhiveframework.is_table(doctype): + check_parent_permission(parent, doctype) + + args = xhiveframework._dict( + doctype=doctype, + parent_doctype=parent, + fields=fields, + filters=filters, + or_filters=or_filters, + group_by=group_by, + order_by=order_by, + limit_start=limit_start, + limit_page_length=limit_page_length, + debug=debug, + as_list=not as_dict, + ) + + validate_args(args) + return xhiveframework.get_list(**args) + + +@xhiveframework.whitelist() +def get_count(doctype, filters=None, debug=False, cache=False): + return xhiveframework.db.count(doctype, get_safe_filters(filters), debug, cache) + + +@xhiveframework.whitelist() +def get(doctype, name=None, filters=None, parent=None): + """Returns a document by name or filters + + :param doctype: DocType of the document to be returned + :param name: return document of this `name` + :param filters: If name is not set, filter by these values and return the first match""" + if xhiveframework.is_table(doctype): + check_parent_permission(parent, doctype) + + if name: + doc = xhiveframework.get_doc(doctype, name) + elif filters or filters == {}: + doc = xhiveframework.get_doc(doctype, xhiveframework.parse_json(filters)) + else: + doc = xhiveframework.get_doc(doctype) # single + + doc.check_permission() + doc.apply_fieldlevel_read_permissions() + + return doc.as_dict() + + +@xhiveframework.whitelist() +def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, parent=None): + """Returns a value form a document + + :param doctype: DocType to be queried + :param fieldname: Field to be returned (default `name`) + :param filters: dict or string for identifying the record""" + if xhiveframework.is_table(doctype): + check_parent_permission(parent, doctype) + + if not xhiveframework.has_permission(doctype, parent_doctype=parent): + xhiveframework.throw(_("No permission for {0}").format(_(doctype)), xhiveframework.PermissionError) + + filters = get_safe_filters(filters) + if isinstance(filters, str): + filters = {"name": filters} + + try: + fields = xhiveframework.parse_json(fieldname) + except (TypeError, ValueError): + # name passed, not json + fields = [fieldname] + + # check whether the used filters were really parseable and usable + # and did not just result in an empty string or dict + if not filters: + filters = None + + if xhiveframework.get_meta(doctype).issingle: + value = xhiveframework.db.get_values_from_single(fields, filters, doctype, as_dict=as_dict, debug=debug) + else: + value = get_list( + doctype, + filters=filters, + fields=fields, + debug=debug, + limit_page_length=1, + parent=parent, + as_dict=as_dict, + ) + + if as_dict: + return value[0] if value else {} + + if not value: + return + + return value[0] if len(fields) > 1 else value[0][0] + + +@xhiveframework.whitelist() +def get_single_value(doctype, field): + if not xhiveframework.has_permission(doctype): + xhiveframework.throw(_("No permission for {0}").format(_(doctype)), xhiveframework.PermissionError) + + return xhiveframework.db.get_single_value(doctype, field) + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def set_value(doctype, name, fieldname, value=None): + """Set a value using get_doc, group of values + + :param doctype: DocType of the document + :param name: name of the document + :param fieldname: fieldname string or JSON / dict with key value pair + :param value: value if fieldname is JSON / dict""" + + if fieldname in (xhiveframework.model.default_fields + xhiveframework.model.child_table_fields): + xhiveframework.throw(_("Cannot edit standard fields")) + + if not value: + values = fieldname + if isinstance(fieldname, str): + try: + values = json.loads(fieldname) + except ValueError: + values = {fieldname: ""} + else: + values = {fieldname: value} + + # check for child table doctype + if not xhiveframework.get_meta(doctype).istable: + doc = xhiveframework.get_doc(doctype, name) + doc.update(values) + else: + doc = xhiveframework.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True) + doc = xhiveframework.get_doc(doc.parenttype, doc.parent) + child = doc.getone({"doctype": doctype, "name": name}) + child.update(values) + + doc.save() + + return doc.as_dict() + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def insert(doc=None): + """Insert a document + + :param doc: JSON or dict object to be inserted""" + if isinstance(doc, str): + doc = json.loads(doc) + + return insert_doc(doc).as_dict() + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def insert_many(docs=None): + """Insert multiple documents + + :param docs: JSON or list of dict objects to be inserted in one request""" + if isinstance(docs, str): + docs = json.loads(docs) + + if len(docs) > 200: + xhiveframework.throw(_("Only 200 inserts allowed in one request")) + + return [insert_doc(doc).name for doc in docs] + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def save(doc): + """Update (save) an existing document + + :param doc: JSON or dict object with the properties of the document to be updated""" + if isinstance(doc, str): + doc = json.loads(doc) + + doc = xhiveframework.get_doc(doc) + doc.save() + + return doc.as_dict() + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def rename_doc(doctype, old_name, new_name, merge=False): + """Rename document + + :param doctype: DocType of the document to be renamed + :param old_name: Current `name` of the document to be renamed + :param new_name: New `name` to be set""" + new_name = xhiveframework.rename_doc(doctype, old_name, new_name, merge=merge) + return new_name + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def submit(doc): + """Submit a document + + :param doc: JSON or dict object to be submitted remotely""" + if isinstance(doc, str): + doc = json.loads(doc) + + doc = xhiveframework.get_doc(doc) + doc.submit() + + return doc.as_dict() + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def cancel(doctype, name): + """Cancel a document + + :param doctype: DocType of the document to be cancelled + :param name: name of the document to be cancelled""" + wrapper = xhiveframework.get_doc(doctype, name) + wrapper.cancel() + + return wrapper.as_dict() + + +@xhiveframework.whitelist(methods=["DELETE", "POST"]) +def delete(doctype, name): + """Delete a remote document + + :param doctype: DocType of the document to be deleted + :param name: name of the document to be deleted""" + delete_doc(doctype, name) + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def bulk_update(docs): + """Bulk update documents + + :param docs: JSON list of documents to be updated remotely. Each document must have `docname` property""" + docs = json.loads(docs) + failed_docs = [] + for doc in docs: + doc.pop("flags", None) + try: + existing_doc = xhiveframework.get_doc(doc["doctype"], doc["docname"]) + existing_doc.update(doc) + existing_doc.save() + except Exception: + failed_docs.append({"doc": doc, "exc": xhiveframework.utils.get_traceback()}) + + return {"failed_docs": failed_docs} + + +@xhiveframework.whitelist() +def has_permission(doctype, docname, perm_type="read"): + """Returns a JSON with data whether the document has the requested permission + + :param doctype: DocType of the document to be checked + :param docname: `name` of the document to be checked + :param perm_type: one of `read`, `write`, `create`, `submit`, `cancel`, `report`. Default is `read`""" + # perm_type can be one of read, write, create, submit, cancel, report + return {"has_permission": xhiveframework.has_permission(doctype, perm_type.lower(), docname)} + + +@xhiveframework.whitelist() +def get_doc_permissions(doctype, docname): + """Returns an evaluated document permissions dict like `{"read":1, "write":1}` + + :param doctype: DocType of the document to be evaluated + :param docname: `name` of the document to be evaluated + """ + doc = xhiveframework.get_doc(doctype, docname) + return {"permissions": xhiveframework.permissions.get_doc_permissions(doc)} + + +@xhiveframework.whitelist() +def get_password(doctype, name, fieldname): + """Return a password type property. Only applicable for System Managers + + :param doctype: DocType of the document that holds the password + :param name: `name` of the document that holds the password + :param fieldname: `fieldname` of the password property + """ + xhiveframework.only_for("System Manager") + return xhiveframework.get_doc(doctype, name).get_password(fieldname) + + +@xhiveframework.whitelist() +@deprecated +def get_js(items): + """Load JS code files. Will also append translations + and extend `xhiveframework._messages` + + :param items: JSON list of paths of the js files to be loaded.""" + items = json.loads(items) + out = [] + for src in items: + src = src.strip("/").split("/") + + if ".." in src or src[0] != "assets": + xhiveframework.throw(_("Invalid file path: {0}").format("/".join(src))) + + contentpath = os.path.join(xhiveframework.local.sites_path, *src) + with open(contentpath) as srcfile: + code = xhiveframework.utils.cstr(srcfile.read()) + + out.append(code) + + return out + + +@xhiveframework.whitelist(allow_guest=True) +def get_time_zone(): + """Returns default time zone""" + return {"time_zone": xhiveframework.defaults.get_defaults().get("time_zone")} + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def attach_file( + filename=None, + filedata=None, + doctype=None, + docname=None, + folder=None, + decode_base64=False, + is_private=None, + docfield=None, +): + """Attach a file to Document + + :param filename: filename e.g. test-file.txt + :param filedata: base64 encode filedata which must be urlencoded + :param doctype: Reference DocType to attach file to + :param docname: Reference DocName to attach file to + :param folder: Folder to add File into + :param decode_base64: decode filedata from base64 encode, default is False + :param is_private: Attach file as private file (1 or 0) + :param docfield: file to attach to (optional)""" + + doc = xhiveframework.get_doc(doctype, docname) + doc.check_permission() + + file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": filename, + "attached_to_doctype": doctype, + "attached_to_name": docname, + "attached_to_field": docfield, + "folder": folder, + "is_private": is_private, + "content": filedata, + "decode": decode_base64, + } + ).save() + + if docfield and doctype: + doc.set(docfield, file.file_url) + doc.save() + + return file + + +@xhiveframework.whitelist() +def is_document_amended(doctype, docname): + if xhiveframework.permissions.has_permission(doctype): + try: + return xhiveframework.db.exists(doctype, {"amended_from": docname}) + except xhiveframework.db.InternalError: + pass + + return False + + +@xhiveframework.whitelist() +def validate_link(doctype: str, docname: str, fields=None): + if not isinstance(doctype, str): + xhiveframework.throw(_("DocType must be a string")) + + if not isinstance(docname, str): + xhiveframework.throw(_("Document Name must be a string")) + + if doctype != "DocType" and not ( + xhiveframework.has_permission(doctype, "select") or xhiveframework.has_permission(doctype, "read") + ): + xhiveframework.throw( + _("You do not have Read or Select Permissions for {}").format(xhiveframework.bold(doctype)), + xhiveframework.PermissionError, + ) + + values = xhiveframework._dict() + + if is_virtual_doctype(doctype): + try: + xhiveframework.get_doc(doctype, docname) + values.name = docname + except xhiveframework.DoesNotExistError: + xhiveframework.clear_last_message() + xhiveframework.msgprint( + _("Document {0} {1} does not exist").format(xhiveframework.bold(doctype), xhiveframework.bold(docname)), + ) + return values + + values.name = xhiveframework.db.get_value(doctype, docname, cache=True) + + fields = xhiveframework.parse_json(fields) + if not values.name or not fields: + return values + + try: + values.update(get_value(doctype, fields, docname)) + except xhiveframework.PermissionError: + xhiveframework.clear_last_message() + xhiveframework.msgprint( + _("You need {0} permission to fetch values from {1} {2}").format( + xhiveframework.bold(_("Read")), xhiveframework.bold(doctype), xhiveframework.bold(docname) + ), + title=_("Cannot Fetch Values"), + indicator="orange", + ) + + return values + + +def insert_doc(doc) -> "Document": + """Inserts document and returns parent document object with appended child document + if `doc` is child document else returns the inserted document object + + :param doc: doc to insert (dict)""" + + doc = xhiveframework._dict(doc) + if xhiveframework.is_table(doc.doctype): + if not (doc.parenttype and doc.parent and doc.parentfield): + xhiveframework.throw(_("Parenttype, Parent and Parentfield are required to insert a child record")) + + # inserting a child record + parent = xhiveframework.get_doc(doc.parenttype, doc.parent) + parent.append(doc.parentfield, doc) + parent.save() + return parent + + return xhiveframework.get_doc(doc).insert() + + +def delete_doc(doctype, name): + """Deletes document + if doctype is a child table, then deletes the child record using the parent doc + so that the parent doc's `on_update` is called + """ + + if xhiveframework.is_table(doctype): + values = xhiveframework.db.get_value(doctype, name, ["parenttype", "parent", "parentfield"]) + if not values: + raise xhiveframework.DoesNotExistError + parenttype, parent, parentfield = values + parent = xhiveframework.get_doc(parenttype, parent) + for row in parent.get(parentfield): + if row.name == name: + parent.remove(row) + parent.save() + break + else: + xhiveframework.delete_doc(doctype, name, ignore_missing=False) diff --git a/xhiveframework/commands/__init__.py b/xhiveframework/commands/__init__.py new file mode 100644 index 0000000..6e44b92 --- /dev/null +++ b/xhiveframework/commands/__init__.py @@ -0,0 +1,124 @@ +# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import cProfile +import pstats +import subprocess # nosec +import sys +from functools import wraps +from io import StringIO +from os import environ + +import click + +import xhiveframework +import xhiveframework.utils + +click.disable_unicode_literals_warning = True + + +def pass_context(f): + @wraps(f) + def _func(ctx, *args, **kwargs): + profile = ctx.obj["profile"] + if profile: + pr = cProfile.Profile() + pr.enable() + + try: + ret = f(xhiveframework._dict(ctx.obj), *args, **kwargs) + except xhiveframework.exceptions.SiteNotSpecifiedError as e: + click.secho(str(e), fg="yellow") + sys.exit(1) + except xhiveframework.exceptions.IncorrectSitePath: + site = ctx.obj.get("sites", "")[0] + click.secho(f"Site {site} does not exist!", fg="yellow") + sys.exit(1) + + if profile: + pr.disable() + s = StringIO() + ps = pstats.Stats(pr, stream=s).sort_stats("cumtime", "tottime", "ncalls") + ps.print_stats() + + # print the top-100 + for line in s.getvalue().splitlines()[:100]: + print(line) + + return ret + + return click.pass_context(_func) + + +def get_site(context, raise_err=True): + try: + return context.sites[0] + except (IndexError, TypeError): + if raise_err: + raise xhiveframework.SiteNotSpecifiedError + return None + + +def popen(command, *args, **kwargs): + output = kwargs.get("output", True) + cwd = kwargs.get("cwd") + shell = kwargs.get("shell", True) + raise_err = kwargs.get("raise_err") + env = kwargs.get("env") + if env: + env = dict(environ, **env) + + def set_low_prio(): + import psutil + + if psutil.LINUX: + psutil.Process().nice(19) + psutil.Process().ionice(psutil.IOPRIO_CLASS_IDLE) + elif psutil.WINDOWS: + psutil.Process().nice(psutil.IDLE_PRIORITY_CLASS) + psutil.Process().ionice(psutil.IOPRIO_VERYLOW) + else: + psutil.Process().nice(19) + # ionice not supported + + proc = subprocess.Popen( + command, + stdout=None if output else subprocess.PIPE, + stderr=None if output else subprocess.PIPE, + shell=shell, + cwd=cwd, + preexec_fn=set_low_prio, + env=env, + ) + + return_ = proc.wait() + + if return_ and raise_err: + raise subprocess.CalledProcessError(return_, command) + + return return_ + + +def call_command(cmd, context): + return click.Context(cmd, obj=context).forward(cmd) + + +def get_commands(): + # prevent circular imports + from .redis_utils import commands as redis_commands + from .scheduler import commands as scheduler_commands + from .site import commands as site_commands + from .translate import commands as translate_commands + from .utils import commands as utils_commands + + clickable_link = "https://xhiveframework.com/docs" + all_commands = scheduler_commands + site_commands + translate_commands + utils_commands + redis_commands + + for command in all_commands: + if not command.help: + command.help = f"Refer to {clickable_link}" + + return all_commands + + +commands = get_commands() diff --git a/xhiveframework/commands/redis_utils.py b/xhiveframework/commands/redis_utils.py new file mode 100644 index 0000000..b518719 --- /dev/null +++ b/xhiveframework/commands/redis_utils.py @@ -0,0 +1,69 @@ +import os + +import click + +import xhiveframework + + +@click.command("create-rq-users") +@click.option( + "--set-admin-password", + is_flag=True, + default=False, + help="Set new Redis admin(default user) password", +) +@click.option("--use-rq-auth", is_flag=True, default=False, help="Enable Redis authentication for sites") +def create_rq_users(set_admin_password=False, use_rq_auth=False): + """Create Redis Queue users and add to acl and app configs. + + acl config file will be used by redis server while starting the server + and app config is used by app while connecting to redis server. + """ + from xhiveframework.installer import update_site_config + from xhiveframework.utils.redis_queue import RedisQueue + + acl_file_path = os.path.abspath("../config/redis_queue.acl") + + with xhiveframework.init_site(): + acl_list, user_credentials = RedisQueue.gen_acl_list(set_admin_password=set_admin_password) + + with open(acl_file_path, "w") as f: + f.writelines([acl + "\n" for acl in acl_list]) + + sites_path = os.getcwd() + common_site_config_path = os.path.join(sites_path, "common_site_config.json") + update_site_config( + "rq_username", + user_credentials["bench"][0], + validate=False, + site_config_path=common_site_config_path, + ) + update_site_config( + "rq_password", + user_credentials["bench"][1], + validate=False, + site_config_path=common_site_config_path, + ) + update_site_config("use_rq_auth", use_rq_auth, validate=False, site_config_path=common_site_config_path) + + click.secho( + "* ACL and site configs are updated with new user credentials. " + "Please restart Redis Queue server to enable namespaces.", + fg="green", + ) + + if set_admin_password: + env_key = "RQ_ADMIN_PASWORD" + click.secho( + "* Redis admin password is successfully set up. " + "Include below line in .bashrc file for system to use", + fg="green", + ) + click.secho(f"`export {env_key}={user_credentials['default'][1]}`") + click.secho( + "NOTE: Please save the admin password as you " "can not access redis server without the password", + fg="yellow", + ) + + +commands = [create_rq_users] diff --git a/xhiveframework/commands/scheduler.py b/xhiveframework/commands/scheduler.py new file mode 100755 index 0000000..4cb44f0 --- /dev/null +++ b/xhiveframework/commands/scheduler.py @@ -0,0 +1,271 @@ +import sys + +import click + +import xhiveframework +from xhiveframework.commands import get_site, pass_context +from xhiveframework.exceptions import SiteNotSpecifiedError + + +@click.command("trigger-scheduler-event", help="Trigger a scheduler event") +@click.argument("event") +@pass_context +def trigger_scheduler_event(context, event): + import xhiveframework.utils.scheduler + + exit_code = 0 + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + try: + xhiveframework.get_doc("Scheduled Job Type", {"method": event}).execute() + except xhiveframework.DoesNotExistError: + click.secho(f"Event {event} does not exist!", fg="red") + exit_code = 1 + finally: + xhiveframework.destroy() + + if not context.sites: + raise SiteNotSpecifiedError + + sys.exit(exit_code) + + +@click.command("enable-scheduler") +@pass_context +def enable_scheduler(context): + "Enable scheduler" + import xhiveframework.utils.scheduler + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.utils.scheduler.enable_scheduler() + xhiveframework.db.commit() + print("Enabled for", site) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("disable-scheduler") +@pass_context +def disable_scheduler(context): + "Disable scheduler" + import xhiveframework.utils.scheduler + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.utils.scheduler.disable_scheduler() + xhiveframework.db.commit() + print("Disabled for", site) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("scheduler") +@click.option("--site", help="site name") +@click.argument("state", type=click.Choice(["pause", "resume", "disable", "enable", "status"])) +@click.option("--format", "-f", default="text", type=click.Choice(["json", "text"]), help="Output format") +@click.option("--verbose", "-v", is_flag=True, help="Verbose output") +@pass_context +def scheduler(context, state: str, format: str, verbose: bool = False, site: str | None = None): + """Control scheduler state.""" + import xhiveframework + from xhiveframework.utils.scheduler import is_scheduler_inactive, toggle_scheduler + + site = site or get_site(context) + + output = { + "text": "Scheduler is {status} for site {site}", + "json": '{{"status": "{status}", "site": "{site}"}}', + } + + with xhiveframework.init_site(site=site): + match state: + case "status": + xhiveframework.connect() + status = "disabled" if is_scheduler_inactive(verbose=verbose) else "enabled" + return print(output[format].format(status=status, site=site)) + case "pause" | "resume": + from xhiveframework.installer import update_site_config + + update_site_config("pause_scheduler", state == "pause") + case "enable" | "disable": + xhiveframework.connect() + toggle_scheduler(state == "enable") + xhiveframework.db.commit() + + print(output[format].format(status=f"{state}d", site=site)) + + +@click.command("set-maintenance-mode") +@click.option("--site", help="site name") +@click.argument("state", type=click.Choice(["on", "off"])) +@pass_context +def set_maintenance_mode(context, state, site=None): + """Put the site in maintenance mode for upgrades.""" + from xhiveframework.installer import update_site_config + + if not site: + site = get_site(context) + + try: + xhiveframework.init(site=site) + update_site_config("maintenance_mode", 1 if (state == "on") else 0) + + finally: + xhiveframework.destroy() + + +@click.command("doctor") # Passing context always gets a site and if there is no use site it breaks +@click.option("--site", help="site name") +@pass_context +def doctor(context, site=None): + "Get diagnostic info about background workers" + from xhiveframework.utils.doctor import doctor as _doctor + + if not site: + site = get_site(context, raise_err=False) + return _doctor(site=site) + + +@click.command("show-pending-jobs") +@click.option("--site", help="site name") +@pass_context +def show_pending_jobs(context, site=None): + "Get diagnostic info about background jobs" + from xhiveframework.utils.doctor import pending_jobs as _pending_jobs + + if not site: + site = get_site(context) + + with xhiveframework.init_site(site): + pending_jobs = _pending_jobs(site=site) + + return pending_jobs + + +@click.command("purge-jobs") +@click.option("--site", help="site name") +@click.option("--queue", default=None, help='one of "low", "default", "high') +@click.option( + "--event", + default=None, + help='one of "all", "weekly", "monthly", "hourly", "daily", "weekly_long", "daily_long"', +) +def purge_jobs(site=None, queue=None, event=None): + "Purge any pending periodic tasks, if event option is not given, it will purge everything for the site" + from xhiveframework.utils.doctor import purge_pending_jobs + + xhiveframework.init(site or "") + count = purge_pending_jobs(event=event, site=site, queue=queue) + print(f"Purged {count} jobs") + + +@click.command("schedule") +def start_scheduler(): + """Start scheduler process which is responsible for enqueueing the scheduled job types.""" + from xhiveframework.utils.scheduler import start_scheduler + + start_scheduler() + + +@click.command("worker") +@click.option( + "--queue", + type=str, + help="Queue to consume from. Multiple queues can be specified using comma-separated string. If not specified all queues are consumed.", +) +@click.option("--quiet", is_flag=True, default=False, help="Hide Log Outputs") +@click.option("-u", "--rq-username", default=None, help="Redis ACL user") +@click.option("-p", "--rq-password", default=None, help="Redis ACL user password") +@click.option("--burst", is_flag=True, default=False, help="Run Worker in Burst mode.") +@click.option( + "--strategy", + required=False, + type=click.Choice(["round_robin", "random"]), + help="Dequeuing strategy to use", +) +def start_worker(queue, quiet=False, rq_username=None, rq_password=None, burst=False, strategy=None): + """Start a backgrond worker""" + from xhiveframework.utils.background_jobs import start_worker + + start_worker( + queue, + quiet=quiet, + rq_username=rq_username, + rq_password=rq_password, + burst=burst, + strategy=strategy, + ) + + +@click.command("worker-pool") +@click.option( + "--queue", + type=str, + help="Queue to consume from. Multiple queues can be specified using comma-separated string. If not specified all queues are consumed.", +) +@click.option("--num-workers", type=int, default=2, help="Number of workers to spawn in pool.") +@click.option("--quiet", is_flag=True, default=False, help="Hide Log Outputs") +@click.option("--burst", is_flag=True, default=False, help="Run Worker in Burst mode.") +def start_worker_pool(queue, quiet=False, num_workers=2, burst=False): + """Start a backgrond worker""" + from xhiveframework.utils.background_jobs import start_worker_pool + + start_worker_pool( + queue=queue, + quiet=quiet, + burst=burst, + num_workers=num_workers, + ) + + +@click.command("ready-for-migration") +@click.option("--site", help="site name") +@pass_context +def ready_for_migration(context, site=None): + from xhiveframework.utils.doctor import any_job_pending + + if not site: + site = get_site(context) + + try: + xhiveframework.init(site=site) + pending_jobs = any_job_pending(site=site) + + if pending_jobs: + print(f"NOT READY for migration: site {site} has pending background jobs") + sys.exit(1) + + else: + print(f"READY for migration: site {site} does not have any background jobs") + return 0 + + finally: + xhiveframework.destroy() + + +commands = [ + disable_scheduler, + doctor, + enable_scheduler, + purge_jobs, + ready_for_migration, + scheduler, + set_maintenance_mode, + show_pending_jobs, + start_scheduler, + start_worker, + start_worker_pool, + trigger_scheduler_event, +] diff --git a/xhiveframework/commands/site.py b/xhiveframework/commands/site.py new file mode 100644 index 0000000..3fb59ea --- /dev/null +++ b/xhiveframework/commands/site.py @@ -0,0 +1,1543 @@ +# imports - standard imports +import os +import shutil +import sys + +# imports - third party imports +import click + +# imports - module imports +import xhiveframework +from xhiveframework.commands import get_site, pass_context +from xhiveframework.exceptions import SiteNotSpecifiedError + + +@click.command("new-site") +@click.argument("site") +@click.option("--db-name", help="Database name") +@click.option("--db-password", help="Database password") +@click.option( + "--db-type", + default="mariadb", + type=click.Choice(["mariadb", "postgres"]), + help='Optional "postgres" or "mariadb". Default is "mariadb"', +) +@click.option("--db-host", help="Database Host") +@click.option("--db-port", type=int, help="Database Port") +@click.option( + "--db-root-username", + "--mariadb-root-username", + help='Root username for MariaDB or PostgreSQL, Default is "root"', +) +@click.option("--db-root-password", "--mariadb-root-password", help="Root password for MariaDB or PostgreSQL") +@click.option( + "--no-mariadb-socket", + is_flag=True, + default=False, + help="Set MariaDB host to % and use TCP/IP Socket instead of using the UNIX Socket", +) +@click.option("--admin-password", help="Administrator password for new site", default=None) +@click.option("--verbose", is_flag=True, default=False, help="Verbose") +@click.option("--force", help="Force restore if site/database already exists", is_flag=True, default=False) +@click.option("--source-sql", "--source_sql", help="Initiate database with a SQL file") +@click.option("--install-app", multiple=True, help="Install app after installation") +@click.option("--set-default", is_flag=True, default=False, help="Set the new site as default site") +def new_site( + site, + db_root_username=None, + db_root_password=None, + admin_password=None, + verbose=False, + source_sql=None, + force=None, + no_mariadb_socket=False, + install_app=None, + db_name=None, + db_password=None, + db_type=None, + db_host=None, + db_port=None, + set_default=False, +): + "Create a new site" + from xhiveframework.installer import _new_site + + xhiveframework.init(site=site, new_site=True) + + _new_site( + db_name, + site, + db_root_username=db_root_username, + db_root_password=db_root_password, + admin_password=admin_password, + verbose=verbose, + install_apps=install_app, + source_sql=source_sql, + force=force, + no_mariadb_socket=no_mariadb_socket, + db_password=db_password, + db_type=db_type, + db_host=db_host, + db_port=db_port, + ) + + if set_default: + use(site) + + +@click.command("restore") +@click.argument("sql-file-path") +@click.option( + "--db-root-username", + "--mariadb-root-username", + help='Root username for MariaDB or PostgreSQL, Default is "root"', +) +@click.option("--db-root-password", "--mariadb-root-password", help="Root password for MariaDB or PostgreSQL") +@click.option("--db-name", help="Database name for site in case it is a new one") +@click.option("--admin-password", help="Administrator password for new site") +@click.option("--install-app", multiple=True, help="Install app after installation") +@click.option("--with-public-files", help="Restores the public files of the site, given path to its tar file") +@click.option( + "--with-private-files", + help="Restores the private files of the site, given path to its tar file", +) +@click.option( + "--force", + is_flag=True, + default=False, + help="Ignore the validations and downgrade warnings. This action is not recommended", +) +@click.option("--encryption-key", help="Backup encryption key") +@pass_context +def restore( + context, + sql_file_path, + encryption_key=None, + db_root_username=None, + db_root_password=None, + db_name=None, + verbose=None, + install_app=None, + admin_password=None, + force=None, + with_public_files=None, + with_private_files=None, +): + "Restore site database from an sql file" + + from xhiveframework.utils.synchronization import filelock + + site = get_site(context) + xhiveframework.init(site=site) + + with filelock("site_restore", timeout=1): + _restore( + site=site, + sql_file_path=sql_file_path, + encryption_key=encryption_key, + db_root_username=db_root_username, + db_root_password=db_root_password, + verbose=context.verbose or verbose, + install_app=install_app, + admin_password=admin_password, + force=context.force or force, + with_public_files=with_public_files, + with_private_files=with_private_files, + ) + + +def _restore( + *, + site=None, + sql_file_path=None, + encryption_key=None, + db_root_username=None, + db_root_password=None, + verbose=None, + install_app=None, + admin_password=None, + force=None, + with_public_files=None, + with_private_files=None, +): + from xhiveframework.installer import extract_files + from xhiveframework.utils.backups import decrypt_backup, get_or_generate_backup_encryption_key + + err, out = xhiveframework.utils.execute_in_shell(f"file {sql_file_path}", check_exit_code=True) + if err: + click.secho("Failed to detect type of backup file", fg="red") + sys.exit(1) + + if "cipher" in out.decode().split(":")[-1].strip(): + if encryption_key: + click.secho("Encrypted backup file detected. Decrypting using provided key.", fg="yellow") + + else: + click.secho("Encrypted backup file detected. Decrypting using site config.", fg="yellow") + encryption_key = get_or_generate_backup_encryption_key() + + with decrypt_backup(sql_file_path, encryption_key): + # Rollback on unsuccessful decryption + if not os.path.exists(sql_file_path): + click.secho("Decryption failed. Please provide a valid key and try again.", fg="red") + sys.exit(1) + + restore_backup( + sql_file_path, + site, + db_root_username, + db_root_password, + verbose, + install_app, + admin_password, + force, + ) + else: + restore_backup( + sql_file_path, + site, + db_root_username, + db_root_password, + verbose, + install_app, + admin_password, + force, + ) + + # Extract public and/or private files to the restored site, if user has given the path + if with_public_files: + # Decrypt data if there is a Key + if encryption_key: + with decrypt_backup(with_public_files, encryption_key): + public = extract_files(site, with_public_files) + else: + public = extract_files(site, with_public_files) + + # Removing temporarily created file + os.remove(public) + + if with_private_files: + # Decrypt data if there is a Key + if encryption_key: + with decrypt_backup(with_private_files, encryption_key): + private = extract_files(site, with_private_files) + else: + private = extract_files(site, with_private_files) + + # Removing temporarily created file + os.remove(private) + + success_message = "Site {} has been restored{}".format( + site, " with files" if (with_public_files or with_private_files) else "" + ) + click.secho(success_message, fg="green") + + +def restore_backup( + sql_file_path: str, + site, + db_root_username, + db_root_password, + verbose, + install_app, + admin_password, + force, +): + from xhiveframework.installer import _new_site, is_downgrade, is_partial, validate_database_sql + + if is_partial(sql_file_path): + click.secho( + "Partial Backup file detected. You cannot use a partial file to restore a XhiveFramework site.", + fg="red", + ) + click.secho( + "Use `bench partial-restore` to restore a partial backup to an existing site.", + fg="yellow", + ) + sys.exit(1) + + # Check if the backup is of an older version of xhiveframework and the user hasn't specified force + if is_downgrade(sql_file_path, verbose=True) and not force: + warn_message = ( + "This is not recommended and may lead to unexpected behaviour. " "Do you want to continue anyway?" + ) + click.confirm(warn_message, abort=True) + + # Validate the sql file + validate_database_sql(sql_file_path, _raise=not force) + + try: + _new_site( + xhiveframework.conf.db_name, + site, + db_root_username=db_root_username, + db_root_password=db_root_password, + admin_password=admin_password, + verbose=verbose, + install_apps=install_app, + source_sql=sql_file_path, + force=True, + db_type=xhiveframework.conf.db_type, + ) + + except Exception as err: + print(err.args[1]) + sys.exit(1) + + +@click.command("partial-restore") +@click.argument("sql-file-path") +@click.option("--verbose", "-v", is_flag=True) +@click.option("--encryption-key", help="Backup encryption key") +@pass_context +def partial_restore(context, sql_file_path, verbose, encryption_key=None): + from xhiveframework.installer import is_partial, partial_restore + from xhiveframework.utils.backups import decrypt_backup, get_or_generate_backup_encryption_key + + if not os.path.exists(sql_file_path): + print("Invalid path", sql_file_path) + sys.exit(1) + + site = get_site(context) + verbose = context.verbose or verbose + xhiveframework.init(site=site) + xhiveframework.connect(site=site) + err, out = xhiveframework.utils.execute_in_shell(f"file {sql_file_path}", check_exit_code=True) + if err: + click.secho("Failed to detect type of backup file", fg="red") + sys.exit(1) + + if "cipher" in out.decode().split(":")[-1].strip(): + if encryption_key: + click.secho("Encrypted backup file detected. Decrypting using provided key.", fg="yellow") + key = encryption_key + + else: + click.secho("Encrypted backup file detected. Decrypting using site config.", fg="yellow") + key = get_or_generate_backup_encryption_key() + + with decrypt_backup(sql_file_path, key): + if not is_partial(sql_file_path): + click.secho( + "Full backup file detected. Use `bench restore` to restore a XhiveFramework Site.", + fg="red", + ) + sys.exit(1) + + partial_restore(sql_file_path, verbose) + + # Rollback on unsuccessful decryption + if not os.path.exists(sql_file_path): + click.secho("Decryption failed. Please provide a valid key and try again.", fg="red") + sys.exit(1) + + else: + if not is_partial(sql_file_path): + click.secho( + "Full backup file detected. Use `bench restore` to restore a XhiveFramework Site.", + fg="red", + ) + sys.exit(1) + + partial_restore(sql_file_path, verbose) + xhiveframework.destroy() + + +@click.command("reinstall") +@click.option("--admin-password", help="Administrator Password for reinstalled site") +@click.option( + "--db-root-username", + "--mariadb-root-username", + help='Root username for MariaDB or PostgreSQL, Default is "root"', +) +@click.option("--db-root-password", "--mariadb-root-password", help="Root password for MariaDB or PostgreSQL") +@click.option("--yes", is_flag=True, default=False, help="Pass --yes to skip confirmation") +@pass_context +def reinstall(context, admin_password=None, db_root_username=None, db_root_password=None, yes=False): + "Reinstall site ie. wipe all data and start over" + site = get_site(context) + _reinstall(site, admin_password, db_root_username, db_root_password, yes, verbose=context.verbose) + + +def _reinstall( + site, + admin_password=None, + db_root_username=None, + db_root_password=None, + yes=False, + verbose=False, +): + from xhiveframework.installer import _new_site + from xhiveframework.utils.synchronization import filelock + + if not yes: + click.confirm("This will wipe your database. Are you sure you want to reinstall?", abort=True) + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.clear_cache() + installed = xhiveframework.get_installed_apps() + xhiveframework.clear_cache() + except Exception: + installed = [] + finally: + if xhiveframework.db: + xhiveframework.db.close() + xhiveframework.destroy() + + xhiveframework.init(site=site) + + _new_site( + xhiveframework.conf.db_name, + site, + verbose=verbose, + force=True, + reinstall=True, + install_apps=installed, + db_root_username=db_root_username, + db_root_password=db_root_password, + admin_password=admin_password, + ) + + +@click.command("install-app") +@click.argument("apps", nargs=-1) +@click.option("--force", is_flag=True, default=False) +@pass_context +def install_app(context, apps, force=False): + "Install a new app to site, supports multiple apps" + from xhiveframework.installer import install_app as _install_app + from xhiveframework.utils.synchronization import filelock + + exit_code = 0 + + if not context.sites: + raise SiteNotSpecifiedError + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + + with filelock("install_app", timeout=1): + for app in apps: + try: + _install_app(app, verbose=context.verbose, force=force) + except xhiveframework.IncompatibleApp as err: + err_msg = f":\n{err}" if str(err) else "" + print(f"App {app} is Incompatible with Site {site}{err_msg}") + exit_code = 1 + except Exception as err: + err_msg = f": {err!s}\n{xhiveframework.get_traceback(with_context=True)}" + print(f"An error occurred while installing {app}{err_msg}") + exit_code = 1 + + if not exit_code: + xhiveframework.db.commit() + + xhiveframework.destroy() + + sys.exit(exit_code) + + +@click.command("list-apps") +@click.option("--format", "-f", type=click.Choice(["text", "json"]), default="text") +@pass_context +def list_apps(context, format): + """ + List apps in site. + """ + + summary_dict = {} + + def format_app(app): + name_len = max(len(app.app_name) for app in apps) + ver_len = max(len(app.app_version) for app in apps) + template = f"{{0:{name_len}}} {{1:{ver_len}}} {{2}}" + return template.format(app.app_name, app.app_version, app.git_branch) + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + site_title = click.style(f"{site}", fg="green") if len(context.sites) > 1 else "" + installed_apps_info = [] + + apps = xhiveframework.get_single("Installed Applications").installed_applications + if apps: + installed_apps_info.extend(format_app(app) for app in apps) + else: + installed_apps_info.extend(xhiveframework.get_installed_apps()) + + installed_apps_info_str = "\n".join(installed_apps_info) + summary = f"{site_title}\n{installed_apps_info_str}\n" + summary_dict[site] = [app.app_name for app in apps] + + if format == "text" and installed_apps_info and summary: + print(summary) + + xhiveframework.destroy() + + if format == "json": + click.echo(xhiveframework.as_json(summary_dict)) + + +@click.command("add-database-index") +@click.option("--doctype", help="DocType on which index needs to be added") +@click.option( + "--column", + multiple=True, + help="Column to index. Multiple columns will create multi-column index in given order. To create a multiple, single column index, execute the command multiple times.", +) +@pass_context +def add_db_index(context, doctype, column): + "Adds a new DB index and creates a property setter to persist it." + from xhiveframework.custom.doctype.property_setter.property_setter import make_property_setter + + columns = column # correct naming + for site in context.sites: + xhiveframework.connect(site=site) + try: + xhiveframework.db.add_index(doctype, columns) + if len(columns) == 1: + make_property_setter( + doctype, + columns[0], + property="search_index", + value="1", + property_type="Check", + for_doctype=False, # Applied on docfield + ) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("describe-database-table") +@click.option("--doctype", help="DocType to describe") +@click.option( + "--column", + multiple=True, + help="Explicitly fetch accurate cardinality from table data. This can be quite slow on large tables.", +) +@pass_context +def describe_database_table(context, doctype, column): + """Describes various statistics about the table. + + This is useful to build integration like + This includes: + 1. Schema + 2. Indexes + 3. stats - total count of records + 4. if column is specified then extra stats are generated for column: + Distinct values count in column + """ + import json + + for site in context.sites: + xhiveframework.connect(site=site) + try: + data = _extract_table_stats(doctype, column) + # NOTE: Do not print anything else in this to avoid clobbering the output. + print(json.dumps(data, indent=2)) + finally: + xhiveframework.destroy() + + if not context.sites: + raise SiteNotSpecifiedError + + +def _extract_table_stats(doctype: str, columns: list[str]) -> dict: + from xhiveframework.utils import cint, cstr, get_table_name + + def sql_bool(val): + return cstr(val).lower() in ("yes", "1", "true") + + table = get_table_name(doctype, wrap_in_backticks=True) + + schema = [] + for field in xhiveframework.db.sql(f"describe {table}", as_dict=True): + schema.append( + { + "column": field["Field"], + "type": field["Type"], + "is_nullable": sql_bool(field["Null"]), + "default": field["Default"], + } + ) + + def update_cardinality(column, value): + for col in schema: + if col["column"] == column: + col["cardinality"] = value + break + + indexes = [] + for idx in xhiveframework.db.sql(f"show index from {table}", as_dict=True): + indexes.append( + { + "unique": not sql_bool(idx["Non_unique"]), + "cardinality": idx["Cardinality"], + "name": idx["Key_name"], + "sequence": idx["Seq_in_index"], + "nullable": sql_bool(idx["Null"]), + "column": idx["Column_name"], + "type": idx["Index_type"], + } + ) + if idx["Seq_in_index"] == 1: + update_cardinality(idx["Column_name"], idx["Cardinality"]) + + total_rows = cint( + xhiveframework.db.sql( + f"""select table_rows + from information_schema.tables + where table_name = 'tab{doctype}'""" + )[0][0] + ) + + # fetch accurate cardinality for columns by query. WARN: This can take a lot of time. + for column in columns: + cardinality = xhiveframework.db.sql(f"select count(distinct {column}) from {table}")[0][0] + update_cardinality(column, cardinality) + + return { + "table_name": table.strip("`"), + "total_rows": total_rows, + "schema": schema, + "indexes": indexes, + } + + +@click.command("add-system-manager") +@click.argument("email") +@click.option("--first-name") +@click.option("--last-name") +@click.option("--password") +@click.option("--send-welcome-email", default=False, is_flag=True) +@pass_context +def add_system_manager(context, email, first_name, last_name, send_welcome_email, password): + "Add a new system manager to a site" + import xhiveframework.utils.user + + for site in context.sites: + xhiveframework.connect(site=site) + try: + xhiveframework.utils.user.add_system_manager(email, first_name, last_name, send_welcome_email, password) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("add-user") +@click.argument("email") +@click.option("--first-name") +@click.option("--last-name") +@click.option("--password") +@click.option("--user-type") +@click.option("--add-role", multiple=True) +@click.option("--send-welcome-email", default=False, is_flag=True) +@pass_context +def add_user_for_sites( + context, email, first_name, last_name, user_type, send_welcome_email, password, add_role +): + "Add user to a site" + import xhiveframework.utils.user + + for site in context.sites: + xhiveframework.connect(site=site) + try: + add_new_user(email, first_name, last_name, user_type, send_welcome_email, password, add_role) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("disable-user") +@click.argument("email") +@pass_context +def disable_user(context, email): + """Disable a user account on site.""" + site = get_site(context) + with xhiveframework.init_site(site): + xhiveframework.connect() + user = xhiveframework.get_doc("User", email) + user.enabled = 0 + user.save(ignore_permissions=True) + xhiveframework.db.commit() + + +@click.command("migrate") +@click.option("--skip-failing", is_flag=True, help="Skip patches that fail to run") +@click.option("--skip-search-index", is_flag=True, help="Skip search indexing for web documents") +@pass_context +def migrate(context, skip_failing=False, skip_search_index=False): + "Run patches, sync schema and rebuild files/translations" + from traceback_with_variables import activate_by_import + + from xhiveframework.migrate import SiteMigration + + for site in context.sites: + click.secho(f"Migrating {site}", fg="green") + try: + SiteMigration( + skip_failing=skip_failing, + skip_search_index=skip_search_index, + ).run(site=site) + finally: + print() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("migrate-to") +@click.argument("xhiveframework_provider") +@pass_context +def migrate_to(context, xhiveframework_provider): + "Migrates site to the specified provider" + from xhiveframework.integrations.xhiveframework_providers import migrate_to + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + migrate_to(site, xhiveframework_provider) + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("run-patch") +@click.argument("module") +@click.option("--force", is_flag=True) +@pass_context +def run_patch(context, module, force): + "Run a particular patch" + import xhiveframework.modules.patch_handler + + for site in context.sites: + xhiveframework.init(site=site) + try: + xhiveframework.connect() + xhiveframework.modules.patch_handler.run_single(module, force=force or context.force) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("reload-doc") +@click.argument("module") +@click.argument("doctype") +@click.argument("docname") +@pass_context +def reload_doc(context, module, doctype, docname): + "Reload schema for a DocType" + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.reload_doc(module, doctype, docname, force=context.force) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("reload-doctype") +@click.argument("doctype") +@pass_context +def reload_doctype(context, doctype): + "Reload schema for a DocType" + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.reload_doctype(doctype, force=context.force) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("add-to-hosts") +@pass_context +def add_to_hosts(context): + "Add site to hosts" + for site in context.sites: + xhiveframework.commands.popen(f"echo 127.0.0.1\t{site} | sudo tee -a /etc/hosts") + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("use") +@click.argument("site") +def _use(site, sites_path="."): + "Set a default site" + use(site, sites_path=sites_path) + + +def use(site, sites_path="."): + from xhiveframework.installer import update_site_config + + if os.path.exists(os.path.join(sites_path, site)): + sites_path = os.getcwd() + conifg = os.path.join(sites_path, "common_site_config.json") + update_site_config("default_site", site, validate=False, site_config_path=conifg) + print(f"Current Site set to {site}") + else: + print(f"Site {site} does not exist") + + +@click.command("backup") +@click.option("--with-files", default=False, is_flag=True, help="Take backup with files") +@click.option( + "--include", + "--only", + "-i", + default="", + type=str, + help="Specify the DocTypes to backup seperated by commas", +) +@click.option( + "--exclude", + "-e", + default="", + type=str, + help="Specify the DocTypes to not backup seperated by commas", +) +@click.option("--backup-path", default=None, help="Set path for saving all the files in this operation") +@click.option("--backup-path-db", default=None, help="Set path for saving database file") +@click.option("--backup-path-files", default=None, help="Set path for saving public file") +@click.option("--backup-path-private-files", default=None, help="Set path for saving private file") +@click.option("--backup-path-conf", default=None, help="Set path for saving config file") +@click.option( + "--ignore-backup-conf", + default=False, + is_flag=True, + help="Ignore excludes/includes set in config", +) +@click.option("--verbose", default=False, is_flag=True, help="Add verbosity") +@click.option("--compress", default=False, is_flag=True, help="Compress private and public files") +@click.option("--old-backup-metadata", default=False, is_flag=True, help="Use older backup metadata") +@pass_context +def backup( + context, + with_files=False, + backup_path=None, + backup_path_db=None, + backup_path_files=None, + backup_path_private_files=None, + backup_path_conf=None, + ignore_backup_conf=False, + verbose=False, + compress=False, + include="", + exclude="", + old_backup_metadata=False, +): + "Backup" + + from xhiveframework.utils.backups import scheduled_backup + + verbose = verbose or context.verbose + exit_code = 0 + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + odb = scheduled_backup( + ignore_files=not with_files, + backup_path=backup_path, + backup_path_db=backup_path_db, + backup_path_files=backup_path_files, + backup_path_private_files=backup_path_private_files, + backup_path_conf=backup_path_conf, + ignore_conf=ignore_backup_conf, + include_doctypes=include, + exclude_doctypes=exclude, + compress=compress, + verbose=verbose, + force=True, + old_backup_metadata=old_backup_metadata, + ) + except Exception: + click.secho( + f"Backup failed for Site {site}. Database or site_config.json may be corrupted", + fg="red", + ) + if verbose: + print(xhiveframework.get_traceback(with_context=True)) + exit_code = 1 + continue + if xhiveframework.get_system_settings("encrypt_backup") and xhiveframework.get_site_config().encryption_key: + click.secho( + "Backup encryption is turned on. Please note the backup encryption key.", + fg="yellow", + ) + + odb.print_summary() + click.secho( + "Backup for Site {} has been successfully completed{}".format( + site, " with files" if with_files else "" + ), + fg="green", + ) + xhiveframework.destroy() + + if not context.sites: + raise SiteNotSpecifiedError + + sys.exit(exit_code) + + +@click.command("remove-from-installed-apps") +@click.argument("app") +@pass_context +def remove_from_installed_apps(context, app): + "Remove app from site's installed-apps list" + from xhiveframework.installer import remove_from_installed_apps + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + remove_from_installed_apps(app) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("uninstall-app") +@click.argument("app") +@click.option( + "--yes", + "-y", + help="To bypass confirmation prompt for uninstalling the app", + is_flag=True, + default=False, +) +@click.option("--dry-run", help="List all doctypes that will be deleted", is_flag=True, default=False) +@click.option("--no-backup", help="Do not backup the site", is_flag=True, default=False) +@click.option("--force", help="Force remove app from site", is_flag=True, default=False) +@pass_context +def uninstall(context, app, dry_run, yes, no_backup, force): + "Remove app and linked modules from site" + from xhiveframework.installer import remove_app + from xhiveframework.utils.synchronization import filelock + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + with filelock("uninstall_app"): + remove_app(app_name=app, dry_run=dry_run, yes=yes, no_backup=no_backup, force=force) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("drop-site") +@click.argument("site") +@click.option( + "--db-root-username", + "--mariadb-root-username", + "--root-login", + help='Root username for MariaDB or PostgreSQL, Default is "root"', +) +@click.option( + "--db-root-password", + "--mariadb-root-password", + "--root-password", + help="Root password for MariaDB or PostgreSQL", +) +@click.option("--archived-sites-path") +@click.option("--no-backup", is_flag=True, default=False) +@click.option("--force", help="Force drop-site even if an error is encountered", is_flag=True, default=False) +def drop_site( + site, + db_root_username="root", + db_root_password=None, + archived_sites_path=None, + force=False, + no_backup=False, +): + """Remove a site from database and filesystem.""" + _drop_site(site, db_root_username, db_root_password, archived_sites_path, force, no_backup) + + +def _drop_site( + site, + db_root_username=None, + db_root_password=None, + archived_sites_path=None, + force=False, + no_backup=False, +): + from xhiveframework.database import drop_user_and_database + from xhiveframework.utils.backups import scheduled_backup + + xhiveframework.init(site=site) + xhiveframework.connect() + + try: + if not no_backup: + click.secho(f"Taking backup of {site}", fg="green") + odb = scheduled_backup(ignore_files=False, ignore_conf=True, force=True, verbose=True) + odb.print_summary() + except Exception as err: + if force: + pass + else: + messages = [ + "=" * 80, + f"Error: The operation has stopped because backup of {site}'s database failed.", + f"Reason: {err!s}\n", + "Fix the issue and try again.", + f"Hint: Use 'bench drop-site {site} --force' to force the removal of {site}", + ] + click.echo("\n".join(messages)) + sys.exit(1) + + click.secho("Dropping site database and user", fg="green") + drop_user_and_database(xhiveframework.conf.db_name, db_root_username, db_root_password) + + archived_sites_path = archived_sites_path or os.path.join( + xhiveframework.utils.get_bench_path(), "archived", "sites" + ) + archived_sites_path = os.path.realpath(archived_sites_path) + + click.secho(f"Moving site to archive under {archived_sites_path}", fg="green") + os.makedirs(archived_sites_path, exist_ok=True) + move(archived_sites_path, site) + + +def move(dest_dir, site): + if not os.path.isdir(dest_dir): + raise Exception("destination is not a directory or does not exist") + + xhiveframework.init(site) + old_path = xhiveframework.utils.get_site_path() + new_path = os.path.join(dest_dir, site) + + # check if site dump of same name already exists + site_dump_exists = True + count = 0 + while site_dump_exists: + final_new_path = new_path + (count and str(count) or "") + site_dump_exists = os.path.exists(final_new_path) + count = int(count or 0) + 1 + + shutil.move(old_path, final_new_path) + xhiveframework.destroy() + return final_new_path + + +@click.command("set-password") +@click.argument("user") +@click.argument("password", required=False) +@click.option("--logout-all-sessions", help="Log out from all sessions", is_flag=True, default=False) +@pass_context +def set_password(context, user, password=None, logout_all_sessions=False): + "Set password for a user on a site" + if not context.sites: + raise SiteNotSpecifiedError + + for site in context.sites: + set_user_password(site, user, password, logout_all_sessions) + + +@click.command("set-admin-password") +@click.argument("admin-password", required=False) +@click.option("--logout-all-sessions", help="Log out from all sessions", is_flag=True, default=False) +@pass_context +def set_admin_password(context, admin_password=None, logout_all_sessions=False): + "Set Administrator password for a site" + if not context.sites: + raise SiteNotSpecifiedError + + for site in context.sites: + set_user_password(site, "Administrator", admin_password, logout_all_sessions) + + +def set_user_password(site, user, password, logout_all_sessions=False): + import getpass + + from xhiveframework.utils.password import update_password + + try: + xhiveframework.init(site=site) + + while not password: + password = getpass.getpass(f"{user}'s password for {site}: ") + + xhiveframework.connect() + if not xhiveframework.db.exists("User", user): + print(f"User {user} does not exist") + sys.exit(1) + + update_password(user=user, pwd=password, logout_all_sessions=logout_all_sessions) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + + +@click.command("set-last-active-for-user") +@click.option("--user", help="Setup last active date for user") +@pass_context +def set_last_active_for_user(context, user=None): + "Set users last active date to current datetime" + from xhiveframework.core.doctype.user.user import get_system_users + from xhiveframework.utils import now_datetime + + site = get_site(context) + + with xhiveframework.init_site(site): + xhiveframework.connect() + if not user: + user = get_system_users(limit=1) + if len(user) > 0: + user = user[0] + else: + return + + xhiveframework.db.set_value("User", user, "last_active", now_datetime()) + xhiveframework.db.commit() + + +@click.command("publish-realtime") +@click.argument("event") +@click.option("--message") +@click.option("--room") +@click.option("--user") +@click.option("--doctype") +@click.option("--docname") +@click.option("--after-commit") +@pass_context +def publish_realtime(context, event, message, room, user, doctype, docname, after_commit): + "Publish realtime event from bench" + from xhiveframework import publish_realtime + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + publish_realtime( + event, + message=message, + room=room, + user=user, + doctype=doctype, + docname=docname, + after_commit=after_commit, + ) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("browse") +@click.argument("site", required=False) +@click.option("--user", required=False, help="Login as user") +@pass_context +def browse(context, site, user=None): + """Opens the site on web browser""" + from xhiveframework.auth import CookieManager, LoginManager + + site = get_site(context, raise_err=False) or site + + if not site: + raise SiteNotSpecifiedError + + if site not in xhiveframework.utils.get_sites(): + click.echo(f"\nSite named {click.style(site, bold=True)} doesn't exist\n", err=True) + sys.exit(1) + + xhiveframework.init(site=site) + xhiveframework.connect() + + sid = "" + if user: + if xhiveframework.conf.developer_mode or user == "Administrator": + xhiveframework.utils.set_request(path="/") + xhiveframework.local.cookie_manager = CookieManager() + xhiveframework.local.login_manager = LoginManager() + xhiveframework.local.login_manager.login_as(user) + sid = f"/app?sid={xhiveframework.session.sid}" + else: + click.echo("Please enable developer mode to login as a user") + + url = f"{xhiveframework.utils.get_site_url(site)}{sid}" + + if user == "Administrator": + click.echo(f"Login URL: {url}") + + click.launch(url) + + +@click.command("start-recording") +@pass_context +def start_recording(context): + """Start XhiveFramework Recorder.""" + import xhiveframework.recorder + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.set_user("Administrator") + xhiveframework.recorder.start() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("stop-recording") +@pass_context +def stop_recording(context): + """Stop XhiveFramework Recorder.""" + import xhiveframework.recorder + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.set_user("Administrator") + xhiveframework.recorder.stop() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("ngrok") +@click.option("--bind-tls", is_flag=True, default=False, help="Returns a reference to the https tunnel.") +@click.option( + "--use-default-authtoken", + is_flag=True, + default=False, + help="Use the auth token present in ngrok's config.", +) +@pass_context +def start_ngrok(context, bind_tls, use_default_authtoken): + """Start a ngrok tunnel to your local development server.""" + from pyngrok import ngrok + + site = get_site(context) + xhiveframework.init(site=site) + + ngrok_authtoken = xhiveframework.conf.ngrok_authtoken + if not use_default_authtoken: + if not ngrok_authtoken: + click.echo( + f"\n{click.style('ngrok_authtoken', fg='yellow')} not found in site config.\n" + "Please register for a free ngrok account at: https://dashboard.ngrok.com/signup and place the obtained authtoken in the site config.", + ) + sys.exit(1) + + ngrok.set_auth_token(ngrok_authtoken) + + port = xhiveframework.conf.http_port or xhiveframework.conf.webserver_port + tunnel = ngrok.connect(addr=str(port), host_header=site, bind_tls=bind_tls) + print(f"Public URL: {tunnel.public_url}") + print("Inspect logs at http://127.0.0.1:4040") + + ngrok_process = ngrok.get_ngrok_process() + try: + # Block until CTRL-C or some other terminating event + ngrok_process.proc.wait() + except KeyboardInterrupt: + print("Shutting down server...") + xhiveframework.destroy() + ngrok.kill() + + +@click.command("build-search-index") +@pass_context +def build_search_index(context): + """Rebuild search index used by global search.""" + from xhiveframework.search.website_search import build_index_for_all_routes + + site = get_site(context) + if not site: + raise SiteNotSpecifiedError + + print(f"Building search index for {site}") + xhiveframework.init(site=site) + xhiveframework.connect() + try: + build_index_for_all_routes() + finally: + xhiveframework.destroy() + + +@click.command("clear-log-table") +@click.option("--doctype", required=True, type=str, help="Log DocType") +@click.option("--days", type=int, help="Keep records for days") +@click.option("--no-backup", is_flag=True, default=False, help="Do not backup the table") +@pass_context +def clear_log_table(context, doctype, days, no_backup): + """If any logtype table grows too large then clearing it with DELETE query + is not feasible in reasonable time. This command copies recent data to new + table and replaces current table with new smaller table. + + + ref: https://mariadb.com/kb/en/big-deletes/#deleting-more-than-half-a-table + """ + from xhiveframework.core.doctype.log_settings.log_settings import LOG_DOCTYPES + from xhiveframework.core.doctype.log_settings.log_settings import clear_log_table as clear_logs + from xhiveframework.utils.backups import scheduled_backup + + if not context.sites: + raise SiteNotSpecifiedError + + if doctype not in LOG_DOCTYPES: + raise xhiveframework.ValidationError(f"Unsupported logging DocType: {doctype}") + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + + if not no_backup: + scheduled_backup( + ignore_conf=False, + include_doctypes=doctype, + ignore_files=True, + force=True, + ) + click.echo(f"Backed up {doctype}") + + try: + click.echo(f"Copying {doctype} records from last {days} days to temporary table.") + clear_logs(doctype, days=days) + except Exception as e: + click.echo(f"Log cleanup for {doctype} failed:\n{e}") + sys.exit(1) + else: + click.secho(f"Cleared {doctype} records older than {days} days", fg="green") + + +@click.command("trim-database") +@click.option("--dry-run", is_flag=True, default=False, help="Show what would be deleted") +@click.option("--format", "-f", default="text", type=click.Choice(["json", "text"]), help="Output format") +@click.option("--no-backup", is_flag=True, default=False, help="Do not backup the site") +@click.option( + "--yes", + "-y", + help="To bypass confirmation prompt.", + is_flag=True, + default=False, +) +@pass_context +def trim_database(context, dry_run, format, no_backup, yes=False): + """Remove database tables for deleted DocTypes.""" + if not context.sites: + raise SiteNotSpecifiedError + + from xhiveframework.utils.backups import scheduled_backup + + ALL_DATA = {} + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + + TABLES_TO_DROP = [] + STANDARD_TABLES = get_standard_tables() + information_schema = xhiveframework.qb.Schema("information_schema") + table_name = xhiveframework.qb.Field("table_name").as_("name") + + database_tables: list[str] = ( + xhiveframework.qb.from_(information_schema.tables) + .select(table_name) + .where(information_schema.tables.table_schema == xhiveframework.conf.db_name) + .where(information_schema.tables.table_type == "BASE TABLE") + .run(pluck=True) + ) + doctype_tables = xhiveframework.get_all("DocType", pluck="name") + + for table_name in database_tables: + if not table_name.startswith("tab"): + continue + if not (table_name.replace("tab", "", 1) in doctype_tables or table_name in STANDARD_TABLES): + TABLES_TO_DROP.append(table_name) + + if not TABLES_TO_DROP: + if format == "text": + click.secho(f"{site}: No ghost tables", fg="green") + else: + if format == "text": + print(f"{site}: Following tables will be dropped:") + print("\n".join(f"* {dt}" for dt in TABLES_TO_DROP)) + + if dry_run: + continue + + if not yes: + click.confirm("Do you want to continue?", abort=True) + + if not no_backup: + if format == "text": + print(f"Backing Up Tables: {', '.join(TABLES_TO_DROP)}") + + odb = scheduled_backup( + ignore_conf=False, + include_doctypes=",".join(x.replace("tab", "", 1) for x in TABLES_TO_DROP), + ignore_files=True, + force=True, + ) + if format == "text": + odb.print_summary() + print("\nTrimming Database") + + for table in TABLES_TO_DROP: + if format == "text": + print(f"* Dropping Table '{table}'...") + xhiveframework.db.sql_ddl(f"drop table `{table}`") + + ALL_DATA[xhiveframework.local.site] = TABLES_TO_DROP + xhiveframework.destroy() + + if format == "json": + import json + + print(json.dumps(ALL_DATA, indent=1)) + + +def get_standard_tables(): + import re + + tables = [] + sql_file = os.path.join( + "..", + "apps", + "xhiveframework", + "xhiveframework", + "database", + xhiveframework.conf.db_type, + f"framework_{xhiveframework.conf.db_type}.sql", + ) + content = open(sql_file).read().splitlines() + + for line in content: + table_found = re.search(r"""CREATE TABLE ("|`)(.*)?("|`) \(""", line) + if table_found: + tables.append(table_found.group(2)) + + return tables + + +@click.command("trim-tables") +@click.option("--dry-run", is_flag=True, default=False, help="Show what would be deleted") +@click.option("--format", "-f", default="table", type=click.Choice(["json", "table"]), help="Output format") +@click.option("--no-backup", is_flag=True, default=False, help="Do not backup the site") +@pass_context +def trim_tables(context, dry_run, format, no_backup): + """Remove columns from tables where fields are deleted from doctypes.""" + if not context.sites: + raise SiteNotSpecifiedError + + from xhiveframework.model.meta import trim_tables + from xhiveframework.utils.backups import scheduled_backup + + for site in context.sites: + xhiveframework.init(site=site) + xhiveframework.connect() + + if not (no_backup or dry_run): + click.secho(f"Taking backup for {xhiveframework.local.site}", fg="green") + odb = scheduled_backup(ignore_files=False, force=True) + odb.print_summary() + + try: + trimmed_data = trim_tables(dry_run=dry_run, quiet=format == "json") + + if format == "table" and not dry_run: + click.secho(f"The following data have been removed from {xhiveframework.local.site}", fg="green") + + handle_data(trimmed_data, format=format) + finally: + xhiveframework.destroy() + + +def handle_data(data: dict, format="json"): + if format == "json": + import json + + print(json.dumps({xhiveframework.local.site: data}, indent=1, sort_keys=True)) + else: + from xhiveframework.utils.commands import render_table + + data = [["DocType", "Fields"]] + [[table, ", ".join(columns)] for table, columns in data.items()] + render_table(data) + + +def add_new_user( + email, + first_name=None, + last_name=None, + user_type="System User", + send_welcome_email=False, + password=None, + role=None, +): + user = xhiveframework.new_doc("User") + user.update( + { + "name": email, + "email": email, + "enabled": 1, + "first_name": first_name or email, + "last_name": last_name, + "user_type": user_type, + "send_welcome_email": 1 if send_welcome_email else 0, + } + ) + user.insert() + user.add_roles(*role) + if password: + from xhiveframework.utils.password import update_password + + update_password(user=user.name, pwd=password) + + +commands = [ + add_system_manager, + add_user_for_sites, + add_db_index, + describe_database_table, + backup, + drop_site, + install_app, + list_apps, + migrate, + migrate_to, + new_site, + reinstall, + reload_doc, + reload_doctype, + remove_from_installed_apps, + restore, + run_patch, + set_password, + set_admin_password, + uninstall, + disable_user, + _use, + set_last_active_for_user, + publish_realtime, + browse, + start_recording, + stop_recording, + add_to_hosts, + start_ngrok, + build_search_index, + partial_restore, + trim_tables, + trim_database, + clear_log_table, +] diff --git a/xhiveframework/commands/translate.py b/xhiveframework/commands/translate.py new file mode 100644 index 0000000..f31593c --- /dev/null +++ b/xhiveframework/commands/translate.py @@ -0,0 +1,123 @@ +import click + +from xhiveframework.commands import get_site, pass_context +from xhiveframework.exceptions import SiteNotSpecifiedError + + +# translation +@click.command("build-message-files") +@pass_context +def build_message_files(context): + "Build message files for translation" + import xhiveframework.translate + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.translate.rebuild_all_translation_files() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("new-language") # , help="Create lang-code.csv for given app") +@pass_context +@click.argument("lang_code") # , help="Language code eg. en") +@click.argument("app") # , help="App name eg. xhiveframework") +def new_language(context, lang_code, app): + """Create lang-code.csv for given app""" + import xhiveframework.translate + + if not context["sites"]: + raise Exception("--site is required") + + # init site + xhiveframework.connect(site=context["sites"][0]) + xhiveframework.translate.write_translations_file(app, lang_code) + + print(f"File created at ./apps/{app}/{app}/translations/{lang_code}.csv") + print("You will need to add the language in xhiveframework/geo/languages.json, if you haven't done it already.") + + +@click.command("get-untranslated") +@click.option("--app", default="_ALL_APPS") +@click.argument("lang") +@click.argument("untranslated_file") +@click.option("--all", default=False, is_flag=True, help="Get all message strings") +@pass_context +def get_untranslated(context, lang, untranslated_file, app="_ALL_APPS", all=None): + "Get untranslated strings for language" + import xhiveframework.translate + + site = get_site(context) + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.translate.get_untranslated(lang, untranslated_file, get_all=all, app=app) + finally: + xhiveframework.destroy() + + +@click.command("update-translations") +@click.option("--app", default="_ALL_APPS") +@click.argument("lang") +@click.argument("untranslated_file") +@click.argument("translated-file") +@pass_context +def update_translations(context, lang, untranslated_file, translated_file, app="_ALL_APPS"): + "Update translated strings" + import xhiveframework.translate + + site = get_site(context) + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.translate.update_translations(lang, untranslated_file, translated_file, app=app) + finally: + xhiveframework.destroy() + + +@click.command("import-translations") +@click.argument("lang") +@click.argument("path") +@pass_context +def import_translations(context, lang, path): + "Update translated strings" + import xhiveframework.translate + + site = get_site(context) + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.translate.import_translations(lang, path) + finally: + xhiveframework.destroy() + + +@click.command("migrate-translations") +@click.argument("source-app") +@click.argument("target-app") +@pass_context +def migrate_translations(context, source_app, target_app): + "Migrate target-app-specific translations from source-app to target-app" + import xhiveframework.translate + + site = get_site(context) + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.translate.migrate_translations(source_app, target_app) + finally: + xhiveframework.destroy() + + +commands = [ + build_message_files, + get_untranslated, + import_translations, + new_language, + update_translations, + migrate_translations, +] diff --git a/xhiveframework/commands/utils.py b/xhiveframework/commands/utils.py new file mode 100644 index 0000000..e4cf021 --- /dev/null +++ b/xhiveframework/commands/utils.py @@ -0,0 +1,1181 @@ +import json +import os +import subprocess +import sys +import typing +from shutil import which + +import click + +import xhiveframework +from xhiveframework import _ +from xhiveframework.commands import get_site, pass_context +from xhiveframework.coverage import CodeCoverage +from xhiveframework.exceptions import SiteNotSpecifiedError +from xhiveframework.utils import cint, update_progress_bar + +EXTRA_ARGS_CTX = {"ignore_unknown_options": True, "allow_extra_args": True} + +if typing.TYPE_CHECKING: + from IPython.terminal.embed import InteractiveShellEmbed + + +@click.command("build") +@click.option("--app", help="Build assets for app") +@click.option("--apps", help="Build assets for specific apps") +@click.option( + "--hard-link", + is_flag=True, + default=False, + help="Copy the files instead of symlinking", + envvar="XHIVEFRAMEWORK_HARD_LINK_ASSETS", +) +@click.option("--production", is_flag=True, default=False, help="Build assets in production mode") +@click.option("--verbose", is_flag=True, default=False, help="Verbose") +@click.option( + "--force", is_flag=True, default=False, help="Force build assets instead of downloading available" +) +@click.option( + "--save-metafiles", + is_flag=True, + default=False, + help="Saves esbuild metafiles for built assets. Useful for analyzing bundle size. More info: https://esbuild.github.io/api/#metafile", +) +def build( + app=None, + apps=None, + hard_link=False, + production=False, + verbose=False, + force=False, + save_metafiles=False, +): + "Compile JS and CSS source files" + from xhiveframework.build import bundle, download_xhiveframework_assets + from xhiveframework.utils.synchronization import filelock + + xhiveframework.init("") + + if not apps and app: + apps = app + + with filelock("bench_build", is_global=True, timeout=10): + # dont try downloading assets if force used, app specified or running via CI + if not (force or apps or os.environ.get("CI")): + # skip building xhiveframework if assets exist remotely + skip_xhiveframework = download_xhiveframework_assets(verbose=verbose) + else: + skip_xhiveframework = False + + # don't minify in developer_mode for faster builds + development = xhiveframework.local.conf.developer_mode or xhiveframework.local.dev_server + mode = "development" if development else "production" + if production: + mode = "production" + + bundle( + mode, + apps=apps, + hard_link=hard_link, + verbose=verbose, + skip_xhiveframework=skip_xhiveframework, + save_metafiles=save_metafiles, + ) + + +@click.command("watch") +@click.option("--apps", help="Watch assets for specific apps") +def watch(apps=None): + "Watch and compile JS and CSS files as and when they change" + from xhiveframework.build import watch + + xhiveframework.init("") + watch(apps) + + +@click.command("clear-cache") +@pass_context +def clear_cache(context): + "Clear cache, doctype cache and defaults" + import xhiveframework.sessions + from xhiveframework.desk.notifications import clear_notifications + from xhiveframework.website.utils import clear_website_cache + + for site in context.sites: + try: + xhiveframework.connect(site) + xhiveframework.clear_cache() + clear_notifications() + clear_website_cache() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("clear-website-cache") +@pass_context +def clear_website_cache(context): + "Clear website cache" + from xhiveframework.website.utils import clear_website_cache + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + clear_website_cache() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("destroy-all-sessions") +@click.option("--reason") +@pass_context +def destroy_all_sessions(context, reason=None): + "Clear sessions of all users (logs them out)" + import xhiveframework.sessions + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.sessions.clear_all_sessions(reason) + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("show-config") +@click.option("--format", "-f", type=click.Choice(["text", "json"]), default="text") +@pass_context +def show_config(context, format): + "Print configuration file to STDOUT in speified format" + + if not context.sites: + raise SiteNotSpecifiedError + + sites_config = {} + sites_path = os.getcwd() + + from xhiveframework.utils.commands import render_table + + def transform_config(config, prefix=None): + prefix = f"{prefix}." if prefix else "" + site_config = [] + + for conf, value in config.items(): + if isinstance(value, dict): + site_config += transform_config(value, prefix=f"{prefix}{conf}") + else: + log_value = json.dumps(value) if isinstance(value, list) else value + site_config += [[f"{prefix}{conf}", log_value]] + + return site_config + + for site in context.sites: + xhiveframework.init(site) + + if len(context.sites) != 1 and format == "text": + if context.sites.index(site) != 0: + click.echo() + click.secho(f"Site {site}", fg="yellow") + + configuration = xhiveframework.get_site_config(sites_path=sites_path, site_path=site) + + if format == "text": + data = transform_config(configuration) + data.insert(0, ["Config", "Value"]) + render_table(data) + + if format == "json": + sites_config[site] = configuration + + xhiveframework.destroy() + + if format == "json": + click.echo(xhiveframework.as_json(sites_config)) + + +@click.command("reset-perms") +@pass_context +def reset_perms(context): + "Reset permissions for all doctypes" + from xhiveframework.permissions import reset_perms + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + for d in xhiveframework.db.sql_list( + """select name from `tabDocType` + where istable=0 and custom=0""" + ): + xhiveframework.clear_cache(doctype=d) + reset_perms(d) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("execute") +@click.argument("method") +@click.option("--args") +@click.option("--kwargs") +@click.option("--profile", is_flag=True, default=False) +@pass_context +def execute(context, method, args=None, kwargs=None, profile=False): + "Execute a function" + for site in context.sites: + ret = "" + try: + xhiveframework.init(site=site) + xhiveframework.connect() + + if args: + try: + args = eval(args) + except NameError: + args = [args] + else: + args = () + + if kwargs: + kwargs = eval(kwargs) + else: + kwargs = {} + + if profile: + import cProfile + + pr = cProfile.Profile() + pr.enable() + + try: + ret = xhiveframework.get_attr(method)(*args, **kwargs) + except Exception: + # eval is safe here because input is from console + ret = eval(method + "(*args, **kwargs)", globals(), locals()) # nosemgrep + + if profile: + import pstats + from io import StringIO + + pr.disable() + s = StringIO() + pstats.Stats(pr, stream=s).sort_stats("cumulative").print_stats(0.5) + print(s.getvalue()) + + if xhiveframework.db: + xhiveframework.db.commit() + finally: + xhiveframework.destroy() + if ret: + from xhiveframework.utils.response import json_handler + + print(json.dumps(ret, default=json_handler)) + + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("add-to-email-queue") +@click.argument("email-path") +@pass_context +def add_to_email_queue(context, email_path): + "Add an email to the Email Queue" + site = get_site(context) + + if os.path.isdir(email_path): + with xhiveframework.init_site(site): + xhiveframework.connect() + for email in os.listdir(email_path): + with open(os.path.join(email_path, email)) as email_data: + kwargs = json.load(email_data) + kwargs["delayed"] = True + xhiveframework.sendmail(**kwargs) + xhiveframework.db.commit() + + +@click.command("export-doc") +@click.argument("doctype") +@click.argument("docname") +@pass_context +def export_doc(context, doctype, docname): + "Export a single document to csv" + import xhiveframework.modules + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.modules.export_doc(doctype, docname) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("export-json") +@click.argument("doctype") +@click.argument("path") +@click.option("--name", help="Export only one document") +@pass_context +def export_json(context, doctype, path, name=None): + "Export doclist as json to the given path, use '-' as name for Singles." + from xhiveframework.core.doctype.data_import.data_import import export_json + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + export_json(doctype, path, name=name) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("export-csv") +@click.argument("doctype") +@click.argument("path") +@pass_context +def export_csv(context, doctype, path): + "Export data import template with data for DocType" + from xhiveframework.core.doctype.data_import.data_import import export_csv + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + export_csv(doctype, path) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("export-fixtures") +@click.option("--app", default=None, help="Export fixtures of a specific app") +@pass_context +def export_fixtures(context, app=None): + "Export fixtures" + from xhiveframework.utils.fixtures import export_fixtures + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + export_fixtures(app=app) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("import-doc") +@click.argument("path") +@pass_context +def import_doc(context, path, force=False): + "Import (insert/update) doclist. If the argument is a directory, all files ending with .json are imported" + from xhiveframework.core.doctype.data_import.data_import import import_doc + + if not os.path.exists(path): + path = os.path.join("..", path) + if not os.path.exists(path): + print(f"Invalid path {path}") + sys.exit(1) + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + import_doc(path) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("data-import") +@click.option( + "--file", + "file_path", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + required=True, + help=( + "Path to import file (.csv, .xlsx)." + "Consider that relative paths will resolve from 'sites' directory" + ), +) +@click.option("--doctype", type=str, required=True) +@click.option( + "--type", + "import_type", + type=click.Choice(["Insert", "Update"], case_sensitive=False), + default="Insert", + help="Insert New Records or Update Existing Records", +) +@click.option("--submit-after-import", default=False, is_flag=True, help="Submit document after importing it") +@click.option("--mute-emails", default=True, is_flag=True, help="Mute emails during import") +@pass_context +def data_import(context, file_path, doctype, import_type=None, submit_after_import=False, mute_emails=True): + "Import documents in bulk from CSV or XLSX using data import" + from xhiveframework.core.doctype.data_import.data_import import import_file + + site = get_site(context) + + xhiveframework.init(site=site) + xhiveframework.connect() + import_file(doctype, file_path, import_type, submit_after_import, console=True) + xhiveframework.destroy() + + +@click.command("bulk-rename") +@click.argument("doctype") +@click.argument("path") +@pass_context +def bulk_rename(context, doctype, path): + "Rename multiple records via CSV file" + from xhiveframework.model.rename_doc import bulk_rename + from xhiveframework.utils.csvutils import read_csv_content + + site = get_site(context) + + with open(path) as csvfile: + rows = read_csv_content(csvfile.read()) + + xhiveframework.init(site=site) + xhiveframework.connect() + + bulk_rename(doctype, rows, via_console=True) + + xhiveframework.destroy() + + +@click.command("db-console", context_settings=EXTRA_ARGS_CTX) +@click.argument("extra_args", nargs=-1) +@pass_context +def database(context, extra_args): + """ + Enter into the Database console for given site. + """ + site = get_site(context) + xhiveframework.init(site=site) + _enter_console(extra_args=extra_args) + + +@click.command("mariadb", context_settings=EXTRA_ARGS_CTX) +@click.argument("extra_args", nargs=-1) +@pass_context +def mariadb(context, extra_args): + """ + Enter into mariadb console for a given site. + """ + site = get_site(context) + xhiveframework.init(site=site) + xhiveframework.conf.db_type = "mariadb" + _enter_console(extra_args=extra_args) + + +@click.command("postgres", context_settings=EXTRA_ARGS_CTX) +@click.argument("extra_args", nargs=-1) +@pass_context +def postgres(context, extra_args): + """ + Enter into postgres console for a given site. + """ + site = get_site(context) + xhiveframework.init(site=site) + xhiveframework.conf.db_type = "postgres" + _enter_console(extra_args=extra_args) + + +def _enter_console(extra_args=None): + from xhiveframework.database import get_command + from xhiveframework.utils import get_site_path + + if xhiveframework.conf.db_type == "mariadb": + os.environ["MYSQL_HISTFILE"] = os.path.abspath(get_site_path("logs", "mariadb_console.log")) + else: + os.environ["PSQL_HISTORY"] = os.path.abspath(get_site_path("logs", "postgresql_console.log")) + + bin, args, bin_name = get_command( + host=xhiveframework.conf.db_host, + port=xhiveframework.conf.db_port, + user=xhiveframework.conf.db_name, + password=xhiveframework.conf.db_password, + db_name=xhiveframework.conf.db_name, + extra=list(extra_args) if extra_args else [], + ) + if not bin: + xhiveframework.throw( + _("{} not found in PATH! This is required to access the console.").format(bin_name), + exc=xhiveframework.ExecutableNotFound, + ) + os.execv(bin, [bin, *args]) + + +@click.command("jupyter") +@pass_context +def jupyter(context): + """Start an interactive jupyter notebook""" + installed_packages = ( + r.split("==", 1)[0] + for r in subprocess.check_output([sys.executable, "-m", "pip", "freeze"], encoding="utf8") + ) + + if "jupyter" not in installed_packages: + subprocess.check_output([sys.executable, "-m", "pip", "install", "jupyter"]) + + site = get_site(context) + xhiveframework.init(site=site) + + jupyter_notebooks_path = os.path.abspath(xhiveframework.get_site_path("jupyter_notebooks")) + sites_path = os.path.abspath(xhiveframework.get_site_path("..")) + + try: + os.stat(jupyter_notebooks_path) + except OSError: + print(f"Creating folder to keep jupyter notebooks at {jupyter_notebooks_path}") + os.mkdir(jupyter_notebooks_path) + bin_path = os.path.abspath("../env/bin") + print( + f""" +Starting Jupyter notebook +Run the following in your first cell to connect notebook to xhiveframework +``` +import xhiveframework +xhiveframework.init(site='{site}', sites_path='{sites_path}') +xhiveframework.connect() +xhiveframework.local.lang = xhiveframework.db.get_default('lang') +xhiveframework.db.connect() +``` + """ + ) + os.execv( + f"{bin_path}/jupyter", + [ + f"{bin_path}/jupyter", + "notebook", + jupyter_notebooks_path, + ], + ) + + +def _console_cleanup(): + # Execute after_rollback on console close + xhiveframework.db.rollback() + xhiveframework.destroy() + + +def store_logs(terminal: "InteractiveShellEmbed") -> None: + from contextlib import suppress + + xhiveframework.log_level = 20 # info + with suppress(Exception): + logger = xhiveframework.logger("ipython") + logger.info("=== bench console session ===") + for line in terminal.history_manager.get_range(): + logger.info(line[2]) + logger.info("=== session end ===") + + +@click.command("console") +@click.option("--autoreload", is_flag=True, help="Reload changes to code automatically") +@pass_context +def console(context, autoreload=False): + "Start ipython console for a site" + site = get_site(context) + xhiveframework.init(site=site) + xhiveframework.connect() + xhiveframework.local.lang = xhiveframework.db.get_default("lang") + + from atexit import register + + from IPython.terminal.embed import InteractiveShellEmbed + + register(_console_cleanup) + + terminal = InteractiveShellEmbed.instance() + if autoreload: + terminal.extension_manager.load_extension("autoreload") + terminal.run_line_magic("autoreload", "2") + + all_apps = xhiveframework.get_installed_apps() + failed_to_import = [] + register(store_logs, terminal) # Note: atexit runs in reverse order of registration + + for app in list(all_apps): + try: + locals()[app] = __import__(app) + except ModuleNotFoundError: + failed_to_import.append(app) + all_apps.remove(app) + + print("Apps in this namespace:\n{}".format(", ".join(all_apps))) + if failed_to_import: + print("\nFailed to import:\n{}".format(", ".join(failed_to_import))) + + # ref: https://stackoverflow.com/a/74681224 + try: + from IPython.core import ultratb + + ultratb.VerboseTB._tb_highlight = "bg:ansibrightblack" + except Exception: + pass + + terminal.colors = "neutral" + terminal.display_banner = False + terminal() + + +@click.command("transform-database", help="Change tables' internal settings changing engine and row formats") +@click.option( + "--table", + required=True, + help="Comma separated name of tables to convert. To convert all tables, pass 'all'", +) +@click.option( + "--engine", + default=None, + type=click.Choice(["InnoDB", "MyISAM"]), + help="Choice of storage engine for said table(s)", +) +@click.option( + "--row_format", + default=None, + type=click.Choice(["DYNAMIC", "COMPACT", "REDUNDANT", "COMPRESSED"]), + help="Set ROW_FORMAT parameter for said table(s)", +) +@click.option("--failfast", is_flag=True, default=False, help="Exit on first failure occurred") +@pass_context +def transform_database(context, table, engine, row_format, failfast): + "Transform site database through given parameters" + site = get_site(context) + check_table = [] + add_line = False + skipped = 0 + xhiveframework.init(site=site) + + if xhiveframework.conf.db_type != "mariadb": + click.secho("This command only has support for MariaDB databases at this point", fg="yellow") + sys.exit(1) + + if not (engine or row_format): + click.secho("Values for `--engine` or `--row_format` must be set") + sys.exit(1) + + xhiveframework.connect() + + if table == "all": + information_schema = xhiveframework.qb.Schema("information_schema") + queried_tables = ( + xhiveframework.qb.from_(information_schema.tables) + .select("table_name") + .where( + (information_schema.tables.row_format != row_format) + & (information_schema.tables.table_schema == xhiveframework.conf.db_name) + ) + .run() + ) + tables = [x[0] for x in queried_tables] + else: + tables = [x.strip() for x in table.split(",")] + + total = len(tables) + + for current, table in enumerate(tables): + values_to_set = "" + if engine: + values_to_set += f" ENGINE={engine}" + if row_format: + values_to_set += f" ROW_FORMAT={row_format}" + + try: + xhiveframework.db.sql(f"ALTER TABLE `{table}`{values_to_set}") + update_progress_bar("Updating table schema", current - skipped, total) + add_line = True + + except Exception as e: + check_table.append([table, e.args]) + skipped += 1 + + if failfast: + break + + if add_line: + print() + + for errored_table in check_table: + table, err = errored_table + err_msg = f"{table}: ERROR {err[0]}: {err[1]}" + click.secho(err_msg, fg="yellow") + + xhiveframework.destroy() + + +@click.command("run-tests") +@click.option("--app", help="For App") +@click.option("--doctype", help="For DocType") +@click.option("--module-def", help="For all Doctypes in Module Def") +@click.option("--case", help="Select particular TestCase") +@click.option( + "--doctype-list-path", + help="Path to .txt file for list of doctypes. Example xhiveerp/tests/server/agriculture.txt", +) +@click.option("--test", multiple=True, help="Specific test") +@click.option("--module", help="Run tests in a module") +@click.option("--profile", is_flag=True, default=False) +@click.option("--coverage", is_flag=True, default=False) +@click.option("--skip-test-records", is_flag=True, default=False, help="Don't create test records") +@click.option("--skip-before-tests", is_flag=True, default=False, help="Don't run before tests hook") +@click.option("--junit-xml-output", help="Destination file path for junit xml report") +@click.option( + "--failfast", is_flag=True, default=False, help="Stop the test run on the first error or failure" +) +@pass_context +def run_tests( + context, + app=None, + module=None, + doctype=None, + module_def=None, + test=(), + profile=False, + coverage=False, + junit_xml_output=False, + doctype_list_path=None, + skip_test_records=False, + skip_before_tests=False, + failfast=False, + case=None, +): + """Run python unit-tests""" + + with CodeCoverage(coverage, app): + import xhiveframework + import xhiveframework.test_runner + + tests = test + site = get_site(context) + + allow_tests = xhiveframework.get_conf(site).allow_tests + + if not (allow_tests or os.environ.get("CI")): + click.secho("Testing is disabled for the site!", bold=True) + click.secho("You can enable tests by entering following command:") + click.secho(f"bench --site {site} set-config allow_tests true", fg="green") + return + + xhiveframework.init(site=site) + + xhiveframework.flags.skip_before_tests = skip_before_tests + xhiveframework.flags.skip_test_records = skip_test_records + + ret = xhiveframework.test_runner.main( + app, + module, + doctype, + module_def, + context.verbose, + tests=tests, + force=context.force, + profile=profile, + junit_xml_output=junit_xml_output, + doctype_list_path=doctype_list_path, + failfast=failfast, + case=case, + ) + + if len(ret.failures) == 0 and len(ret.errors) == 0: + ret = 0 + + if os.environ.get("CI"): + sys.exit(ret) + + +@click.command("run-parallel-tests") +@click.option("--app", help="For App", default="xhiveframework") +@click.option("--build-number", help="Build number", default=1) +@click.option("--total-builds", help="Total number of builds", default=1) +@click.option("--with-coverage", is_flag=True, help="Build coverage file") +@click.option("--use-orchestrator", is_flag=True, help="Use orchestrator to run parallel tests") +@click.option("--dry-run", is_flag=True, default=False, help="Dont actually run tests") +@pass_context +def run_parallel_tests( + context, + app, + build_number, + total_builds, + with_coverage=False, + use_orchestrator=False, + dry_run=False, +): + from traceback_with_variables import activate_by_import + + with CodeCoverage(with_coverage, app): + site = get_site(context) + if use_orchestrator: + from xhiveframework.parallel_test_runner import ParallelTestWithOrchestrator + + ParallelTestWithOrchestrator(app, site=site) + else: + from xhiveframework.parallel_test_runner import ParallelTestRunner + + ParallelTestRunner( + app, + site=site, + build_number=build_number, + total_builds=total_builds, + dry_run=dry_run, + ) + + +@click.command( + "run-ui-tests", + context_settings=dict( + ignore_unknown_options=True, + ), +) +@click.argument("app") +@click.argument("cypressargs", nargs=-1, type=click.UNPROCESSED) +@click.option("--headless", is_flag=True, help="Run UI Test in headless mode") +@click.option("--parallel", is_flag=True, help="Run UI Test in parallel mode") +@click.option("--with-coverage", is_flag=True, help="Generate coverage report") +@click.option("--browser", default="chrome", help="Browser to run tests in") +@click.option("--ci-build-id") +@pass_context +def run_ui_tests( + context, + app, + headless=False, + parallel=True, + with_coverage=False, + browser="chrome", + ci_build_id=None, + cypressargs=None, +): + "Run UI tests" + site = get_site(context) + xhiveframework.init(site) + app_base_path = xhiveframework.get_app_source_path(app) + site_url = xhiveframework.utils.get_site_url(site) + admin_password = xhiveframework.get_conf(site).admin_password + + # override baseUrl using env variable + site_env = f"CYPRESS_baseUrl={site_url}" + password_env = f"CYPRESS_adminPassword={admin_password}" if admin_password else "" + coverage_env = f"CYPRESS_coverage={str(with_coverage).lower()}" + + os.chdir(app_base_path) + + node_bin = subprocess.getoutput("(cd ../xhiveframework && yarn bin)") + cypress_path = f"{node_bin}/cypress" + drag_drop_plugin_path = f"{node_bin}/../@4tw/cypress-drag-drop" + real_events_plugin_path = f"{node_bin}/../cypress-real-events" + testing_library_path = f"{node_bin}/../@testing-library" + coverage_plugin_path = f"{node_bin}/../@cypress/code-coverage" + + # check if cypress in path...if not, install it. + if not ( + os.path.exists(cypress_path) + and os.path.exists(drag_drop_plugin_path) + and os.path.exists(real_events_plugin_path) + and os.path.exists(testing_library_path) + and os.path.exists(coverage_plugin_path) + ): + # install cypress & dependent plugins + click.secho("Installing Cypress...", fg="yellow") + packages = " ".join( + [ + "cypress@^13", + "@4tw/cypress-drag-drop@^2", + "cypress-real-events", + "@testing-library/cypress@^10", + "@testing-library/dom@8.17.1", + "@cypress/code-coverage@^3", + ] + ) + xhiveframework.commands.popen(f"(cd ../xhiveframework && yarn add {packages} --no-lockfile)") + + # run for headless mode + run_or_open = f"run --browser {browser}" if headless else "open" + formatted_command = f"{site_env} {password_env} {coverage_env} {cypress_path} {run_or_open}" + + if os.environ.get("CYPRESS_RECORD_KEY"): + formatted_command += " --record" + + if parallel: + formatted_command += " --parallel" + + if ci_build_id: + formatted_command += f" --ci-build-id {ci_build_id}" + + if cypressargs: + formatted_command += " " + " ".join(cypressargs) + + click.secho("Running Cypress...", fg="yellow") + xhiveframework.commands.popen(formatted_command, cwd=app_base_path, raise_err=True) + + +@click.command("serve") +@click.option("--port", default=8000) +@click.option("--profile", is_flag=True, default=False) +@click.option( + "--proxy", + is_flag=True, + default=False, + help="The development server may be run behind a proxy, e.g. ngrok / localtunnel", +) +@click.option("--noreload", "no_reload", is_flag=True, default=False) +@click.option("--nothreading", "no_threading", is_flag=True, default=False) +@click.option("--with-coverage", is_flag=True, default=False) +@pass_context +def serve( + context, + port=None, + profile=False, + proxy=False, + no_reload=False, + no_threading=False, + sites_path=".", + site=None, + with_coverage=False, +): + "Start development web server" + import xhiveframework.app + + if not context.sites: + site = None + else: + site = context.sites[0] + with CodeCoverage(with_coverage, "xhiveframework"): + if with_coverage: + # unable to track coverage with threading enabled + no_threading = True + no_reload = True + xhiveframework.app.serve( + port=port, + profile=profile, + proxy=proxy, + no_reload=no_reload, + no_threading=no_threading, + site=site, + sites_path=".", + ) + + +@click.command("request") +@click.option("--args", help="arguments like `?cmd=test&key=value` or `/api/request/method?..`") +@click.option("--path", help="path to request JSON") +@pass_context +def request(context, args=None, path=None): + "Run a request as an admin" + import xhiveframework.api + import xhiveframework.handler + + for site in context.sites: + try: + xhiveframework.init(site=site) + xhiveframework.connect() + if args: + if "?" in args: + xhiveframework.local.form_dict = xhiveframework._dict( + [a.split("=") for a in args.split("?")[-1].split("&")] + ) + else: + xhiveframework.local.form_dict = xhiveframework._dict() + + if args.startswith("/api/method"): + xhiveframework.local.form_dict.cmd = args.split("?", 1)[0].split("/")[-1] + elif path: + with open(os.path.join("..", path)) as f: + args = json.loads(f.read()) + + xhiveframework.local.form_dict = xhiveframework._dict(args) + + xhiveframework.handler.execute_cmd(xhiveframework.form_dict.cmd) + + print(xhiveframework.response) + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +@click.command("make-app") +@click.argument("destination") +@click.argument("app_name") +@click.option("--no-git", is_flag=True, default=False, help="Do not initialize git repository for the app") +def make_app(destination, app_name, no_git=False): + "Creates a boilerplate app" + from xhiveframework.utils.boilerplate import make_boilerplate + + make_boilerplate(destination, app_name, no_git=no_git) + + +@click.command("create-patch") +def create_patch(): + "Creates a new patch interactively" + from xhiveframework.utils.boilerplate import PatchCreator + + pc = PatchCreator() + pc.fetch_user_inputs() + pc.create_patch_file() + + +@click.command("set-config") +@click.argument("key") +@click.argument("value") +@click.option("-g", "--global", "global_", is_flag=True, default=False, help="Set value in bench config") +@click.option("-p", "--parse", is_flag=True, default=False, help="Evaluate as Python Object") +@pass_context +def set_config(context, key, value, global_=False, parse=False): + "Insert/Update a value in site_config.json" + from xhiveframework.installer import update_site_config + + if parse: + import ast + + value = ast.literal_eval(value) + + if global_: + sites_path = os.getcwd() + common_site_config_path = os.path.join(sites_path, "common_site_config.json") + update_site_config(key, value, validate=False, site_config_path=common_site_config_path) + else: + if not context.sites: + raise SiteNotSpecifiedError + for site in context.sites: + xhiveframework.init(site=site) + update_site_config(key, value, validate=False) + xhiveframework.destroy() + + +@click.command("version") +@click.option( + "-f", + "--format", + "output", + type=click.Choice(["plain", "table", "json", "legacy"]), + help="Output format", + default="legacy", +) +def get_version(output): + """Show the versions of all the installed apps.""" + from git import Repo + from git.exc import InvalidGitRepositoryError + + from xhiveframework.utils.change_log import get_app_branch + from xhiveframework.utils.commands import render_table + + xhiveframework.init("") + data = [] + + for app in sorted(xhiveframework.get_all_apps()): + module = xhiveframework.get_module(app) + app_hooks = xhiveframework.get_module(app + ".hooks") + + app_info = xhiveframework._dict() + + try: + app_info.commit = Repo(xhiveframework.get_app_source_path(app)).head.object.hexsha[:7] + except InvalidGitRepositoryError: + app_info.commit = "" + + app_info.app = app + app_info.branch = get_app_branch(app) + app_info.version = getattr(app_hooks, f"{app_info.branch}_version", None) or module.__version__ + + data.append(app_info) + + { + "legacy": lambda: [click.echo(f"{app_info.app} {app_info.version}") for app_info in data], + "plain": lambda: [ + click.echo(f"{app_info.app} {app_info.version} {app_info.branch} ({app_info.commit})") + for app_info in data + ], + "table": lambda: render_table( + [["App", "Version", "Branch", "Commit"]] + + [[app_info.app, app_info.version, app_info.branch, app_info.commit] for app_info in data] + ), + "json": lambda: click.echo(json.dumps(data, indent=4)), + }[output]() + + +@click.command("rebuild-global-search") +@click.option("--static-pages", is_flag=True, default=False, help="Rebuild global search for static pages") +@pass_context +def rebuild_global_search(context, static_pages=False): + """Setup help table in the current site (called after migrate)""" + from xhiveframework.utils.global_search import ( + add_route_to_global_search, + get_doctypes_with_global_search, + get_routes_to_index, + rebuild_for_doctype, + sync_global_search, + ) + + for site in context.sites: + try: + xhiveframework.init(site) + xhiveframework.connect() + + if static_pages: + routes = get_routes_to_index() + for i, route in enumerate(routes): + add_route_to_global_search(route) + xhiveframework.local.request = None + update_progress_bar("Rebuilding Global Search", i, len(routes)) + sync_global_search() + else: + doctypes = get_doctypes_with_global_search() + for i, doctype in enumerate(doctypes): + rebuild_for_doctype(doctype) + update_progress_bar("Rebuilding Global Search", i, len(doctypes)) + + finally: + xhiveframework.destroy() + if not context.sites: + raise SiteNotSpecifiedError + + +commands = [ + build, + clear_cache, + clear_website_cache, + database, + transform_database, + jupyter, + console, + destroy_all_sessions, + execute, + export_csv, + export_doc, + export_fixtures, + export_json, + get_version, + data_import, + import_doc, + make_app, + create_patch, + mariadb, + postgres, + request, + reset_perms, + run_tests, + run_ui_tests, + serve, + set_config, + show_config, + watch, + bulk_rename, + add_to_email_queue, + rebuild_global_search, + run_parallel_tests, +] diff --git a/xhiveframework/config/__init__.py b/xhiveframework/config/__init__.py new file mode 100644 index 0000000..ac23b84 --- /dev/null +++ b/xhiveframework/config/__init__.py @@ -0,0 +1,64 @@ +import xhiveframework +from xhiveframework import _ + + +def get_modules_from_all_apps_for_user(user: str | None = None) -> list[dict]: + user = user or xhiveframework.session.user + all_modules = get_modules_from_all_apps() + global_blocked_modules = xhiveframework.get_doc("User", "Administrator").get_blocked_modules() + user_blocked_modules = xhiveframework.get_doc("User", user).get_blocked_modules() + blocked_modules = global_blocked_modules + user_blocked_modules + allowed_modules_list = [m for m in all_modules if m.get("module_name") not in blocked_modules] + + empty_tables_by_module = get_all_empty_tables_by_module() + + for module in allowed_modules_list: + module_name = module.get("module_name") + + # Apply onboarding status + if module_name in empty_tables_by_module: + module["onboard_present"] = 1 + + return allowed_modules_list + + +def get_modules_from_all_apps(): + modules_list = [] + for app in xhiveframework.get_installed_apps(): + modules_list += get_modules_from_app(app) + return modules_list + + +def get_modules_from_app(app): + return xhiveframework.get_all("Module Def", filters={"app_name": app}, fields=["module_name", "app_name as app"]) + + +def get_all_empty_tables_by_module(): + table_rows = xhiveframework.qb.Field("table_rows") + table_name = xhiveframework.qb.Field("table_name") + information_schema = xhiveframework.qb.Schema("information_schema") + + empty_tables = ( + xhiveframework.qb.from_(information_schema.tables).select(table_name).where(table_rows == 0) + ).run() + + empty_tables = {r[0] for r in empty_tables} + + results = xhiveframework.get_all("DocType", fields=["name", "module"]) + empty_tables_by_module = {} + + for doctype, module in results: + if f"tab{doctype}" in empty_tables: + if module in empty_tables_by_module: + empty_tables_by_module[module].append(doctype) + else: + empty_tables_by_module[module] = [doctype] + return empty_tables_by_module + + +def is_domain(module): + return module.get("category") == "Domains" + + +def is_module(module): + return module.get("type") == "module" diff --git a/xhiveframework/contacts/__init__.py b/xhiveframework/contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/address_and_contact.py b/xhiveframework/contacts/address_and_contact.py new file mode 100644 index 0000000..53298c5 --- /dev/null +++ b/xhiveframework/contacts/address_and_contact.py @@ -0,0 +1,149 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ + + +def load_address_and_contact(doc, key=None) -> None: + """Loads address list and contact list in `__onload`""" + from xhiveframework.contacts.doctype.address.address import get_address_display_list + from xhiveframework.contacts.doctype.contact.contact import get_contact_display_list + + doc.set_onload("addr_list", get_address_display_list(doc.doctype, doc.name)) + doc.set_onload("contact_list", get_contact_display_list(doc.doctype, doc.name)) + + +def has_permission(doc, ptype, user): + links = get_permitted_and_not_permitted_links(doc.doctype) + if not links.get("not_permitted_links"): + # optimization: don't determine permissions based on link fields + return True + + # True if any one is True or all are empty + names = [] + for df in links.get("permitted_links") + links.get("not_permitted_links"): + doctype = df.options + name = doc.get(df.fieldname) + names.append(name) + + if name and xhiveframework.has_permission(doctype, ptype, doc=name): + return True + + if not any(names): + return True + return False + + +def get_permission_query_conditions_for_contact(user): + return get_permission_query_conditions("Contact") + + +def get_permission_query_conditions_for_address(user): + return get_permission_query_conditions("Address") + + +def get_permission_query_conditions(doctype): + links = get_permitted_and_not_permitted_links(doctype) + + if not links.get("not_permitted_links"): + # when everything is permitted, don't add additional condition + return "" + + elif not links.get("permitted_links"): + # when everything is not permitted + conditions = [ + f"ifnull(`tab{doctype}`.`{df.fieldname}`, '')=''" for df in links.get("not_permitted_links") + ] + + return "( " + " and ".join(conditions) + " )" + + else: + conditions = [ + f"ifnull(`tab{doctype}`.`{df.fieldname}`, '')!=''" for df in links.get("permitted_links") + ] + + return "( " + " or ".join(conditions) + " )" + + +def get_permitted_and_not_permitted_links(doctype): + permitted_links = [] + not_permitted_links = [] + + meta = xhiveframework.get_meta(doctype) + allowed_doctypes = xhiveframework.permissions.get_doctypes_with_read() + + for df in meta.get_link_fields(): + if df.options not in ("Customer", "Supplier", "Company", "Sales Partner"): + continue + + if df.options in allowed_doctypes: + permitted_links.append(df) + else: + not_permitted_links.append(df) + + return {"permitted_links": permitted_links, "not_permitted_links": not_permitted_links} + + +def delete_contact_and_address(doctype: str, docname: str) -> None: + for parenttype in ("Contact", "Address"): + for name in xhiveframework.get_all( + "Dynamic Link", + filters={ + "parenttype": parenttype, + "link_doctype": doctype, + "link_name": docname, + }, + pluck="parent", + ): + doc = xhiveframework.get_doc(parenttype, name) + if len(doc.links) == 1: + doc.delete() + else: + for link in doc.links: + if link.link_doctype == doctype and link.link_name == docname: + doc.remove(link) + doc.save() + break + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def filter_dynamic_link_doctypes( + doctype, txt: str, searchfield, start, page_len, filters: dict +) -> list[list[str]]: + from xhiveframework.permissions import get_doctypes_with_read + + txt = txt or "" + filters = filters or {} + + _doctypes_from_df = xhiveframework.get_all( + "DocField", + filters=filters, + pluck="parent", + distinct=True, + order_by=None, + ) + doctypes_from_df = {d for d in _doctypes_from_df if txt.lower() in _(d).lower()} + + filters.update({"dt": ("not in", doctypes_from_df)}) + _doctypes_from_cdf = xhiveframework.get_all( + "Custom Field", filters=filters, pluck="dt", distinct=True, order_by=None + ) + doctypes_from_cdf = {d for d in _doctypes_from_cdf if txt.lower() in _(d).lower()} + + all_doctypes = doctypes_from_df.union(doctypes_from_cdf) + allowed_doctypes = set(get_doctypes_with_read()) + + valid_doctypes = sorted(all_doctypes.intersection(allowed_doctypes)) + + return [[doctype] for doctype in valid_doctypes] + + +def set_link_title(doc): + if not doc.links: + return + for link in doc.links: + if not link.link_title: + linked_doc = xhiveframework.get_doc(link.link_doctype, link.link_name) + link.link_title = linked_doc.get_title() or link.link_name diff --git a/xhiveframework/contacts/doctype/__init__.py b/xhiveframework/contacts/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/address/__init__.py b/xhiveframework/contacts/doctype/address/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/address/address.js b/xhiveframework/contacts/doctype/address/address.js new file mode 100644 index 0000000..3c6eded --- /dev/null +++ b/xhiveframework/contacts/doctype/address/address.js @@ -0,0 +1,75 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Address", { + refresh: function (frm) { + if (frm.doc.__islocal) { + const last_doc = xhiveframework.contacts.get_last_doc(frm); + if ( + xhiveframework.dynamic_link && + xhiveframework.dynamic_link.doc && + xhiveframework.dynamic_link.doc.name == last_doc.docname + ) { + frm.set_value("links", ""); + frm.add_child("links", { + link_doctype: xhiveframework.dynamic_link.doctype, + link_name: xhiveframework.dynamic_link.doc[xhiveframework.dynamic_link.fieldname], + }); + } + } + frm.set_query("link_doctype", "links", function () { + return { + query: "xhiveframework.contacts.address_and_contact.filter_dynamic_link_doctypes", + filters: { + fieldtype: "HTML", + fieldname: "address_html", + }, + }; + }); + frm.refresh_field("links"); + + if (frm.doc.links) { + for (let i in frm.doc.links) { + let link = frm.doc.links[i]; + frm.add_custom_button( + __("{0}: {1}", [__(link.link_doctype), __(link.link_name)]), + function () { + xhiveframework.set_route("Form", link.link_doctype, link.link_name); + }, + __("Links") + ); + } + } + }, + validate: function (frm) { + // clear linked customer / supplier / sales partner on saving... + if (frm.doc.links) { + frm.doc.links.forEach(function (d) { + xhiveframework.model.remove_from_locals(d.link_doctype, d.link_name); + }); + } + }, + after_save: function (frm) { + xhiveframework.run_serially([ + () => xhiveframework.timeout(1), + () => { + const last_doc = xhiveframework.contacts.get_last_doc(frm); + if ( + xhiveframework.dynamic_link && + xhiveframework.dynamic_link.doc && + xhiveframework.dynamic_link.doc.name == last_doc.docname + ) { + for (let i in frm.doc.links) { + let link = frm.doc.links[i]; + if ( + last_doc.doctype == link.link_doctype && + last_doc.docname == link.link_name + ) { + xhiveframework.set_route("Form", last_doc.doctype, last_doc.docname); + } + } + } + }, + ]); + }, +}); diff --git a/xhiveframework/contacts/doctype/address/address.json b/xhiveframework/contacts/doctype/address/address.json new file mode 100644 index 0000000..54d6b03 --- /dev/null +++ b/xhiveframework/contacts/doctype/address/address.json @@ -0,0 +1,226 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:32", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "address_details", + "address_title", + "address_type", + "address_line1", + "address_line2", + "city", + "county", + "state", + "country", + "pincode", + "column_break0", + "email_id", + "phone", + "fax", + "is_primary_address", + "is_shipping_address", + "disabled", + "linked_with", + "links" + ], + "fields": [ + { + "fieldname": "address_details", + "fieldtype": "Section Break", + "options": "fa fa-map-marker" + }, + { + "fieldname": "address_title", + "fieldtype": "Data", + "label": "Address Title" + }, + { + "fieldname": "address_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Address Type", + "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther", + "reqd": 1 + }, + { + "fieldname": "address_line1", + "fieldtype": "Data", + "label": "Address Line 1", + "length": 240, + "reqd": 1 + }, + { + "fieldname": "address_line2", + "fieldtype": "Data", + "label": "Address Line 2", + "length": 240 + }, + { + "fieldname": "city", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "City/Town", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "county", + "fieldtype": "Data", + "label": "County" + }, + { + "fieldname": "state", + "fieldtype": "Data", + "label": "State/Province" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Country", + "options": "Country", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "pincode", + "fieldtype": "Data", + "label": "Postal Code", + "search_index": 1 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email Address", + "options": "Email" + }, + { + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone" + }, + { + "fieldname": "fax", + "fieldtype": "Data", + "label": "Fax" + }, + { + "default": "0", + "fieldname": "is_primary_address", + "fieldtype": "Check", + "label": "Preferred Billing Address" + }, + { + "default": "0", + "fieldname": "is_shipping_address", + "fieldtype": "Check", + "label": "Preferred Shipping Address" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "linked_with", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin" + }, + { + "fieldname": "links", + "fieldtype": "Table", + "label": "Links", + "options": "Dynamic Link" + } + ], + "icon": "fa fa-map-marker", + "idx": 5, + "links": [], + "modified": "2023-11-20 17:28:41.698356", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Address", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Maintenance User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "read": 1, + "role": "All", + "write": 1 + } + ], + "search_fields": "country, state", + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/address/address.py b/xhiveframework/contacts/doctype/address/address.py new file mode 100644 index 0000000..65f46da --- /dev/null +++ b/xhiveframework/contacts/doctype/address/address.py @@ -0,0 +1,356 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from jinja2 import TemplateSyntaxError + +import xhiveframework +from xhiveframework import _, throw +from xhiveframework.contacts.address_and_contact import set_link_title +from xhiveframework.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links +from xhiveframework.model.document import Document +from xhiveframework.model.naming import make_autoname +from xhiveframework.utils import cstr + + +class Address(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.dynamic_link.dynamic_link import DynamicLink + from xhiveframework.types import DF + + address_line1: DF.Data + address_line2: DF.Data | None + address_title: DF.Data | None + address_type: DF.Literal[ + "Billing", + "Shipping", + "Office", + "Personal", + "Plant", + "Postal", + "Shop", + "Subsidiary", + "Warehouse", + "Current", + "Permanent", + "Other", + ] + city: DF.Data + country: DF.Link + county: DF.Data | None + disabled: DF.Check + email_id: DF.Data | None + fax: DF.Data | None + is_primary_address: DF.Check + is_shipping_address: DF.Check + links: DF.Table[DynamicLink] + phone: DF.Data | None + pincode: DF.Data | None + state: DF.Data | None + + # end: auto-generated types + def __setup__(self): + self.flags.linked = False + + def autoname(self): + if not self.address_title: + if self.links: + self.address_title = self.links[0].link_name + + if self.address_title: + self.name = cstr(self.address_title).strip() + "-" + cstr(_(self.address_type)).strip() + if xhiveframework.db.exists("Address", self.name): + self.name = make_autoname( + cstr(self.address_title).strip() + "-" + cstr(self.address_type).strip() + "-.#", + ignore_validate=True, + ) + else: + throw(_("Address Title is mandatory.")) + + def validate(self): + self.link_address() + self.validate_preferred_address() + set_link_title(self) + deduplicate_dynamic_links(self) + + def link_address(self): + """Link address based on owner""" + if not self.links: + contact_name = xhiveframework.db.get_value("Contact", {"email_id": self.owner}) + if contact_name: + contact = xhiveframework.get_cached_doc("Contact", contact_name) + for link in contact.links: + self.append("links", dict(link_doctype=link.link_doctype, link_name=link.link_name)) + return True + + return False + + def validate_preferred_address(self): + preferred_fields = ["is_primary_address", "is_shipping_address"] + + for field in preferred_fields: + if self.get(field): + for link in self.links: + address = get_preferred_address(link.link_doctype, link.link_name, field) + + if address: + update_preferred_address(address, field) + + def get_display(self): + return get_address_display(self.as_dict()) + + def has_link(self, doctype, name): + for link in self.links: + if link.link_doctype == doctype and link.link_name == name: + return True + + def has_common_link(self, doc): + reference_links = [(link.link_doctype, link.link_name) for link in doc.links] + for link in self.links: + if (link.link_doctype, link.link_name) in reference_links: + return True + + return False + + +def get_preferred_address(doctype, name, preferred_key="is_primary_address"): + if preferred_key in ["is_shipping_address", "is_primary_address"]: + address = xhiveframework.db.sql( + """ SELECT + addr.name + FROM + `tabAddress` addr, `tabDynamic Link` dl + WHERE + dl.parent = addr.name and dl.link_doctype = {} and + dl.link_name = {} and ifnull(addr.disabled, 0) = 0 and + {} = {} + """.format("%s", "%s", preferred_key, "%s"), + (doctype, name, 1), + as_dict=1, + ) + + if address: + return address[0].name + + return + + +@xhiveframework.whitelist() +def get_default_address(doctype: str, name: str | None, sort_key: str = "is_primary_address") -> str | None: + """Returns default Address name for the given doctype, name""" + if sort_key not in ["is_shipping_address", "is_primary_address"]: + return None + + addresses = xhiveframework.get_all( + "Address", + filters=[ + ["Dynamic Link", "link_doctype", "=", doctype], + ["Dynamic Link", "link_name", "=", name], + ["disabled", "=", 0], + ], + pluck="name", + order_by=f"{sort_key} DESC", + limit=1, + ) + + return addresses[0] if addresses else None + + +@xhiveframework.whitelist() +def get_address_display(address_dict: dict | str | None) -> str | None: + return render_address(address_dict) + + +def render_address(address: dict | str | None, check_permissions=True) -> str | None: + if not address: + return + + if not isinstance(address, dict): + address = xhiveframework.get_cached_doc("Address", address) + if check_permissions: + address.check_permission() + address = address.as_dict() + + name, template = get_address_templates(address) + + try: + return xhiveframework.render_template(template, address) + except TemplateSyntaxError: + xhiveframework.throw(_("There is an error in your Address Template {0}").format(name)) + + +def get_territory_from_address(address): + """Tries to match city, state and country of address to existing territory""" + if not address: + return + + if isinstance(address, str): + address = xhiveframework.get_cached_doc("Address", address) + + territory = None + for fieldname in ("city", "state", "country"): + if address.get(fieldname): + territory = xhiveframework.db.get_value("Territory", address.get(fieldname)) + if territory: + break + + return territory + + +def get_list_context(context=None): + return { + "title": _("Addresses"), + "get_list": get_address_list, + "row_template": "templates/includes/address_row.html", + "no_breadcrumbs": True, + } + + +def get_address_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by=None): + from xhiveframework.www.list import get_list + + user = xhiveframework.session.user + ignore_permissions = True + + if not filters: + filters = [] + filters.append(("Address", "owner", "=", user)) + + return get_list( + doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=ignore_permissions + ) + + +def has_website_permission(doc, ptype, user, verbose=False): + """Returns true if there is a related lead or contact related to this document""" + contact_name = xhiveframework.db.get_value("Contact", {"email_id": xhiveframework.session.user}) + + if contact_name: + contact = xhiveframework.get_doc("Contact", contact_name) + return contact.has_common_link(doc) + + return False + + +def get_address_templates(address): + result = xhiveframework.db.get_value( + "Address Template", {"country": address.get("country")}, ["name", "template"] + ) + + if not result: + result = xhiveframework.db.get_value("Address Template", {"is_default": 1}, ["name", "template"]) + + if not result: + xhiveframework.throw( + _( + "No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template." + ) + ) + else: + return result + + +def get_company_address(company): + ret = xhiveframework._dict() + + if company: + ret.company_address = get_default_address("Company", company) + ret.company_address_display = render_address(ret.company_address, check_permissions=False) + + return ret + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def address_query(doctype, txt, searchfield, start, page_len, filters): + from xhiveframework.desk.reportview import get_match_cond + + doctype = "Address" + link_doctype = filters.pop("link_doctype") + link_name = filters.pop("link_name") + + condition = "" + meta = xhiveframework.get_meta(doctype) + for fieldname, value in filters.items(): + if meta.get_field(fieldname) or fieldname in xhiveframework.db.DEFAULT_COLUMNS: + condition += f" and {fieldname}={xhiveframework.db.escape(value)}" + + searchfields = meta.get_search_fields() + + if searchfield and (meta.get_field(searchfield) or searchfield in xhiveframework.db.DEFAULT_COLUMNS): + searchfields.append(searchfield) + + search_condition = "" + for field in searchfields: + if search_condition == "": + search_condition += f"`tabAddress`.`{field}` like %(txt)s" + else: + search_condition += f" or `tabAddress`.`{field}` like %(txt)s" + + return xhiveframework.db.sql( + """select + `tabAddress`.name, `tabAddress`.city, `tabAddress`.country + from + `tabAddress` + join `tabDynamic Link` + on (`tabDynamic Link`.parent = `tabAddress`.name and `tabDynamic Link`.parenttype = 'Address') + where + `tabDynamic Link`.link_doctype = %(link_doctype)s and + `tabDynamic Link`.link_name = %(link_name)s and + ifnull(`tabAddress`.disabled, 0) = 0 and + ({search_condition}) + {mcond} {condition} + order by + case + when locate(%(_txt)s, `tabAddress`.name) != 0 + then locate(%(_txt)s, `tabAddress`.name) + else 99999 + end, + `tabAddress`.idx desc, `tabAddress`.name + limit %(page_len)s offset %(start)s""".format( + mcond=get_match_cond(doctype), + search_condition=search_condition, + condition=condition or "", + ), + { + "txt": "%" + txt + "%", + "_txt": txt.replace("%", ""), + "start": start, + "page_len": page_len, + "link_name": link_name, + "link_doctype": link_doctype, + }, + ) + + +def get_condensed_address(doc): + fields = ["address_title", "address_line1", "address_line2", "city", "county", "state", "country"] + return ", ".join(doc.get(d) for d in fields if doc.get(d)) + + +def update_preferred_address(address, field): + xhiveframework.db.set_value("Address", address, field, 0) + + +def get_address_display_list(doctype: str, name: str) -> list[dict]: + if not xhiveframework.has_permission("Address", "read"): + return [] + + address_list = xhiveframework.get_list( + "Address", + filters=[ + ["Dynamic Link", "link_doctype", "=", doctype], + ["Dynamic Link", "link_name", "=", name], + ["Dynamic Link", "parenttype", "=", "Address"], + ], + fields=["*"], + order_by="is_primary_address DESC, creation ASC", + ) + for a in address_list: + a["display"] = get_address_display(a) + + return address_list diff --git a/xhiveframework/contacts/doctype/address/test_address.py b/xhiveframework/contacts/doctype/address/test_address.py new file mode 100644 index 0000000..5a5eea3 --- /dev/null +++ b/xhiveframework/contacts/doctype/address/test_address.py @@ -0,0 +1,58 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from functools import partial + +import xhiveframework +from xhiveframework.contacts.doctype.address.address import address_query, get_address_display +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestAddress(XhiveFrameworkTestCase): + def test_template_works(self): + if not xhiveframework.db.exists("Address Template", "India"): + xhiveframework.get_doc({"doctype": "Address Template", "country": "India", "is_default": 1}).insert() + + if not xhiveframework.db.exists("Address", "_Test Address-Office"): + xhiveframework.get_doc( + { + "address_line1": "_Test Address Line 1", + "address_title": "_Test Address", + "address_type": "Office", + "city": "_Test City", + "state": "Test State", + "country": "India", + "doctype": "Address", + "is_primary_address": 1, + "phone": "+91 0000000000", + } + ).insert() + + address = xhiveframework.get_list("Address")[0].name + display = get_address_display(xhiveframework.get_doc("Address", address).as_dict()) + self.assertTrue(display) + + def test_address_query(self): + def query(doctype="Address", txt="", searchfield="name", start=0, page_len=20, filters=None): + if filters is None: + filters = {"link_doctype": "User", "link_name": "Administrator"} + return address_query(doctype, txt, searchfield, start, page_len, filters) + + xhiveframework.get_doc( + { + "address_type": "Billing", + "address_line1": "1", + "city": "Mumbai", + "state": "Maharashtra", + "country": "India", + "doctype": "Address", + "links": [ + { + "link_doctype": "User", + "link_name": "Administrator", + } + ], + } + ).insert() + + self.assertGreaterEqual(len(query(txt="Admin")), 1) + self.assertEqual(len(query(txt="what_zyx")), 0) diff --git a/xhiveframework/contacts/doctype/address_template/__init__.py b/xhiveframework/contacts/doctype/address_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/address_template/address_template.jinja b/xhiveframework/contacts/doctype/address_template/address_template.jinja new file mode 100644 index 0000000..65ea58e --- /dev/null +++ b/xhiveframework/contacts/doctype/address_template/address_template.jinja @@ -0,0 +1,10 @@ +{{ address_line1 }}
    +{% if address_line2 %}{{ address_line2 }}
    {% endif -%} +{{ city }}
    +{% if state %}{{ state }}
    {% endif -%} +{% if pincode %}{{ pincode }}
    {% endif -%} +{{ country }}
    +
    +{% if phone %}{{ _("Phone") }}: {{ phone }}
    {% endif -%} +{% if fax %}{{ _("Fax") }}: {{ fax }}
    {% endif -%} +{% if email_id %}{{ _("Email") }}: {{ email_id }}
    {% endif -%} diff --git a/xhiveframework/contacts/doctype/address_template/address_template.js b/xhiveframework/contacts/doctype/address_template/address_template.js new file mode 100644 index 0000000..ee77359 --- /dev/null +++ b/xhiveframework/contacts/doctype/address_template/address_template.js @@ -0,0 +1,16 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Address Template", { + refresh: function (frm) { + if (frm.is_new() && !frm.doc.template) { + // set default template via js so that it is translated + xhiveframework.call({ + method: "xhiveframework.contacts.doctype.address_template.address_template.get_default_address_template", + callback: function (r) { + frm.set_value("template", r.message); + }, + }); + } + }, +}); diff --git a/xhiveframework/contacts/doctype/address_template/address_template.json b/xhiveframework/contacts/doctype/address_template/address_template.json new file mode 100644 index 0000000..58b8210 --- /dev/null +++ b/xhiveframework/contacts/doctype/address_template/address_template.json @@ -0,0 +1,64 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:country", + "creation": "2014-06-05 02:22:36.029850", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "country", + "is_default", + "template" + ], + "fields": [ + { + "fieldname": "country", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Country", + "options": "Country", + "reqd": 1, + "search_index": 1, + "unique": 1 + }, + { + "default": "0", + "description": "This format is used if country specific format is not found", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Default" + }, + { + "description": "

    Default Template

    \n

    Uses Jinja Templating and all the fields of Address (including Custom Fields if any) will be available

    \n
    {{ address_line1 }}<br>\n{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\n{{ city }}<br>\n{% if state %}{{ state }}<br>{% endif -%}\n{% if pincode %} PIN:  {{ pincode }}<br>{% endif -%}\n{{ country }}<br>\n{% if phone %}Phone: {{ phone }}<br>{% endif -%}\n{% if fax %}Fax: {{ fax }}<br>{% endif -%}\n{% if email_id %}Email: {{ email_id }}<br>{% endif -%}\n
    ", + "fieldname": "template", + "fieldtype": "Code", + "label": "Template" + } + ], + "icon": "fa fa-map-marker", + "links": [], + "modified": "2022-08-03 12:20:49.095228", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Address Template", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "export": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/xhiveframework/contacts/doctype/address_template/address_template.py b/xhiveframework/contacts/doctype/address_template/address_template.py new file mode 100644 index 0000000..624947f --- /dev/null +++ b/xhiveframework/contacts/doctype/address_template/address_template.py @@ -0,0 +1,52 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils.jinja import validate_template + + +class AddressTemplate(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + country: DF.Link + is_default: DF.Check + template: DF.Code | None + + # end: auto-generated types + def validate(self): + validate_template(self.template) + + if not self.template: + self.template = get_default_address_template() + + if not self.is_default and not self._get_previous_default(): + self.is_default = 1 + if xhiveframework.db.get_single_value("System Settings", "setup_complete"): + xhiveframework.msgprint(_("Setting this Address Template as default as there is no other default")) + + def on_update(self): + if self.is_default and (previous_default := self._get_previous_default()): + xhiveframework.db.set_value("Address Template", previous_default, "is_default", 0) + + def on_trash(self): + if self.is_default: + xhiveframework.throw(_("Default Address Template cannot be deleted")) + + def _get_previous_default(self) -> str | None: + return xhiveframework.db.get_value("Address Template", {"is_default": 1, "name": ("!=", self.name)}) + + +@xhiveframework.whitelist() +def get_default_address_template() -> str: + """Return the default address template.""" + from pathlib import Path + + return (Path(__file__).parent / "address_template.jinja").read_text() diff --git a/xhiveframework/contacts/doctype/address_template/test_address_template.py b/xhiveframework/contacts/doctype/address_template/test_address_template.py new file mode 100644 index 0000000..b46e90c --- /dev/null +++ b/xhiveframework/contacts/doctype/address_template/test_address_template.py @@ -0,0 +1,37 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.contacts.doctype.address_template.address_template import get_default_address_template +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils.jinja import validate_template + + +class TestAddressTemplate(XhiveFrameworkTestCase): + def setUp(self) -> None: + xhiveframework.db.delete("Address Template", {"country": "India"}) + xhiveframework.db.delete("Address Template", {"country": "Brazil"}) + + def test_default_address_template(self): + validate_template(get_default_address_template()) + + def test_default_is_unset(self): + xhiveframework.get_doc({"doctype": "Address Template", "country": "India", "is_default": 1}).insert() + + self.assertEqual(xhiveframework.db.get_value("Address Template", "India", "is_default"), 1) + + xhiveframework.get_doc({"doctype": "Address Template", "country": "Brazil", "is_default": 1}).insert() + + self.assertEqual(xhiveframework.db.get_value("Address Template", "India", "is_default"), 0) + self.assertEqual(xhiveframework.db.get_value("Address Template", "Brazil", "is_default"), 1) + + def test_delete_address_template(self): + india = xhiveframework.get_doc({"doctype": "Address Template", "country": "India", "is_default": 0}).insert() + + brazil = xhiveframework.get_doc( + {"doctype": "Address Template", "country": "Brazil", "is_default": 1} + ).insert() + + india.reload() # might have been modified by the second template + india.delete() # should not raise an error + + self.assertRaises(xhiveframework.ValidationError, brazil.delete) diff --git a/xhiveframework/contacts/doctype/contact/__init__.py b/xhiveframework/contacts/doctype/contact/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/contact/contact.js b/xhiveframework/contacts/doctype/contact/contact.js new file mode 100644 index 0000000..09becf0 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/contact.js @@ -0,0 +1,156 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Contact", { + onload(frm) { + frm.email_field = "email_id"; + }, + refresh: function (frm) { + if (frm.doc.__islocal) { + const last_doc = xhiveframework.contacts.get_last_doc(frm); + if ( + xhiveframework.dynamic_link && + xhiveframework.dynamic_link.doc && + xhiveframework.dynamic_link.doc.name == last_doc.docname + ) { + frm.set_value("links", ""); + frm.add_child("links", { + link_doctype: xhiveframework.dynamic_link.doctype, + link_name: xhiveframework.dynamic_link.doc[xhiveframework.dynamic_link.fieldname], + }); + } + } + + if ( + !frm.doc.user && + !frm.is_new() && + frm.perm[0].write && + xhiveframework.boot.user.can_create.includes("User") + ) { + frm.add_custom_button(__("Invite as User"), function () { + return xhiveframework.call({ + method: "xhiveframework.contacts.doctype.contact.contact.invite_user", + args: { + contact: frm.doc.name, + }, + callback: function (r) { + frm.set_value("user", r.message); + }, + }); + }); + } + frm.set_query("link_doctype", "links", function () { + return { + query: "xhiveframework.contacts.address_and_contact.filter_dynamic_link_doctypes", + filters: { + fieldtype: "HTML", + fieldname: "contact_html", + }, + }; + }); + frm.refresh_field("links"); + + let numbers = frm.doc.phone_nos; + if (numbers && numbers.length && xhiveframework.phone_call.handler) { + frm.add_custom_button(__("Call"), () => { + numbers = frm.doc.phone_nos + .sort((prev, next) => next.is_primary_mobile_no - prev.is_primary_mobile_no) + .map((d) => d.phone); + xhiveframework.phone_call.handler(numbers); + }); + } + + if (frm.doc.links) { + xhiveframework.call({ + method: "xhiveframework.contacts.doctype.contact.contact.address_query", + args: { links: frm.doc.links }, + callback: function (r) { + if (r && r.message) { + frm.set_query("address", function () { + return { + filters: { + name: ["in", r.message], + }, + }; + }); + } + }, + }); + + for (let i in frm.doc.links) { + let link = frm.doc.links[i]; + frm.add_custom_button( + __("{0}: {1}", [__(link.link_doctype), __(link.link_name)]), + function () { + xhiveframework.set_route("Form", link.link_doctype, link.link_name); + }, + __("Links") + ); + } + } + }, + validate: function (frm) { + // clear linked customer / supplier / sales partner on saving... + if (frm.doc.links) { + frm.doc.links.forEach(function (d) { + xhiveframework.model.remove_from_locals(d.link_doctype, d.link_name); + }); + } + }, + after_save: function (frm) { + xhiveframework.run_serially([ + () => xhiveframework.timeout(1), + () => { + const last_doc = xhiveframework.contacts.get_last_doc(frm); + if ( + xhiveframework.dynamic_link && + xhiveframework.dynamic_link.doc && + xhiveframework.dynamic_link.doc.name == last_doc.docname + ) { + for (let i in frm.doc.links) { + let link = frm.doc.links[i]; + if ( + last_doc.doctype == link.link_doctype && + last_doc.docname == link.link_name + ) { + xhiveframework.set_route("Form", last_doc.doctype, last_doc.docname); + } + } + } + }, + ]); + }, + sync_with_google_contacts: function (frm) { + if (frm.doc.sync_with_google_contacts) { + xhiveframework.db.get_value( + "Google Contacts", + { email_id: xhiveframework.session.user }, + "name", + (r) => { + if (r && r.name) { + frm.set_value("google_contacts", r.name); + } + } + ); + } + }, +}); + +xhiveframework.ui.form.on("Dynamic Link", { + link_name: function (frm, cdt, cdn) { + var child = locals[cdt][cdn]; + if (child.link_name) { + xhiveframework.model.with_doctype(child.link_doctype, function () { + var title_field = xhiveframework.get_meta(child.link_doctype).title_field || "name"; + xhiveframework.model.get_value( + child.link_doctype, + child.link_name, + title_field, + function (r) { + xhiveframework.model.set_value(cdt, cdn, "link_title", r[title_field]); + } + ); + }); + } + }, +}); diff --git a/xhiveframework/contacts/doctype/contact/contact.json b/xhiveframework/contacts/doctype/contact/contact.json new file mode 100644 index 0000000..83d1002 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/contact.json @@ -0,0 +1,398 @@ +{ + "actions": [], + "allow_events_in_timeline": 1, + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:32", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "contact_section", + "first_name", + "middle_name", + "last_name", + "full_name", + "email_id", + "user", + "address", + "sync_with_google_contacts", + "cb00", + "status", + "salutation", + "designation", + "gender", + "phone", + "mobile_no", + "company_name", + "image", + "sb_00", + "google_contacts", + "google_contacts_id", + "cb_00", + "pulled_from_google_contacts", + "sb_01", + "email_ids", + "phone_nos", + "contact_details", + "links", + "is_primary_contact", + "more_info", + "department", + "unsubscribed" + ], + "fields": [ + { + "fieldname": "contact_section", + "fieldtype": "Section Break", + "options": "fa fa-user" + }, + { + "fieldname": "first_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "First Name", + "oldfieldname": "first_name", + "oldfieldtype": "Data" + }, + { + "bold": 1, + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name", + "oldfieldname": "last_name", + "oldfieldtype": "Data" + }, + { + "bold": 1, + "fieldname": "email_id", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Email Address", + "oldfieldname": "email_id", + "oldfieldtype": "Data", + "options": "Email", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_global_search": 1, + "label": "User Id", + "options": "User" + }, + { + "fieldname": "cb00", + "fieldtype": "Column Break" + }, + { + "default": "Passive", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Passive\nOpen\nReplied" + }, + { + "fieldname": "salutation", + "fieldtype": "Link", + "label": "Salutation", + "options": "Salutation" + }, + { + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender" + }, + { + "bold": 1, + "fieldname": "phone", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Phone", + "oldfieldname": "contact_no", + "oldfieldtype": "Data", + "options": "Phone", + "read_only": 1 + }, + { + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image", + "print_hide": 1 + }, + { + "fieldname": "contact_details", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin" + }, + { + "default": "0", + "fieldname": "is_primary_contact", + "fieldtype": "Check", + "label": "Is Primary Contact", + "oldfieldname": "is_primary_contact", + "oldfieldtype": "Select" + }, + { + "fieldname": "links", + "fieldtype": "Table", + "label": "Links", + "options": "Dynamic Link" + }, + { + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Information", + "options": "fa fa-file-text" + }, + { + "fieldname": "department", + "fieldtype": "Data", + "label": "Department" + }, + { + "fieldname": "designation", + "fieldtype": "Data", + "label": "Designation" + }, + { + "default": "0", + "fieldname": "unsubscribed", + "fieldtype": "Check", + "label": "Unsubscribed" + }, + { + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.sync_with_google_contacts || doc.pulled_from_google_contacts", + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Google Contacts" + }, + { + "fieldname": "email_ids", + "fieldtype": "Table", + "label": "Email IDs", + "options": "Contact Email" + }, + { + "fieldname": "address", + "fieldtype": "Link", + "label": "Address", + "options": "Address" + }, + { + "fieldname": "phone_nos", + "fieldtype": "Table", + "label": "Contact Numbers", + "options": "Contact Phone" + }, + { + "fieldname": "mobile_no", + "fieldtype": "Data", + "label": "Mobile No", + "options": "Phone", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "pulled_from_google_contacts", + "fieldtype": "Check", + "label": "Pulled from Google Contacts", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "sync_with_google_contacts", + "fieldtype": "Check", + "label": "Sync with Google Contacts" + }, + { + "fieldname": "google_contacts", + "fieldtype": "Link", + "label": "Google Contacts", + "options": "Google Contacts" + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "fieldname": "sb_01", + "fieldtype": "Section Break", + "label": "Contact Details" + }, + { + "fieldname": "google_contacts_id", + "fieldtype": "Data", + "label": "Google Contacts Id", + "read_only": 1 + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company Name" + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Full Name", + "read_only": 1 + } + ], + "icon": "fa fa-user", + "idx": 1, + "image_field": "image", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Contact", + "name_case": "Title Case", + "naming_rule": "By script", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Master Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Maintenance Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Maintenance User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "if_owner": 1, + "read": 1, + "report": 1, + "role": "All", + "write": 1 + } + ], + "show_title_field_in_link": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "full_name" +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/contact/contact.py b/xhiveframework/contacts/doctype/contact/contact.py new file mode 100644 index 0000000..6bfb2c1 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/contact.py @@ -0,0 +1,414 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework import _ +from xhiveframework.contacts.address_and_contact import set_link_title +from xhiveframework.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links +from xhiveframework.model.document import Document +from xhiveframework.model.naming import append_number_if_name_exists +from xhiveframework.utils import cstr, has_gravatar + + +class Contact(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.contacts.doctype.contact_email.contact_email import ContactEmail + from xhiveframework.contacts.doctype.contact_phone.contact_phone import ContactPhone + from xhiveframework.core.doctype.dynamic_link.dynamic_link import DynamicLink + from xhiveframework.types import DF + + address: DF.Link | None + company_name: DF.Data | None + department: DF.Data | None + designation: DF.Data | None + email_id: DF.Data | None + email_ids: DF.Table[ContactEmail] + first_name: DF.Data | None + full_name: DF.Data | None + gender: DF.Link | None + google_contacts: DF.Link | None + google_contacts_id: DF.Data | None + image: DF.AttachImage | None + is_primary_contact: DF.Check + last_name: DF.Data | None + links: DF.Table[DynamicLink] + middle_name: DF.Data | None + mobile_no: DF.Data | None + phone: DF.Data | None + phone_nos: DF.Table[ContactPhone] + pulled_from_google_contacts: DF.Check + salutation: DF.Link | None + status: DF.Literal["Passive", "Open", "Replied"] + sync_with_google_contacts: DF.Check + unsubscribed: DF.Check + user: DF.Link | None + + # end: auto-generated types + def autoname(self): + self.name = self._get_full_name() + + # concat party name if reqd + for link in self.links: + self.name = self.name + "-" + link.link_name.strip() + break + + if xhiveframework.db.exists("Contact", self.name): + self.name = append_number_if_name_exists("Contact", self.name) + + def validate(self): + self.full_name = self._get_full_name() + self.set_primary_email() + self.set_primary("phone") + self.set_primary("mobile_no") + + self.set_user() + + set_link_title(self) + + if self.email_id and not self.image: + self.image = has_gravatar(self.email_id) + + if self.get("sync_with_google_contacts") and not self.get("google_contacts"): + xhiveframework.throw(_("Select Google Contacts to which contact should be synced.")) + + deduplicate_dynamic_links(self) + + def set_user(self): + if not self.user and self.email_id: + self.user = xhiveframework.db.get_value("User", {"email": self.email_id}) + + def get_link_for(self, link_doctype): + """Return the link name, if exists for the given link DocType""" + for link in self.links: + if link.link_doctype == link_doctype: + return link.link_name + + return None + + def has_link(self, doctype, name): + for link in self.links: + if link.link_doctype == doctype and link.link_name == name: + return True + + def has_common_link(self, doc): + reference_links = [(link.link_doctype, link.link_name) for link in doc.links] + for link in self.links: + if (link.link_doctype, link.link_name) in reference_links: + return True + + def add_email(self, email_id, is_primary=0, autosave=False): + if not xhiveframework.db.exists("Contact Email", {"email_id": email_id, "parent": self.name}): + self.append("email_ids", {"email_id": email_id, "is_primary": is_primary}) + + if autosave: + self.save(ignore_permissions=True) + + def add_phone(self, phone, is_primary_phone=0, is_primary_mobile_no=0, autosave=False): + if not xhiveframework.db.exists("Contact Phone", {"phone": phone, "parent": self.name}): + self.append( + "phone_nos", + { + "phone": phone, + "is_primary_phone": is_primary_phone, + "is_primary_mobile_no": is_primary_mobile_no, + }, + ) + + if autosave: + self.save(ignore_permissions=True) + + def set_primary_email(self): + if not self.email_ids: + self.email_id = "" + return + + if len([email.email_id for email in self.email_ids if email.is_primary]) > 1: + xhiveframework.throw(_("Only one {0} can be set as primary.").format(xhiveframework.bold("Email ID"))) + + primary_email_exists = False + for d in self.email_ids: + if d.is_primary == 1: + primary_email_exists = True + self.email_id = d.email_id.strip() + break + + if not primary_email_exists: + self.email_id = "" + + def set_primary(self, fieldname): + # Used to set primary mobile and phone no. + if len(self.phone_nos) == 0: + setattr(self, fieldname, "") + return + + field_name = "is_primary_" + fieldname + + is_primary = [phone.phone for phone in self.phone_nos if phone.get(field_name)] + + if len(is_primary) > 1: + xhiveframework.throw( + _("Only one {0} can be set as primary.").format(xhiveframework.bold(xhiveframework.unscrub(fieldname))) + ) + + primary_number_exists = False + for d in self.phone_nos: + if d.get(field_name) == 1: + primary_number_exists = True + setattr(self, fieldname, d.phone) + break + + if not primary_number_exists: + setattr(self, fieldname, "") + + def _get_full_name(self) -> str: + return get_full_name(self.first_name, self.middle_name, self.last_name, self.company_name) + + +def get_default_contact(doctype, name): + """Returns default contact for the given doctype, name""" + out = xhiveframework.db.sql( + """select parent, + IFNULL((select is_primary_contact from tabContact c where c.name = dl.parent), 0) + as is_primary_contact + from + `tabDynamic Link` dl + where + dl.link_doctype=%s and + dl.link_name=%s and + dl.parenttype = 'Contact' """, + (doctype, name), + as_dict=True, + ) + + if out: + for contact in out: + if contact.is_primary_contact: + return contact.parent + return out[0].parent + else: + return None + + +@xhiveframework.whitelist() +def invite_user(contact: str): + contact = xhiveframework.get_doc("Contact", contact) + contact.check_permission() + + if not contact.email_id: + xhiveframework.throw(_("Please set Email Address")) + + user = xhiveframework.get_doc( + { + "doctype": "User", + "first_name": contact.first_name, + "last_name": contact.last_name, + "email": contact.email_id, + "user_type": "Website User", + "send_welcome_email": 1, + } + ).insert() + + return user.name + + +@xhiveframework.whitelist() +def get_contact_details(contact): + contact = xhiveframework.get_doc("Contact", contact) + contact.check_permission() + + return { + "contact_person": contact.get("name"), + "contact_display": contact.get("full_name"), + "contact_email": contact.get("email_id"), + "contact_mobile": contact.get("mobile_no"), + "contact_phone": contact.get("phone"), + "contact_designation": contact.get("designation"), + "contact_department": contact.get("department"), + } + + +def update_contact(doc, method): + """Update contact when user is updated, if contact is found. Called via hooks""" + contact_name = xhiveframework.db.get_value("Contact", {"email_id": doc.name}) + if contact_name: + contact = xhiveframework.get_doc("Contact", contact_name) + for key in ("first_name", "last_name", "phone"): + if doc.get(key): + contact.set(key, doc.get(key)) + contact.flags.ignore_mandatory = True + contact.save(ignore_permissions=True) + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def contact_query(doctype, txt, searchfield, start, page_len, filters): + from xhiveframework.desk.reportview import get_match_cond + + doctype = "Contact" + if not xhiveframework.get_meta(doctype).get_field(searchfield) and searchfield not in xhiveframework.db.DEFAULT_COLUMNS: + return [] + + link_doctype = filters.pop("link_doctype") + link_name = filters.pop("link_name") + + return xhiveframework.db.sql( + f"""select + `tabContact`.name, `tabContact`.full_name, `tabContact`.company_name + from + `tabContact`, `tabDynamic Link` + where + `tabDynamic Link`.parent = `tabContact`.name and + `tabDynamic Link`.parenttype = 'Contact' and + `tabDynamic Link`.link_doctype = %(link_doctype)s and + `tabDynamic Link`.link_name = %(link_name)s and + `tabContact`.`{searchfield}` like %(txt)s + {get_match_cond(doctype)} + order by + if(locate(%(_txt)s, `tabContact`.full_name), locate(%(_txt)s, `tabContact`.company_name), 99999), + `tabContact`.idx desc, `tabContact`.full_name + limit %(start)s, %(page_len)s """, + { + "txt": "%" + txt + "%", + "_txt": txt.replace("%", ""), + "start": start, + "page_len": page_len, + "link_name": link_name, + "link_doctype": link_doctype, + }, + ) + + +@xhiveframework.whitelist() +def address_query(links): + import json + + links = [ + {"link_doctype": d.get("link_doctype"), "link_name": d.get("link_name")} for d in json.loads(links) + ] + result = [] + + for link in links: + if not xhiveframework.has_permission( + doctype=link.get("link_doctype"), ptype="read", doc=link.get("link_name") + ): + continue + + res = xhiveframework.db.sql( + """ + SELECT `tabAddress`.name + FROM `tabAddress`, `tabDynamic Link` + WHERE `tabDynamic Link`.parenttype='Address' + AND `tabDynamic Link`.parent=`tabAddress`.name + AND `tabDynamic Link`.link_doctype = %(link_doctype)s + AND `tabDynamic Link`.link_name = %(link_name)s + """, + { + "link_doctype": link.get("link_doctype"), + "link_name": link.get("link_name"), + }, + as_dict=True, + ) + + result.extend([l.name for l in res]) + + return result + + +def get_contact_with_phone_number(number): + if not number: + return + + contacts = xhiveframework.get_all( + "Contact Phone", filters=[["phone", "like", f"%{number}"]], fields=["parent"], limit=1 + ) + + return contacts[0].parent if contacts else None + + +def get_contact_name(email_id): + contact = xhiveframework.get_all("Contact Email", filters={"email_id": email_id}, fields=["parent"], limit=1) + return contact[0].parent if contact else None + + +def get_contacts_linking_to(doctype, docname, fields=None): + """Return a list of contacts containing a link to the given document.""" + return xhiveframework.get_list( + "Contact", + fields=fields, + filters=[ + ["Dynamic Link", "link_doctype", "=", doctype], + ["Dynamic Link", "link_name", "=", docname], + ], + ) + + +def get_contacts_linked_from(doctype, docname, fields=None): + """Return a list of contacts that are contained in (linked from) the given document.""" + link_fields = xhiveframework.get_meta(doctype).get("fields", {"fieldtype": "Link", "options": "Contact"}) + if not link_fields: + return [] + + contact_names = xhiveframework.get_value(doctype, docname, fieldname=[f.fieldname for f in link_fields]) + if not contact_names: + return [] + + return xhiveframework.get_list("Contact", fields=fields, filters={"name": ("in", contact_names)}) + + +def get_full_name( + first: str | None = None, + middle: str | None = None, + last: str | None = None, + company: str | None = None, +) -> str: + full_name = " ".join(filter(None, [cstr(f).strip() for f in [first, middle, last]])) + if not full_name and company: + full_name = company + + return full_name + + +def get_contact_display_list(doctype: str, name: str) -> list[dict]: + from xhiveframework.contacts.doctype.address.address import get_condensed_address + + if not xhiveframework.has_permission("Contact", "read"): + return [] + + contact_list = xhiveframework.get_list( + "Contact", + filters=[ + ["Dynamic Link", "link_doctype", "=", doctype], + ["Dynamic Link", "link_name", "=", name], + ["Dynamic Link", "parenttype", "=", "Contact"], + ], + fields=["*"], + order_by="is_primary_contact DESC, creation ASC", + ) + + for contact in contact_list: + contact["email_ids"] = xhiveframework.get_all( + "Contact Email", + filters={"parenttype": "Contact", "parent": contact.name, "is_primary": 0}, + fields=["email_id"], + ) + + contact["phone_nos"] = xhiveframework.get_all( + "Contact Phone", + filters={ + "parenttype": "Contact", + "parent": contact.name, + "is_primary_phone": 0, + "is_primary_mobile_no": 0, + }, + fields=["phone"], + ) + + if contact.address and xhiveframework.has_permission("Address", "read"): + address = xhiveframework.get_doc("Address", contact.address) + contact["address"] = get_condensed_address(address) + + return contact_list diff --git a/xhiveframework/contacts/doctype/contact/contact_list.js b/xhiveframework/contacts/doctype/contact/contact_list.js new file mode 100644 index 0000000..2f5f539 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/contact_list.js @@ -0,0 +1,3 @@ +xhiveframework.listview_settings["Contact"] = { + add_fields: ["image"], +}; diff --git a/xhiveframework/contacts/doctype/contact/test_contact.py b/xhiveframework/contacts/doctype/contact/test_contact.py new file mode 100644 index 0000000..de9d288 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/test_contact.py @@ -0,0 +1,78 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.contacts.doctype.contact.contact import get_full_name +from xhiveframework.email import get_contact_list +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["Contact", "Salutation"] + + +class TestContact(XhiveFrameworkTestCase): + def test_check_default_email(self): + emails = [ + {"email": "test1@example.com", "is_primary": 0}, + {"email": "test2@example.com", "is_primary": 0}, + {"email": "test3@example.com", "is_primary": 0}, + {"email": "test4@example.com", "is_primary": 1}, + {"email": "test5@example.com", "is_primary": 0}, + ] + contact = create_contact("Email", "Mr", emails=emails) + + self.assertEqual(contact.email_id, "test4@example.com") + + def test_check_default_phone_and_mobile(self): + phones = [ + {"phone": "+91 0000000000", "is_primary_phone": 0, "is_primary_mobile_no": 0}, + {"phone": "+91 0000000001", "is_primary_phone": 0, "is_primary_mobile_no": 0}, + {"phone": "+91 0000000002", "is_primary_phone": 1, "is_primary_mobile_no": 0}, + {"phone": "+91 0000000003", "is_primary_phone": 0, "is_primary_mobile_no": 1}, + ] + contact = create_contact("Phone", "Mr", phones=phones) + + self.assertEqual(contact.phone, "+91 0000000002") + self.assertEqual(contact.mobile_no, "+91 0000000003") + + def test_get_full_name(self): + self.assertEqual(get_full_name(first="John"), "John") + self.assertEqual(get_full_name(last="Doe"), "Doe") + self.assertEqual(get_full_name(company="Doe Pvt Ltd"), "Doe Pvt Ltd") + self.assertEqual(get_full_name(first="John", last="Doe"), "John Doe") + self.assertEqual(get_full_name(first="John", middle="Jane"), "John Jane") + self.assertEqual(get_full_name(first="John", last="Doe", company="Doe Pvt Ltd"), "John Doe") + self.assertEqual( + get_full_name(first="John", middle="Jane", last="Doe", company="Doe Pvt Ltd"), + "John Jane Doe", + ) + + def test_get_contact_list(self): + # First time from database + results = get_contact_list("_Test Supplier") + self.assertEqual(results[0].label, "test_contact@example.com") + self.assertEqual(results[0].value, "test_contact@example.com") + self.assertEqual(results[0].description, "_Test Contact For _Test Supplier") + + # Second time from cache + results = get_contact_list("_Test Supplier") + self.assertEqual(results[0].label, "test_contact@example.com") + self.assertEqual(results[0].value, "test_contact@example.com") + self.assertEqual(results[0].description, "_Test Contact For _Test Supplier") + + +def create_contact(name, salutation, emails=None, phones=None, save=True): + doc = xhiveframework.get_doc( + {"doctype": "Contact", "first_name": name, "status": "Open", "salutation": salutation} + ) + + if emails: + for d in emails: + doc.add_email(d.get("email"), d.get("is_primary")) + + if phones: + for d in phones: + doc.add_phone(d.get("phone"), d.get("is_primary_phone"), d.get("is_primary_mobile_no")) + + if save: + doc.insert() + + return doc diff --git a/xhiveframework/contacts/doctype/contact/test_records.json b/xhiveframework/contacts/doctype/contact/test_records.json new file mode 100644 index 0000000..35fff58 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact/test_records.json @@ -0,0 +1,39 @@ +[ + { + "doctype": "Contact", + "salutation": "Mr", + "first_name": "_Test Contact For _Test Customer", + "is_primary_contact": 1, + "status": "Open", + "email_ids": [ + { + "email_id": "test_contact@example.com", + "is_primary": 1 + } + ], + "phone_nos": [ + { + "phone": "+91 0000000000", + "is_primary_phone": 1 + } + ] + }, + { + "doctype": "Contact", + "first_name": "_Test Contact For _Test Supplier", + "is_primary_contact": 1, + "status": "Open", + "email_ids": [ + { + "email_id": "test_contact@example.com", + "is_primary": 1 + } + ], + "phone_nos": [ + { + "phone": "+91 0000000001", + "is_primary_phone": 1 + } + ] + } +] diff --git a/xhiveframework/contacts/doctype/contact_email/__init__.py b/xhiveframework/contacts/doctype/contact_email/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/contact_email/contact_email.json b/xhiveframework/contacts/doctype/contact_email/contact_email.json new file mode 100644 index 0000000..f36e155 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact_email/contact_email.json @@ -0,0 +1,39 @@ +{ + "creation": "2019-08-02 13:08:59.291097", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email_id", + "is_primary" + ], + "fields": [ + { + "fieldname": "email_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email ID", + "options": "Email", + "reqd": 1 + }, + { + "columns": 2, + "default": "0", + "fieldname": "is_primary", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Primary" + } + ], + "istable": 1, + "modified": "2019-09-24 17:47:30.565805", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Contact Email", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/contact_email/contact_email.py b/xhiveframework/contacts/doctype/contact_email/contact_email.py new file mode 100644 index 0000000..31f9cb1 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact_email/contact_email.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class ContactEmail(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email_id: DF.Data + is_primary: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/contacts/doctype/contact_phone/__init__.py b/xhiveframework/contacts/doctype/contact_phone/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/contact_phone/contact_phone.json b/xhiveframework/contacts/doctype/contact_phone/contact_phone.json new file mode 100644 index 0000000..5412e4a --- /dev/null +++ b/xhiveframework/contacts/doctype/contact_phone/contact_phone.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "creation": "2019-08-02 13:10:37.890214", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "phone", + "is_primary_phone", + "is_primary_mobile_no" + ], + "fields": [ + { + "fieldname": "phone", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Number", + "options": "Phone", + "reqd": 1 + }, + { + "columns": 2, + "default": "0", + "fieldname": "is_primary_phone", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Primary Phone" + }, + { + "columns": 2, + "default": "0", + "fieldname": "is_primary_mobile_no", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Primary Mobile" + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-06 18:28:10.486220", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Contact Phone", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/contact_phone/contact_phone.py b/xhiveframework/contacts/doctype/contact_phone/contact_phone.py new file mode 100644 index 0000000..6c0a241 --- /dev/null +++ b/xhiveframework/contacts/doctype/contact_phone/contact_phone.py @@ -0,0 +1,24 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class ContactPhone(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + is_primary_mobile_no: DF.Check + is_primary_phone: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + phone: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/contacts/doctype/gender/__init__.py b/xhiveframework/contacts/doctype/gender/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/gender/gender.js b/xhiveframework/contacts/doctype/gender/gender.js new file mode 100644 index 0000000..692e736 --- /dev/null +++ b/xhiveframework/contacts/doctype/gender/gender.js @@ -0,0 +1,6 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Gender", { + refresh: function () {}, +}); diff --git a/xhiveframework/contacts/doctype/gender/gender.json b/xhiveframework/contacts/doctype/gender/gender.json new file mode 100644 index 0000000..34e1dda --- /dev/null +++ b/xhiveframework/contacts/doctype/gender/gender.json @@ -0,0 +1,48 @@ +{ + "actions": [], + "autoname": "field:gender", + "creation": "2017-04-10 12:11:36.526508", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "gender" + ], + "fields": [ + { + "fieldname": "gender", + "fieldtype": "Data", + "label": "Gender", + "unique": 1 + } + ], + "links": [], + "modified": "2022-08-05 18:33:28.043370", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Gender", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "All" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/gender/gender.py b/xhiveframework/contacts/doctype/gender/gender.py new file mode 100644 index 0000000..93d603b --- /dev/null +++ b/xhiveframework/contacts/doctype/gender/gender.py @@ -0,0 +1,18 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class Gender(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + gender: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/contacts/doctype/gender/test_gender.py b/xhiveframework/contacts/doctype/gender/test_gender.py new file mode 100644 index 0000000..2cb968c --- /dev/null +++ b/xhiveframework/contacts/doctype/gender/test_gender.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestGender(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/contacts/doctype/salutation/__init__.py b/xhiveframework/contacts/doctype/salutation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/doctype/salutation/salutation.js b/xhiveframework/contacts/doctype/salutation/salutation.js new file mode 100644 index 0000000..6a0cf8a --- /dev/null +++ b/xhiveframework/contacts/doctype/salutation/salutation.js @@ -0,0 +1,6 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Salutation", { + refresh: function () {}, +}); diff --git a/xhiveframework/contacts/doctype/salutation/salutation.json b/xhiveframework/contacts/doctype/salutation/salutation.json new file mode 100644 index 0000000..98ed082 --- /dev/null +++ b/xhiveframework/contacts/doctype/salutation/salutation.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:salutation", + "creation": "2017-04-10 12:17:58.071915", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salutation" + ], + "fields": [ + { + "fieldname": "salutation", + "fieldtype": "Data", + "label": "Salutation", + "unique": 1 + } + ], + "links": [], + "modified": "2022-08-05 18:33:28.196387", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Salutation", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "All" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/salutation/salutation.py b/xhiveframework/contacts/doctype/salutation/salutation.py new file mode 100644 index 0000000..485553a --- /dev/null +++ b/xhiveframework/contacts/doctype/salutation/salutation.py @@ -0,0 +1,18 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class Salutation(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + salutation: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/contacts/doctype/salutation/test_records.json b/xhiveframework/contacts/doctype/salutation/test_records.json new file mode 100644 index 0000000..3a87fff --- /dev/null +++ b/xhiveframework/contacts/doctype/salutation/test_records.json @@ -0,0 +1,8 @@ +[ + { + "salutation": "Mr" + }, + { + "salutation": "Mrs" + } +] \ No newline at end of file diff --git a/xhiveframework/contacts/doctype/salutation/test_salutation.py b/xhiveframework/contacts/doctype/salutation/test_salutation.py new file mode 100644 index 0000000..8f004d9 --- /dev/null +++ b/xhiveframework/contacts/doctype/salutation/test_salutation.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSalutation(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/contacts/report/__init__.py b/xhiveframework/contacts/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/report/addresses_and_contacts/__init__.py b/xhiveframework/contacts/report/addresses_and_contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.js b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.js new file mode 100644 index 0000000..7815ae3 --- /dev/null +++ b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.js @@ -0,0 +1,33 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["Addresses And Contacts"] = { + filters: [ + { + reqd: 1, + fieldname: "reference_doctype", + label: __("Entity Type"), + fieldtype: "Link", + options: "DocType", + get_query: function () { + return { + filters: { + name: ["in", "Contact, Address"], + }, + }; + }, + }, + { + fieldname: "reference_name", + label: __("Entity Name"), + fieldtype: "Dynamic Link", + get_options: function () { + let reference_doctype = xhiveframework.query_report.get_filter_value("reference_doctype"); + if (!reference_doctype) { + xhiveframework.throw(__("Please select Entity Type first")); + } + return reference_doctype; + }, + }, + ], +}; diff --git a/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.json b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.json new file mode 100644 index 0000000..2d62444 --- /dev/null +++ b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2017-01-19 12:57:22.881566", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2017-04-10 15:04:12.498920", + "modified_by": "Administrator", + "module": "Contacts", + "name": "Addresses And Contacts", + "owner": "Administrator", + "ref_doctype": "Address", + "report_name": "Addresses And Contacts", + "report_type": "Script Report", + "roles": [ + { + "role": "Sales User" + }, + { + "role": "Purchase User" + }, + { + "role": "Maintenance User" + }, + { + "role": "Accounts User" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.py b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.py new file mode 100644 index 0000000..af3b780 --- /dev/null +++ b/xhiveframework/contacts/report/addresses_and_contacts/addresses_and_contacts.py @@ -0,0 +1,123 @@ +# Copyright (c) 2013, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework import _ + +field_map = { + "Contact": [ + "first_name", + "last_name", + "address", + "phone", + "mobile_no", + "email_id", + "is_primary_contact", + ], + "Address": [ + "address_line1", + "address_line2", + "city", + "state", + "pincode", + "country", + "is_primary_address", + ], +} + + +def execute(filters=None): + columns, data = get_columns(filters), get_data(filters) + return columns, data + + +def get_columns(filters): + return [ + "{reference_doctype}:Link/{reference_doctype}".format( + reference_doctype=filters.get("reference_doctype") + ), + "Address Line 1", + "Address Line 2", + "City", + "State", + "Postal Code", + "Country", + "Is Primary Address:Check", + "First Name", + "Last Name", + "Address", + "Phone", + "Email Id", + "Is Primary Contact:Check", + ] + + +def get_data(filters): + reference_doctype = filters.get("reference_doctype") + reference_name = filters.get("reference_name") + + return get_reference_addresses_and_contact(reference_doctype, reference_name) + + +def get_reference_addresses_and_contact(reference_doctype, reference_name): + data = [] + filters = None + reference_details = xhiveframework._dict() + + if not reference_doctype: + return [] + + if reference_name: + filters = {"name": reference_name} + + reference_list = [ + d[0] for d in xhiveframework.get_list(reference_doctype, filters=filters, fields=["name"], as_list=True) + ] + + for d in reference_list: + reference_details.setdefault(d, xhiveframework._dict()) + reference_details = get_reference_details(reference_doctype, "Address", reference_list, reference_details) + reference_details = get_reference_details(reference_doctype, "Contact", reference_list, reference_details) + + for reference_name, details in reference_details.items(): + addresses = details.get("address", []) + contacts = details.get("contact", []) + if not any([addresses, contacts]): + result = [reference_name] + result.extend(add_blank_columns_for("Address")) + result.extend(add_blank_columns_for("Contact")) + data.append(result) + else: + addresses = list(map(list, addresses)) + contacts = list(map(list, contacts)) + + max_length = max(len(addresses), len(contacts)) + for idx in range(0, max_length): + result = [reference_name] + + result.extend(addresses[idx] if idx < len(addresses) else add_blank_columns_for("Address")) + result.extend(contacts[idx] if idx < len(contacts) else add_blank_columns_for("Contact")) + + data.append(result) + + return data + + +def get_reference_details(reference_doctype, doctype, reference_list, reference_details): + filters = [ + ["Dynamic Link", "link_doctype", "=", reference_doctype], + ["Dynamic Link", "link_name", "in", reference_list], + ] + fields = ["`tabDynamic Link`.link_name", *field_map.get(doctype, [])] + + records = xhiveframework.get_list(doctype, filters=filters, fields=fields, as_list=True) + temp_records = [d[1:] for d in records] + + if not reference_list: + xhiveframework.throw(_("No records present in {0}").format(reference_doctype)) + + reference_details[reference_list[0]][xhiveframework.scrub(doctype)] = temp_records + return reference_details + + +def add_blank_columns_for(doctype): + return ["" for field in field_map.get(doctype, [])] diff --git a/xhiveframework/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py b/xhiveframework/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py new file mode 100644 index 0000000..b77c0e5 --- /dev/null +++ b/xhiveframework/contacts/report/addresses_and_contacts/test_addresses_and_contacts.py @@ -0,0 +1,113 @@ +import xhiveframework +import xhiveframework.defaults +from xhiveframework.contacts.report.addresses_and_contacts.addresses_and_contacts import get_data +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +def get_custom_linked_doctype(): + if bool(xhiveframework.get_all("DocType", filters={"name": "Test Custom Doctype"})): + return + + doc = xhiveframework.get_doc( + { + "doctype": "DocType", + "module": "Core", + "custom": 1, + "fields": [ + {"label": "Test Field", "fieldname": "test_field", "fieldtype": "Data"}, + {"label": "Contact HTML", "fieldname": "contact_html", "fieldtype": "HTML"}, + {"label": "Address HTML", "fieldname": "address_html", "fieldtype": "HTML"}, + ], + "permissions": [{"role": "System Manager", "read": 1}], + "name": "Test Custom Doctype", + } + ) + doc.insert() + + +def get_custom_doc_for_address_and_contacts(): + get_custom_linked_doctype() + return xhiveframework.get_doc( + { + "doctype": "Test Custom Doctype", + "test_field": "Hello", + } + ).insert() + + +def create_linked_address(link_list): + if xhiveframework.flags.test_address_created: + return + + address = xhiveframework.get_doc( + { + "doctype": "Address", + "address_title": "_Test Address", + "address_type": "Billing", + "address_line1": "test address line 1", + "address_line2": "test address line 2", + "city": "Milan", + "country": "Italy", + } + ) + + for name in link_list: + address.append("links", {"link_doctype": "Test Custom Doctype", "link_name": name}) + + address.insert() + xhiveframework.flags.test_address_created = True + + return address.name + + +def create_linked_contact(link_list, address): + if xhiveframework.flags.test_contact_created: + return + + contact = xhiveframework.get_doc( + { + "doctype": "Contact", + "salutation": "Mr", + "first_name": "_Test First Name", + "last_name": "_Test Last Name", + "is_primary_contact": 1, + "address": address, + "status": "Open", + } + ) + contact.add_email("test_contact@example.com", is_primary=True) + contact.add_phone("+91 0000000000", is_primary_phone=True) + + for name in link_list: + contact.append("links", {"link_doctype": "Test Custom Doctype", "link_name": name}) + + contact.insert(ignore_permissions=True) + xhiveframework.flags.test_contact_created = True + + +class TestAddressesAndContacts(XhiveFrameworkTestCase): + def test_get_data(self): + linked_docs = [get_custom_doc_for_address_and_contacts()] + links_list = [item.name for item in linked_docs] + d = create_linked_address(links_list) + create_linked_contact(links_list, d) + report_data = get_data({"reference_doctype": "Test Custom Doctype"}) + for idx, link in enumerate(links_list): + test_item = [ + link, + "test address line 1", + "test address line 2", + "Milan", + None, + None, + "Italy", + 0, + "_Test First Name", + "_Test Last Name", + "_Test Address-Billing", + "+91 0000000000", + "", + "test_contact@example.com", + 1, + ] + self.assertListEqual(test_item, report_data[idx]) diff --git a/xhiveframework/core/README.md b/xhiveframework/core/README.md new file mode 100644 index 0000000..41ca7fd --- /dev/null +++ b/xhiveframework/core/README.md @@ -0,0 +1 @@ +Core module contains the models required for the basic functioning of xhiveframework including DocType, User (user), Role and others. diff --git a/xhiveframework/core/__init__.py b/xhiveframework/core/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/api/__init__.py b/xhiveframework/core/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/api/file.py b/xhiveframework/core/api/file.py new file mode 100644 index 0000000..f67a462 --- /dev/null +++ b/xhiveframework/core/api/file.py @@ -0,0 +1,124 @@ +import json + +import xhiveframework +from xhiveframework.core.doctype.file.file import File +from xhiveframework.core.doctype.file.utils import setup_folder_path +from xhiveframework.utils import cint, cstr + + +@xhiveframework.whitelist() +def unzip_file(name: str): + """Unzip the given file and make file records for each of the extracted files""" + file: File = xhiveframework.get_doc("File", name) + return file.unzip() + + +@xhiveframework.whitelist() +def get_attached_images(doctype: str, names: list[str] | str) -> xhiveframework._dict: + """get list of image urls attached in form + returns {name: ['image.jpg', 'image.png']}""" + + if isinstance(names, str): + names = json.loads(names) + + img_urls = xhiveframework.db.get_list( + "File", + filters={ + "attached_to_doctype": doctype, + "attached_to_name": ("in", names), + "is_folder": 0, + }, + fields=["file_url", "attached_to_name as docname"], + ) + + out = xhiveframework._dict() + for i in img_urls: + out[i.docname] = out.get(i.docname, []) + out[i.docname].append(i.file_url) + + return out + + +@xhiveframework.whitelist() +def get_files_in_folder(folder: str, start: int = 0, page_length: int = 20) -> dict: + attachment_folder = xhiveframework.db.get_value( + "File", + "Home/Attachments", + ["name", "file_name", "file_url", "is_folder", "modified"], + as_dict=1, + ) + + files = xhiveframework.get_list( + "File", + {"folder": folder}, + ["name", "file_name", "file_url", "is_folder", "modified"], + start=start, + page_length=page_length + 1, + ) + + if folder == "Home" and attachment_folder not in files: + files.insert(0, attachment_folder) + + return {"files": files[:page_length], "has_more": len(files) > page_length} + + +@xhiveframework.whitelist() +def get_files_by_search_text(text: str) -> list[dict]: + if not text: + return [] + + text = "%" + cstr(text).lower() + "%" + return xhiveframework.get_list( + "File", + fields=["name", "file_name", "file_url", "is_folder", "modified"], + filters={"is_folder": False}, + or_filters={ + "file_name": ("like", text), + "file_url": text, + "name": ("like", text), + }, + order_by="modified desc", + limit=20, + ) + + +@xhiveframework.whitelist(allow_guest=True) +def get_max_file_size() -> int: + return ( + cint(xhiveframework.get_system_settings("max_file_size")) * 1024 * 1024 + or cint(xhiveframework.conf.get("max_file_size")) + or 25 * 1024 * 1024 + ) + + +@xhiveframework.whitelist() +def create_new_folder(file_name: str, folder: str) -> File: + """create new folder under current parent folder""" + file = xhiveframework.new_doc("File") + file.file_name = file_name + file.is_folder = 1 + file.folder = folder + file.insert(ignore_if_duplicate=True) + return file + + +@xhiveframework.whitelist() +def move_file(file_list: list[File | dict] | str, new_parent: str, old_parent: str) -> None: + if isinstance(file_list, str): + file_list = json.loads(file_list) + + # will check for permission on each file & update parent + for file_obj in file_list: + setup_folder_path(file_obj.get("name"), new_parent) + + # recalculate sizes + xhiveframework.get_doc("File", old_parent).save() + xhiveframework.get_doc("File", new_parent).save() + + +@xhiveframework.whitelist() +def zip_files(files: str): + files = xhiveframework.parse_json(files) + xhiveframework.response["filename"] = "files.zip" + xhiveframework.response["filecontent"] = File.zip_files(files) + xhiveframework.response["type"] = "download" diff --git a/xhiveframework/core/doctype/__init__.py b/xhiveframework/core/doctype/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/access_log/__init__.py b/xhiveframework/core/doctype/access_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/access_log/access_log.js b/xhiveframework/core/doctype/access_log/access_log.js new file mode 100644 index 0000000..fe839fc --- /dev/null +++ b/xhiveframework/core/doctype/access_log/access_log.js @@ -0,0 +1,17 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Access Log", { + show_document: function (frm) { + xhiveframework.set_route("Form", frm.doc.export_from, frm.doc.reference_document); + }, + + show_report: function (frm) { + if (frm.doc.report_name.includes("/")) { + xhiveframework.set_route(frm.doc.report_name); + } else { + let filters = frm.doc.filters ? JSON.parse(frm.doc.filters) : {}; + xhiveframework.set_route("query-report", frm.doc.report_name, filters); + } + }, +}); diff --git a/xhiveframework/core/doctype/access_log/access_log.json b/xhiveframework/core/doctype/access_log/access_log.json new file mode 100644 index 0000000..69803ef --- /dev/null +++ b/xhiveframework/core/doctype/access_log/access_log.json @@ -0,0 +1,156 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2019-07-25 15:44:44.955496", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "log_data_section", + "export_from", + "user", + "show_document", + "column_break_3", + "reference_document", + "timestamp", + "private_file_section", + "file_type", + "method", + "report_information_section", + "report_name", + "filters", + "show_report", + "raw_information_log_section", + "page", + "columns" + ], + "fields": [ + { + "fieldname": "export_from", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Export From", + "read_only": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User ", + "options": "User", + "read_only": 1 + }, + { + "default": "Now", + "fieldname": "timestamp", + "fieldtype": "Datetime", + "label": "Timestamp", + "read_only": 1 + }, + { + "fieldname": "reference_document", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Document", + "read_only": 1 + }, + { + "fieldname": "file_type", + "fieldtype": "Data", + "label": "File Type", + "read_only": 1 + }, + { + "fieldname": "report_name", + "fieldtype": "Data", + "label": "Report Name", + "read_only": 1 + }, + { + "fieldname": "page", + "fieldtype": "HTML Editor", + "label": "HTML Page", + "read_only": 1 + }, + { + "fieldname": "log_data_section", + "fieldtype": "Section Break", + "label": "Log Data" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "private_file_section", + "fieldtype": "Section Break", + "label": "File Information" + }, + { + "fieldname": "report_information_section", + "fieldtype": "Section Break", + "label": "Report Information" + }, + { + "fieldname": "raw_information_log_section", + "fieldtype": "Section Break", + "label": "RAW Information Log" + }, + { + "fieldname": "method", + "fieldtype": "Data", + "label": "Method", + "read_only": 1 + }, + { + "depends_on": "report_name", + "fieldname": "show_report", + "fieldtype": "Button", + "label": "Show Report" + }, + { + "depends_on": "reference_document", + "fieldname": "show_document", + "fieldtype": "Button", + "label": "Show Document" + }, + { + "depends_on": "eval: doc.filters != null", + "fieldname": "filters", + "fieldtype": "Code", + "label": "Filters", + "read_only": 1 + }, + { + "fieldname": "columns", + "fieldtype": "HTML Editor", + "label": "Columns / Fields", + "read_only": 1 + } + ], + "links": [], + "modified": "2022-06-13 05:59:26.866004", + "modified_by": "Administrator", + "module": "Core", + "name": "Access Log", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_seen": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/access_log/access_log.py b/xhiveframework/core/doctype/access_log/access_log.py new file mode 100644 index 0000000..26b6a18 --- /dev/null +++ b/xhiveframework/core/doctype/access_log/access_log.py @@ -0,0 +1,107 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +from tenacity import retry, retry_if_exception_type, stop_after_attempt + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.utils import cstr + + +class AccessLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + columns: DF.HTMLEditor | None + export_from: DF.Data | None + file_type: DF.Data | None + filters: DF.Code | None + method: DF.Data | None + page: DF.HTMLEditor | None + reference_document: DF.Data | None + report_name: DF.Data | None + timestamp: DF.Datetime | None + user: DF.Link | None + + # end: auto-generated types + @staticmethod + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Access Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + +@xhiveframework.whitelist() +def make_access_log( + doctype=None, + document=None, + method=None, + file_type=None, + report_name=None, + filters=None, + page=None, + columns=None, +): + _make_access_log( + doctype, + document, + method, + file_type, + report_name, + filters, + page, + columns, + ) + + +@xhiveframework.write_only() +@retry( + stop=stop_after_attempt(3), + retry=retry_if_exception_type(xhiveframework.DuplicateEntryError), + reraise=True, +) +def _make_access_log( + doctype=None, + document=None, + method=None, + file_type=None, + report_name=None, + filters=None, + page=None, + columns=None, +): + user = xhiveframework.session.user + in_request = xhiveframework.request and xhiveframework.request.method == "GET" + + access_log = xhiveframework.get_doc( + { + "doctype": "Access Log", + "user": user, + "export_from": doctype, + "reference_document": document, + "file_type": file_type, + "report_name": report_name, + "page": page, + "method": method, + "filters": cstr(filters) or None, + "columns": columns, + } + ) + + if xhiveframework.flags.read_only: + access_log.deferred_insert() + return + else: + access_log.db_insert() + + # `xhiveframework.db.commit` added because insert doesnt `commit` when called in GET requests like `printview` + # dont commit in test mode. It must be tempting to put this block along with the in_request in the + # whitelisted method...yeah, don't do it. That part would be executed possibly on a read only DB conn + if not xhiveframework.flags.in_test or in_request: + xhiveframework.db.commit() diff --git a/xhiveframework/core/doctype/access_log/access_log_list.js b/xhiveframework/core/doctype/access_log/access_log_list.js new file mode 100644 index 0000000..e437309 --- /dev/null +++ b/xhiveframework/core/doctype/access_log/access_log_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Access Log"] = { + onload: function (list_view) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(list_view.doctype); + }); + }, +}; diff --git a/xhiveframework/core/doctype/access_log/test_access_log.py b/xhiveframework/core/doctype/access_log/test_access_log.py new file mode 100644 index 0000000..60573e5 --- /dev/null +++ b/xhiveframework/core/doctype/access_log/test_access_log.py @@ -0,0 +1,175 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +import base64 +import os + +# imports - third party imports +import requests + +# imports - module imports +import xhiveframework +from xhiveframework.core.doctype.access_log.access_log import make_access_log +from xhiveframework.core.doctype.data_import.data_import import export_csv +from xhiveframework.core.doctype.user.user import generate_keys + +# imports - standard imports +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import cstr, get_site_url + + +class TestAccessLog(XhiveFrameworkTestCase): + def setUp(self): + # generate keys for current user to send requests for the following tests + generate_keys(xhiveframework.session.user) + xhiveframework.db.commit() + generated_secret = xhiveframework.utils.password.get_decrypted_password( + "User", xhiveframework.session.user, fieldname="api_secret" + ) + api_key = xhiveframework.db.get_value("User", "Administrator", "api_key") + self.header = {"Authorization": f"token {api_key}:{generated_secret}"} + + self.test_html_template = """ + + + + + + + +

    HTML Table

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CompanyContactCountry
    Alfreds FutterkisteMaria AndersGermany
    Centro comercial MoctezumaFrancisco ChangMexico
    Ernst HandelRoland MendelAustria
    Island TradingHelen BennettUK
    Laughing Bacchus WinecellarsYoshi TannamuriCanada
    Magazzini Alimentari RiunitiGiovanni RovelliItaly
    + + + + """ + self.test_filters = { + "from_date": "2019-06-30", + "to_date": "2019-07-31", + "party": [], + "group_by": "Group by Voucher (Consolidated)", + "cost_center": [], + "project": [], + } + + self.test_doctype = "File" + self.test_document = "Test Document" + self.test_report_name = "General Ledger" + self.test_file_type = "CSV" + self.test_method = "Test Method" + self.file_name = xhiveframework.utils.random_string(10) + ".txt" + self.test_content = xhiveframework.utils.random_string(1024) + + def test_make_full_access_log(self): + self.maxDiff = None + + # test if all fields maintain data: html page and filters are converted? + make_access_log( + doctype=self.test_doctype, + document=self.test_document, + report_name=self.test_report_name, + page=self.test_html_template, + file_type=self.test_file_type, + method=self.test_method, + filters=self.test_filters, + ) + + last_doc = xhiveframework.get_last_doc("Access Log") + self.assertEqual(last_doc.filters, cstr(self.test_filters)) + self.assertEqual(self.test_doctype, last_doc.export_from) + self.assertEqual(self.test_document, last_doc.reference_document) + + def test_make_export_log(self): + # export data and delete temp file generated on disk + export_csv(self.test_doctype, self.file_name) + os.remove(self.file_name) + + # test if the exported data is logged + last_doc = xhiveframework.get_last_doc("Access Log") + self.assertEqual(self.test_doctype, last_doc.export_from) + + def test_private_file_download(self): + # create new private file + new_private_file = xhiveframework.get_doc( + { + "doctype": self.test_doctype, + "file_name": self.file_name, + "content": base64.b64encode(self.test_content.encode("utf-8")), + "is_private": 1, + } + ) + new_private_file.insert() + + # access the created file + private_file_link = get_site_url(xhiveframework.local.site) + new_private_file.file_url + + try: + request = requests.post(private_file_link, headers=self.header) + last_doc = xhiveframework.get_last_doc("Access Log") + + if request.ok: + # check for the access log of downloaded file + self.assertEqual(new_private_file.doctype, last_doc.export_from) + self.assertEqual(new_private_file.name, last_doc.reference_document) + + except requests.ConnectionError: + pass + + # cleanup + new_private_file.delete() + + def tearDown(self): + pass diff --git a/xhiveframework/core/doctype/activity_log/__init__.py b/xhiveframework/core/doctype/activity_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/activity_log/activity_log.js b/xhiveframework/core/doctype/activity_log/activity_log.js new file mode 100644 index 0000000..fa3b2bb --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/activity_log.js @@ -0,0 +1,9 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Activity Log", { + refresh: function (frm) { + // Nothing in this form is supposed to be editable. + frm.disable_form(); + }, +}); diff --git a/xhiveframework/core/doctype/activity_log/activity_log.json b/xhiveframework/core/doctype/activity_log/activity_log.json new file mode 100644 index 0000000..3943fc1 --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/activity_log.json @@ -0,0 +1,181 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2017-10-05 11:10:38.780133", + "description": "Keep track of all update feeds", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "subject", + "section_break_8", + "content", + "column_break_5", + "additional_info", + "communication_date", + "ip_address", + "column_break_7", + "operation", + "status", + "reference_section", + "reference_doctype", + "reference_name", + "reference_owner", + "column_break_14", + "timeline_doctype", + "timeline_name", + "link_doctype", + "link_name", + "user", + "full_name" + ], + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Subject", + "reqd": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Message", + "width": "400" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "More Information" + }, + { + "default": "Now", + "fieldname": "communication_date", + "fieldtype": "Datetime", + "label": "Date" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "operation", + "fieldtype": "Select", + "label": "Operation", + "options": "\nLogin\nLogout\nImpersonate" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "\nSuccess\nFailed\nLinked\nClosed" + }, + { + "collapsible": 1, + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, + { + "fetch_from": "reference_name.owner", + "fieldname": "reference_owner", + "fieldtype": "Read Only", + "label": "Reference Owner" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "timeline_doctype", + "fieldtype": "Link", + "label": "Timeline DocType", + "options": "DocType" + }, + { + "fieldname": "timeline_name", + "fieldtype": "Dynamic Link", + "label": "Timeline Name", + "options": "timeline_doctype" + }, + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "label": "Link DocType", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "link_name", + "fieldtype": "Dynamic Link", + "label": "Link Name", + "options": "link_doctype", + "read_only": 1 + }, + { + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name" + }, + { + "fieldname": "ip_address", + "fieldtype": "Data", + "label": "IP Address" + } + ], + "icon": "fa fa-comment", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-02-24 16:31:16.253153", + "modified_by": "Administrator", + "module": "Core", + "name": "Activity Log", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "search_fields": "subject", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "subject", + "track_seen": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/activity_log/activity_log.py b/xhiveframework/core/doctype/activity_log/activity_log.py new file mode 100644 index 0000000..b8ca93a --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/activity_log.py @@ -0,0 +1,81 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.core.utils import set_timeline_doc +from xhiveframework.model.document import Document +from xhiveframework.query_builder import DocType, Interval +from xhiveframework.query_builder.functions import Now +from xhiveframework.utils import get_fullname, now + + +class ActivityLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + communication_date: DF.Datetime | None + content: DF.TextEditor | None + full_name: DF.Data | None + ip_address: DF.Data | None + link_doctype: DF.Link | None + link_name: DF.DynamicLink | None + operation: DF.Literal["", "Login", "Logout", "Impersonate"] + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + reference_owner: DF.ReadOnly | None + status: DF.Literal["", "Success", "Failed", "Linked", "Closed"] + subject: DF.SmallText + timeline_doctype: DF.Link | None + timeline_name: DF.DynamicLink | None + user: DF.Link | None + + # end: auto-generated types + def before_insert(self): + self.full_name = get_fullname(self.user) + self.date = now() + + def validate(self): + self.set_status() + set_timeline_doc(self) + self.set_ip_address() + + def set_status(self): + if not self.is_new(): + return + + if self.reference_doctype and self.reference_name: + self.status = "Linked" + + def set_ip_address(self): + if self.operation in ("Login", "Logout"): + self.ip_address = xhiveframework.local.request_ip + + @staticmethod + def clear_old_logs(days=None): + if not days: + days = 90 + doctype = DocType("Activity Log") + xhiveframework.db.delete(doctype, filters=(doctype.modified < (Now() - Interval(days=days)))) + + +def on_doctype_update(): + """Add indexes in `tabActivity Log`""" + xhiveframework.db.add_index("Activity Log", ["reference_doctype", "reference_name"]) + xhiveframework.db.add_index("Activity Log", ["timeline_doctype", "timeline_name"]) + + +def add_authentication_log(subject, user, operation="Login", status="Success"): + xhiveframework.get_doc( + { + "doctype": "Activity Log", + "user": user, + "status": status, + "subject": subject, + "operation": operation, + } + ).insert(ignore_permissions=True, ignore_links=True) diff --git a/xhiveframework/core/doctype/activity_log/activity_log_list.js b/xhiveframework/core/doctype/activity_log/activity_log_list.js new file mode 100644 index 0000000..051adf2 --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/activity_log_list.js @@ -0,0 +1,12 @@ +xhiveframework.listview_settings["Activity Log"] = { + get_indicator: function (doc) { + if (doc.operation == "Login" && doc.status == "Success") return [__(doc.status), "green"]; + else if (doc.operation == "Login" && doc.status == "Failed") + return [__(doc.status), "red"]; + }, + onload: function (listview) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(cur_list.doctype); + }); + }, +}; diff --git a/xhiveframework/core/doctype/activity_log/feed.py b/xhiveframework/core/doctype/activity_log/feed.py new file mode 100644 index 0000000..2a288b0 --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/feed.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +import xhiveframework.permissions +from xhiveframework import _ +from xhiveframework.core.doctype.activity_log.activity_log import add_authentication_log +from xhiveframework.utils import get_fullname + + +def login_feed(login_manager): + if login_manager.user != "Guest": + subject = _("{0} logged in").format(get_fullname(login_manager.user)) + add_authentication_log(subject, login_manager.user) + + +def logout_feed(user, reason): + if user and user != "Guest": + subject = _("{0} logged out: {1}").format(get_fullname(user), xhiveframework.bold(reason)) + add_authentication_log(subject, user, operation="Logout") diff --git a/xhiveframework/core/doctype/activity_log/test_activity_log.py b/xhiveframework/core/doctype/activity_log/test_activity_log.py new file mode 100644 index 0000000..d7ad4ee --- /dev/null +++ b/xhiveframework/core/doctype/activity_log/test_activity_log.py @@ -0,0 +1,95 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import time + +import xhiveframework +from xhiveframework.auth import CookieManager, LoginManager +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestActivityLog(XhiveFrameworkTestCase): + def test_activity_log(self): + # test user login log + xhiveframework.local.form_dict = xhiveframework._dict( + { + "cmd": "login", + "sid": "Guest", + "pwd": xhiveframework.conf.admin_password or "admin", + "usr": "Administrator", + } + ) + + xhiveframework.local.request_ip = "127.0.0.1" + xhiveframework.local.cookie_manager = CookieManager() + xhiveframework.local.login_manager = LoginManager() + + auth_log = self.get_auth_log() + self.assertFalse(xhiveframework.form_dict.pwd) + self.assertEqual(auth_log.status, "Success") + + # test user logout log + xhiveframework.local.login_manager.logout() + auth_log = self.get_auth_log(operation="Logout") + self.assertEqual(auth_log.status, "Success") + + # test invalid login + xhiveframework.form_dict.update({"pwd": "password"}) + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + auth_log = self.get_auth_log() + self.assertEqual(auth_log.status, "Failed") + + xhiveframework.local.form_dict = xhiveframework._dict() + + def get_auth_log(self, operation="Login"): + names = xhiveframework.get_all( + "Activity Log", + filters={ + "user": "Administrator", + "operation": operation, + }, + order_by="`creation` DESC", + ) + + name = names[0] + return xhiveframework.get_doc("Activity Log", name) + + def test_brute_security(self): + update_system_settings({"allow_consecutive_login_attempts": 3, "allow_login_after_fail": 5}) + + xhiveframework.local.form_dict = xhiveframework._dict( + {"cmd": "login", "sid": "Guest", "pwd": "admin", "usr": "Administrator"} + ) + + xhiveframework.local.request_ip = "127.0.0.1" + xhiveframework.local.cookie_manager = CookieManager() + xhiveframework.local.login_manager = LoginManager() + + auth_log = self.get_auth_log() + self.assertEqual(auth_log.status, "Success") + + # test user logout log + xhiveframework.local.login_manager.logout() + auth_log = self.get_auth_log(operation="Logout") + self.assertEqual(auth_log.status, "Success") + + # test invalid login + xhiveframework.form_dict.update({"pwd": "password"}) + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + + # REMOVE ME: current logic allows allow_consecutive_login_attempts+1 attempts + # before raising security exception, remove below line when that is fixed. + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + self.assertRaises(xhiveframework.SecurityException, LoginManager) + time.sleep(5) + self.assertRaises(xhiveframework.AuthenticationError, LoginManager) + + xhiveframework.local.form_dict = xhiveframework._dict() + + +def update_system_settings(args): + doc = xhiveframework.get_doc("System Settings") + doc.update(args) + doc.flags.ignore_mandatory = 1 + doc.save() diff --git a/xhiveframework/core/doctype/amended_document_naming_settings/__init__.py b/xhiveframework/core/doctype/amended_document_naming_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.json b/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.json new file mode 100644 index 0000000..2892cc6 --- /dev/null +++ b/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-06-16 17:57:36.604672", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "action" + ], + "fields": [ + { + "default": "Amend Counter", + "fieldname": "action", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Action", + "options": "Amend Counter\nDefault Naming", + "reqd": 1 + }, + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1, + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-06-16 18:26:16.247475", + "modified_by": "Administrator", + "module": "Core", + "name": "Amended Document Naming Settings", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.py b/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.py new file mode 100644 index 0000000..59dede5 --- /dev/null +++ b/xhiveframework/core/doctype/amended_document_naming_settings/amended_document_naming_settings.py @@ -0,0 +1,23 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class AmendedDocumentNamingSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action: DF.Literal["Amend Counter", "Default Naming"] + document_type: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/audit_trail/__init__.py b/xhiveframework/core/doctype/audit_trail/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/audit_trail/audit_trail.html b/xhiveframework/core/doctype/audit_trail/audit_trail.html new file mode 100644 index 0000000..74ed663 --- /dev/null +++ b/xhiveframework/core/doctype/audit_trail/audit_trail.html @@ -0,0 +1,77 @@ +
    + +
    + {% if documents.length > 1 %} +

    Documents to Compare : {{ documents.length }}

    + {% else %} +

    Documents to Compare : {{ documents.length - 1 }}

    + {% endif %} +
    + + {% var field_keys = Object.keys(changed).sort(); %} + {% if field_keys.length > 0 %} +
    +
    Fields Changed
    + + + + {% for doc in documents %} + + {% endfor %} + + + {% for fieldname in field_keys %} + + + {% var values = changed[fieldname] %} + + {% for value in values %} + + {% endfor %} + + {% endfor %} + +
    Fields {{ doc }}
    {{ fieldname }} {{ value }}
    +
    + {% endif %} + + {% var tables = Object.keys(row_changed).sort(); %} + {% if tables.length > 0 %} +
    +
    Rows Updated
    + + + + + + {% for table in tables %} + + + + {% for doc in documents %} + + {% endfor %} + + + {% var rows = Object.keys(row_changed[table]).sort(); %} + {% for idx in rows %} + + {% var fields = Object.keys(row_changed[table][idx]).sort(); %} + {% for field in fields %} + + + {% var values = row_changed[table][idx][field] %} + {% for value in values %} + + {% endfor %} + + {% endfor %} + {% endfor %} + + {% endfor %} + +
    Fields
    {{ table }}{{ doc }}
    idx : {{ idx }}
    {{ field }} {{ value }}
    +
    + {% endif %} + +
    \ No newline at end of file diff --git a/xhiveframework/core/doctype/audit_trail/audit_trail.js b/xhiveframework/core/doctype/audit_trail/audit_trail.js new file mode 100644 index 0000000..7e2dfec --- /dev/null +++ b/xhiveframework/core/doctype/audit_trail/audit_trail.js @@ -0,0 +1,67 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Audit Trail", { + refresh(frm) { + frm.page.clear_indicator(); + + frm.disable_save(); + + frm.set_query("doctype_name", () => { + return { + filters: { + track_changes: 1, + is_submittable: 1, + }, + }; + }); + + frm.page.set_primary_action("Compare", () => { + frm.call({ + doc: frm.doc, + method: "compare_document", + callback: function (r) { + let document_names = r.message[0]; + let changed_fields = r.message[1]; + frm.events.render_changed_fields(frm, document_names, changed_fields); + frm.events.render_rows_added_or_removed(frm, changed_fields); + }, + }); + }); + }, + + render_changed_fields(frm, document_names, changed_fields) { + let render_dict = { + documents: document_names, + changed: changed_fields.changed, + row_changed: changed_fields.row_changed, + }; + $(xhiveframework.render_template("audit_trail", render_dict)).appendTo( + frm.fields_dict.version_table.$wrapper.empty() + ); + frm.set_df_property("version_table", "hidden", 0); + }, + + render_rows_added_or_removed(frm, changed_fields) { + let added_or_removed = { + rows_added: changed_fields.added, + rows_removed: changed_fields.removed, + }; + + let hide_section = 0; + let section_dict = {}; + + for (let key in added_or_removed) { + hide_section = 0; + section_dict = { + added_or_removed: added_or_removed[key], + }; + $(xhiveframework.render_template("audit_trail_rows_added_removed", section_dict)).appendTo( + frm.fields_dict[key].$wrapper.empty() + ); + + if (!frm.fields_dict[key].disp_area.innerHTML.includes(" + {% var docs = Object.keys(added_or_removed) %} + {% for doc in docs %} +
    + {% if Object.keys(added_or_removed[doc]).length > 0 %} +
    {{ doc }}
    +
    + {% var tables = Object.keys(added_or_removed[doc]) %} + {% for table in tables %} +
    {{ table }}
    + + + {% var fieldnames = Object.keys(added_or_removed[doc][table][0]) %} + {% for fieldname in fieldnames %} + + {% endfor %} + + + {% var rows = Object.keys(added_or_removed[doc][table]) %} + {% for row in rows %} + + {% var field_keys = Object.keys(added_or_removed[doc][table][row]) %} + {% for key in field_keys %} + + {% endfor %} + + {% endfor %} + +
    {{ fieldname }}
    {{ added_or_removed[doc][table][row][key] }}
    + {% endfor %} + {% endif %} +
    + {% endfor %} + \ No newline at end of file diff --git a/xhiveframework/core/doctype/audit_trail/test_audit_trail.py b/xhiveframework/core/doctype/audit_trail/test_audit_trail.py new file mode 100644 index 0000000..287733a --- /dev/null +++ b/xhiveframework/core/doctype/audit_trail/test_audit_trail.py @@ -0,0 +1,134 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestAuditTrail(XhiveFrameworkTestCase): + def setUp(self): + self.child_doctype = create_custom_child_doctype() + self.custom_doctype = create_custom_doctype() + + def test_compare_changed_fields(self): + doc = xhiveframework.new_doc("Test Custom Doctype for Doc Comparator") + doc.test_field = "first value" + doc.submit() + doc.cancel() + + changed_fields = xhiveframework._dict(test_field="second value") + amended_doc = amend_document(doc, changed_fields, {}, 1) + amended_doc.cancel() + + changed_fields = xhiveframework._dict(test_field="third value") + re_amended_doc = amend_document(amended_doc, changed_fields, {}, 1) + + comparator = create_comparator_doc("Test Custom Doctype for Doc Comparator", re_amended_doc.name) + documents, results = comparator.compare_document() + + test_field_values = results["changed"]["Field"] + self.check_expected_values(test_field_values, ["first value", "second value", "third value"]) + + def test_compare_rows(self): + doc = xhiveframework.new_doc("Test Custom Doctype for Doc Comparator") + doc.append("child_table_field", {"test_table_field": "old row 1 value"}) + doc.submit() + doc.cancel() + + child_table_new = [{"test_table_field": "new row 1 value"}, {"test_table_field": "row 2 value"}] + rows_updated = xhiveframework._dict(child_table_field=child_table_new) + amended_doc = amend_document(doc, {}, rows_updated, 1) + + comparator = create_comparator_doc("Test Custom Doctype for Doc Comparator", amended_doc.name) + documents, results = comparator.compare_document() + + results = xhiveframework._dict(results) + self.check_rows_updated(results.row_changed) + self.check_rows_added(results.added[amended_doc.name]) + + def check_rows_updated(self, row_changed): + self.assertIn("Child Table Field", row_changed) + self.assertIn(0, row_changed["Child Table Field"]) + self.assertIn("Table Field", row_changed["Child Table Field"][0]) + table_field_values = row_changed["Child Table Field"][0]["Table Field"] + self.check_expected_values(table_field_values, ["old row 1 value", "new row 1 value"]) + + def check_rows_added(self, rows_added): + self.assertIn("Child Table Field", rows_added) + child_table = rows_added["Child Table Field"] + self.assertIn("Table Field", child_table[0]) + self.check_expected_values(child_table[0]["Table Field"], "row 2 value") + + def check_expected_values(self, values_to_check, expected_values): + for i in range(len(values_to_check)): + self.assertEqual(values_to_check[i], expected_values[i]) + + def tearDown(self): + self.child_doctype.delete() + self.custom_doctype.delete() + + +def create_custom_child_doctype(): + child_doctype = xhiveframework.get_doc( + { + "doctype": "DocType", + "module": "Core", + "name": "Test Custom Child for Doc Comparator", + "custom": 1, + "istable": 1, + "fields": [ + { + "label": "Table Field", + "fieldname": "test_table_field", + "fieldtype": "Data", + "in_list_view": 1, + }, + ], + } + ).insert(ignore_if_duplicate=True) + return child_doctype + + +def create_custom_doctype(): + custom_doctype = xhiveframework.get_doc( + { + "doctype": "DocType", + "module": "Core", + "name": "Test Custom Doctype for Doc Comparator", + "custom": 1, + "is_submittable": 1, + "fields": [ + { + "label": "Field", + "fieldname": "test_field", + "fieldtype": "Data", + }, + { + "label": "Child Table Field", + "fieldname": "child_table_field", + "fieldtype": "Table", + "options": "Test Custom Child for Doc Comparator", + }, + ], + "permissions": [{"role": "System Manager", "read": 1}], + } + ).insert(ignore_if_duplicate=True) + return custom_doctype + + +def amend_document(amend_from, changed_fields, rows_updated, submit=False): + amended_doc = xhiveframework.copy_doc(amend_from) + amended_doc.amended_from = amend_from.name + amended_doc.update(changed_fields) + for child_table in rows_updated: + amended_doc.set(child_table, rows_updated[child_table]) + if submit: + amended_doc.submit() + return amended_doc + + +def create_comparator_doc(doctype_name, document): + comparator = xhiveframework.new_doc("Audit Trail") + comparator.doctype_name = doctype_name + comparator.document = document + return comparator diff --git a/xhiveframework/core/doctype/block_module/__init__.py b/xhiveframework/core/doctype/block_module/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/block_module/block_module.json b/xhiveframework/core/doctype/block_module/block_module.json new file mode 100644 index 0000000..9711aaa --- /dev/null +++ b/xhiveframework/core/doctype/block_module/block_module.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2015-03-24 14:28:15.882903", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "module" + ], + "fields": [ + { + "fieldname": "module", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Module", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:52.738977", + "modified_by": "Administrator", + "module": "Core", + "name": "Block Module", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/block_module/block_module.py b/xhiveframework/core/doctype/block_module/block_module.py new file mode 100644 index 0000000..a6b55ec --- /dev/null +++ b/xhiveframework/core/doctype/block_module/block_module.py @@ -0,0 +1,21 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class BlockModule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + module: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/comment/__init__.py b/xhiveframework/core/doctype/comment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/comment/comment.js b/xhiveframework/core/doctype/comment/comment.js new file mode 100644 index 0000000..b8ff0cb --- /dev/null +++ b/xhiveframework/core/doctype/comment/comment.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Comment", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/comment/comment.json b/xhiveframework/core/doctype/comment/comment.json new file mode 100644 index 0000000..529c1d2 --- /dev/null +++ b/xhiveframework/core/doctype/comment/comment.json @@ -0,0 +1,139 @@ +{ + "actions": [], + "creation": "2019-02-07 10:10:46.845678", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "comment_type", + "comment_email", + "subject", + "comment_by", + "published", + "seen", + "column_break_5", + "reference_doctype", + "reference_name", + "reference_owner", + "section_break_10", + "content", + "ip_address" + ], + "fields": [ + { + "default": "Comment", + "fieldname": "comment_type", + "fieldtype": "Select", + "label": "Comment Type", + "options": "Comment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked\nEdit", + "reqd": 1 + }, + { + "fieldname": "comment_email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Comment Email" + }, + { + "fieldname": "subject", + "fieldtype": "Text", + "label": "Subject" + }, + { + "fieldname": "comment_by", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Comment By" + }, + { + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Published" + }, + { + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "label": "Seen" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, + { + "fieldname": "reference_owner", + "fieldtype": "Data", + "label": "Reference Owner", + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "content", + "fieldtype": "HTML Editor", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Content" + }, + { + "fieldname": "ip_address", + "fieldtype": "Data", + "hidden": 1, + "label": "IP Address" + } + ], + "links": [], + "modified": "2023-07-24 13:52:55.651462", + "modified_by": "Administrator", + "module": "Core", + "name": "Comment", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "comment_type", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/comment/comment.py b/xhiveframework/core/doctype/comment/comment.py new file mode 100644 index 0000000..dc75f0a --- /dev/null +++ b/xhiveframework/core/doctype/comment/comment.py @@ -0,0 +1,207 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +import json + +import xhiveframework +from xhiveframework.database.schema import add_column +from xhiveframework.desk.notifications import notify_mentions +from xhiveframework.exceptions import ImplicitCommitError +from xhiveframework.model.document import Document +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.website.utils import clear_cache + + +class Comment(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + comment_by: DF.Data | None + comment_email: DF.Data | None + comment_type: DF.Literal[ + "Comment", + "Like", + "Info", + "Label", + "Workflow", + "Created", + "Submitted", + "Cancelled", + "Updated", + "Deleted", + "Assigned", + "Assignment Completed", + "Attachment", + "Attachment Removed", + "Shared", + "Unshared", + "Bot", + "Relinked", + "Edit", + ] + content: DF.HTMLEditor | None + ip_address: DF.Data | None + published: DF.Check + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + reference_owner: DF.Data | None + seen: DF.Check + subject: DF.Text | None + + # end: auto-generated types + def after_insert(self): + notify_mentions(self.reference_doctype, self.reference_name, self.content) + self.notify_change("add") + + def validate(self): + if not self.comment_email: + self.comment_email = xhiveframework.session.user + self.content = xhiveframework.utils.sanitize_html(self.content) + + def on_update(self): + update_comment_in_doc(self) + if self.is_new(): + self.notify_change("update") + + def on_trash(self): + self.remove_comment_from_cache() + self.notify_change("delete") + + def notify_change(self, action): + key_map = { + "Like": "like_logs", + "Assigned": "assignment_logs", + "Assignment Completed": "assignment_logs", + "Comment": "comments", + "Attachment": "attachment_logs", + "Attachment Removed": "attachment_logs", + } + key = key_map.get(self.comment_type) + if not key: + return + + xhiveframework.publish_realtime( + "docinfo_update", + {"doc": self.as_dict(), "key": key, "action": action}, + doctype=self.reference_doctype, + docname=self.reference_name, + after_commit=True, + ) + + def remove_comment_from_cache(self): + _comments = get_comments_from_parent(self) + for c in list(_comments): + if c.get("name") == self.name: + _comments.remove(c) + + update_comments_in_parent(self.reference_doctype, self.reference_name, _comments) + + +def on_doctype_update(): + xhiveframework.db.add_index("Comment", ["reference_doctype", "reference_name"]) + + +def update_comment_in_doc(doc): + """Updates `_comments` (JSON) property in parent Document. + Creates a column `_comments` if property does not exist. + + Only user created Communication or Comment of type Comment are saved. + + `_comments` format + + { + "comment": [String], + "by": [user], + "name": [Comment Document name] + }""" + + # only comments get updates, not likes, assignments etc. + if doc.doctype == "Comment" and doc.comment_type != "Comment": + return + + def get_truncated(content): + return (content[:97] + "...") if len(content) > 100 else content + + if doc.reference_doctype and doc.reference_name and doc.content: + _comments = get_comments_from_parent(doc) + + updated = False + for c in _comments: + if c.get("name") == doc.name: + c["comment"] = get_truncated(doc.content) + updated = True + + if not updated: + _comments.append( + { + "comment": get_truncated(doc.content), + # "comment_email" for Comment and "sender" for Communication + "by": getattr(doc, "comment_email", None) or getattr(doc, "sender", None) or doc.owner, + "name": doc.name, + } + ) + + update_comments_in_parent(doc.reference_doctype, doc.reference_name, _comments) + + +def get_comments_from_parent(doc): + """ + get the list of comments cached in the document record in the column + `_comments` + """ + try: + if is_virtual_doctype(doc.reference_doctype): + _comments = "[]" + else: + _comments = xhiveframework.db.get_value(doc.reference_doctype, doc.reference_name, "_comments") or "[]" + + except Exception as e: + if xhiveframework.db.is_missing_table_or_column(e): + _comments = "[]" + + else: + raise + + try: + return json.loads(_comments) + except ValueError: + return [] + + +def update_comments_in_parent(reference_doctype, reference_name, _comments): + """Updates `_comments` property in parent Document with given dict. + + :param _comments: Dict of comments.""" + if ( + not reference_doctype + or not reference_name + or xhiveframework.db.get_value("DocType", reference_doctype, "issingle") + or is_virtual_doctype(reference_doctype) + ): + return + + try: + # use sql, so that we do not mess with the timestamp + xhiveframework.db.sql( + f"""update `tab{reference_doctype}` set `_comments`=%s where name=%s""", # nosec + (json.dumps(_comments[-100:]), reference_name), + ) + + except Exception as e: + if xhiveframework.db.is_column_missing(e) and getattr(xhiveframework.local, "request", None): + pass + elif xhiveframework.db.is_data_too_long(e): + raise xhiveframework.DataTooLongException + else: + raise + else: + if xhiveframework.flags.in_patch: + return + + # Clear route cache + if route := xhiveframework.get_cached_value(reference_doctype, reference_name, "route"): + clear_cache(route) diff --git a/xhiveframework/core/doctype/comment/test_comment.py b/xhiveframework/core/doctype/comment/test_comment.py new file mode 100644 index 0000000..2aa6194 --- /dev/null +++ b/xhiveframework/core/doctype/comment/test_comment.py @@ -0,0 +1,120 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import json + +import xhiveframework +from xhiveframework.templates.includes.comments.comments import add_comment +from xhiveframework.tests.test_model_utils import set_user +from xhiveframework.tests.utils import XhiveFrameworkTestCase, change_settings +from xhiveframework.website.doctype.blog_post.test_blog_post import make_test_blog + + +class TestComment(XhiveFrameworkTestCase): + def test_comment_creation(self): + test_doc = xhiveframework.get_doc(dict(doctype="ToDo", description="test")) + test_doc.insert() + comment = test_doc.add_comment("Comment", "test comment") + + test_doc.reload() + + # check if updated in _comments cache + comments = json.loads(test_doc.get("_comments")) + self.assertEqual(comments[0].get("name"), comment.name) + self.assertEqual(comments[0].get("comment"), comment.content) + + # check document creation + comment_1 = xhiveframework.get_all( + "Comment", + fields=["*"], + filters=dict(reference_doctype=test_doc.doctype, reference_name=test_doc.name), + )[0] + + self.assertEqual(comment_1.content, "test comment") + + # test via blog + def test_public_comment(self): + test_blog = make_test_blog() + + xhiveframework.db.delete("Comment", {"reference_doctype": "Blog Post"}) + add_comment_args = { + "comment": "Good comment with 10 chars", + "comment_email": "test@test.com", + "comment_by": "Good Tester", + "reference_doctype": test_blog.doctype, + "reference_name": test_blog.name, + "route": test_blog.route, + } + add_comment(**add_comment_args) + + self.assertEqual( + xhiveframework.get_all( + "Comment", + fields=["*"], + filters=dict(reference_doctype=test_blog.doctype, reference_name=test_blog.name), + )[0].published, + 1, + ) + + xhiveframework.db.delete("Comment", {"reference_doctype": "Blog Post"}) + + add_comment_args.update(comment="pleez vizits my site http://mysite.com", comment_by="bad commentor") + add_comment(**add_comment_args) + + self.assertEqual( + len( + xhiveframework.get_all( + "Comment", + fields=["*"], + filters=dict(reference_doctype=test_blog.doctype, reference_name=test_blog.name), + ) + ), + 0, + ) + + # test for filtering html and css injection elements + xhiveframework.db.delete("Comment", {"reference_doctype": "Blog Post"}) + + add_comment_args.update(comment="Comment", comment_by="hacker") + add_comment(**add_comment_args) + self.assertEqual( + xhiveframework.get_all( + "Comment", + fields=["content"], + filters=dict(reference_doctype=test_blog.doctype, reference_name=test_blog.name), + )[0]["content"], + "Comment", + ) + + test_blog.delete() + + @change_settings("Blog Settings", {"allow_guest_to_comment": 0}) + def test_guest_cannot_comment(self): + test_blog = make_test_blog() + with set_user("Guest"): + self.assertEqual( + add_comment( + comment="Good comment with 10 chars", + comment_email="mail@example.org", + comment_by="Good Tester", + reference_doctype="Blog Post", + reference_name=test_blog.name, + route=test_blog.route, + ), + None, + ) + + def test_user_not_logged_in(self): + some_system_user = xhiveframework.db.get_value("User", {"name": ("not in", xhiveframework.STANDARD_USERS)}) + + test_blog = make_test_blog() + with set_user("Guest"): + self.assertRaises( + xhiveframework.ValidationError, + add_comment, + comment="Good comment with 10 chars", + comment_email=some_system_user, + comment_by="Good Tester", + reference_doctype="Blog Post", + reference_name=test_blog.name, + route=test_blog.route, + ) diff --git a/xhiveframework/core/doctype/communication/README.md b/xhiveframework/core/doctype/communication/README.md new file mode 100644 index 0000000..8ae1d4a --- /dev/null +++ b/xhiveframework/core/doctype/communication/README.md @@ -0,0 +1 @@ +Email or message sent or received from a contact and other DocTypes. `receive.py` will create a communication when a mail is received in the mailbox. \ No newline at end of file diff --git a/xhiveframework/core/doctype/communication/__init__.py b/xhiveframework/core/doctype/communication/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/communication/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/communication/communication.js b/xhiveframework/core/doctype/communication/communication.js new file mode 100644 index 0000000..65fc5fd --- /dev/null +++ b/xhiveframework/core/doctype/communication/communication.js @@ -0,0 +1,359 @@ +xhiveframework.ui.form.on("Communication", { + onload: function (frm) { + if (frm.doc.content) { + frm.doc.content = xhiveframework.dom.remove_script_and_style(frm.doc.content); + } + frm.set_query("reference_doctype", function () { + return { + filters: { + issingle: 0, + istable: 0, + }, + }; + }); + }, + refresh: function (frm) { + if (frm.is_new()) return; + + frm.convert_to_click && frm.set_convert_button(); + frm.subject_field = "subject"; + + // content field contains weird table html that does not render well in Quill + // this field is not to be edited directly anyway, so setting it as read only + frm.set_df_property("content", "read_only", 1); + + if (frm.doc.reference_doctype && frm.doc.reference_name) { + frm.add_custom_button(__(frm.doc.reference_name), function () { + xhiveframework.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name); + }); + } else { + // if an unlinked communication, set email field + if (frm.doc.sent_or_received === "Received") { + frm.email_field = "sender"; + } else { + frm.email_field = "recipients"; + } + } + + if (frm.doc.status === "Open") { + frm.add_custom_button(__("Close"), function () { + frm.trigger("mark_as_closed_open"); + }); + } else if (frm.doc.status !== "Linked") { + frm.add_custom_button(__("Reopen"), function () { + frm.trigger("mark_as_closed_open"); + }); + } + + frm.add_custom_button(__("Relink"), function () { + frm.trigger("show_relink_dialog"); + }); + + if ( + frm.doc.communication_type == "Communication" && + frm.doc.communication_medium == "Email" && + frm.doc.sent_or_received == "Received" + ) { + frm.add_custom_button(__("Reply"), function () { + frm.trigger("reply"); + }); + + frm.add_custom_button( + __("Reply All"), + function () { + frm.trigger("reply_all"); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Forward"), + function () { + frm.trigger("forward_mail"); + }, + __("Actions") + ); + + frm.add_custom_button( + frm.doc.seen ? __("Mark as Unread") : __("Mark as Read"), + function () { + frm.trigger("mark_as_read_unread"); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Move"), + function () { + frm.trigger("show_move_dialog"); + }, + __("Actions") + ); + + if (frm.doc.email_status != "Spam") + frm.add_custom_button( + __("Mark as Spam"), + function () { + frm.trigger("mark_as_spam"); + }, + __("Actions") + ); + + if (frm.doc.email_status != "Trash") { + frm.add_custom_button( + __("Move To Trash"), + function () { + frm.trigger("move_to_trash"); + }, + __("Actions") + ); + } + + frm.add_custom_button( + __("Contact"), + function () { + frm.trigger("add_to_contact"); + }, + __("Create") + ); + } + + if ( + frm.doc.communication_type == "Communication" && + frm.doc.communication_medium == "Phone" && + frm.doc.sent_or_received == "Received" + ) { + frm.add_custom_button( + __("Add Contact"), + function () { + frm.trigger("add_to_contact"); + }, + __("Actions") + ); + } + }, + + show_relink_dialog: function (frm) { + var d = new xhiveframework.ui.Dialog({ + title: __("Relink Communication"), + fields: [ + { + fieldtype: "Link", + options: "DocType", + label: __("Reference Doctype"), + fieldname: "reference_doctype", + get_query: function () { + return { query: "xhiveframework.email.get_communication_doctype" }; + }, + }, + { + fieldtype: "Dynamic Link", + options: "reference_doctype", + label: __("Reference Name"), + fieldname: "reference_name", + }, + ], + }); + d.set_value("reference_doctype", frm.doc.reference_doctype); + d.set_value("reference_name", frm.doc.reference_name); + d.set_primary_action(__("Relink"), function () { + var values = d.get_values(); + if (values) { + xhiveframework.confirm( + __("Are you sure you want to relink this communication to {0}?", [ + values["reference_name"], + ]), + function () { + d.hide(); + xhiveframework.call({ + method: "xhiveframework.email.relink", + args: { + name: frm.doc.name, + reference_doctype: values["reference_doctype"], + reference_name: values["reference_name"], + }, + callback: function () { + frm.refresh(); + }, + }); + }, + function () { + xhiveframework.show_alert({ + message: __("Document not Relinked"), + indicator: "info", + }); + } + ); + } + }); + d.show(); + }, + + show_move_dialog: function (frm) { + var d = new xhiveframework.ui.Dialog({ + title: __("Move"), + fields: [ + { + fieldtype: "Link", + options: "Email Account", + label: __("Email Account"), + fieldname: "email_account", + reqd: 1, + get_query: function () { + return { + filters: { + name: ["!=", frm.doc.email_account], + enable_incoming: ["=", 1], + }, + }; + }, + }, + ], + primary_action_label: __("Move"), + primary_action(values) { + d.hide(); + xhiveframework.call({ + method: "xhiveframework.email.inbox.move_email", + args: { + communication: frm.doc.name, + email_account: values.email_account, + }, + freeze: true, + callback: function () { + window.history.back(); + }, + }); + }, + }); + d.show(); + }, + + mark_as_read_unread: function (frm) { + var action = frm.doc.seen ? "Unread" : "Read"; + var flag = "(\\SEEN)"; + + return xhiveframework.call({ + method: "xhiveframework.email.inbox.create_email_flag_queue", + args: { + names: [frm.doc.name], + action: action, + flag: flag, + }, + freeze: true, + callback: function () { + frm.reload_doc(); + }, + }); + }, + + mark_as_closed_open: function (frm) { + var status = frm.doc.status == "Open" ? "Closed" : "Open"; + + return xhiveframework.call({ + method: "xhiveframework.email.inbox.mark_as_closed_open", + args: { + communication: frm.doc.name, + status: status, + }, + freeze: true, + callback: function () { + frm.reload_doc(); + }, + }); + }, + + reply: function (frm) { + var args = frm.events.get_mail_args(frm); + $.extend(args, { + subject: __("Re: {0}", [frm.doc.subject]), + recipients: frm.doc.sender, + is_a_reply: true, + }); + + new xhiveframework.views.CommunicationComposer(args); + }, + + reply_all: function (frm) { + var args = frm.events.get_mail_args(frm); + $.extend(args, { + subject: __("Res: {0}", [frm.doc.subject]), + recipients: frm.doc.sender, + cc: frm.doc.cc, + is_a_reply: true, + }); + new xhiveframework.views.CommunicationComposer(args); + }, + + forward_mail: function (frm) { + var args = frm.events.get_mail_args(frm); + $.extend(args, { + forward: true, + subject: __("Fw: {0}", [frm.doc.subject]), + is_a_reply: true, + }); + + new xhiveframework.views.CommunicationComposer(args); + }, + + get_mail_args: function (frm) { + var sender_email_id = ""; + $.each(xhiveframework.boot.email_accounts, function (idx, account) { + if (account.email_account == frm.doc.email_account) { + sender_email_id = account.email_id; + return; + } + }); + + return { + frm: frm, + doc: frm.doc, + last_email: frm.doc, + sender: sender_email_id, + attachments: frm.doc.attachments, + }; + }, + + add_to_contact: function (frm) { + var me = this; + var fullname = frm.doc.sender_full_name || ""; + + var names = fullname.split(" "); + var first_name = names[0]; + var last_name = names.length >= 2 ? names[names.length - 1] : ""; + + xhiveframework.route_options = { + email_id: frm.doc.sender || "", + first_name: first_name, + last_name: last_name, + mobile_no: frm.doc.phone_no || "", + }; + xhiveframework.new_doc("Contact"); + }, + + mark_as_spam: function (frm) { + xhiveframework.call({ + method: "xhiveframework.email.inbox.mark_as_spam", + args: { + communication: frm.doc.name, + sender: frm.doc.sender, + }, + freeze: true, + callback: function (r) { + xhiveframework.msgprint(__("Email has been marked as spam")); + }, + }); + }, + + move_to_trash: function (frm) { + xhiveframework.call({ + method: "xhiveframework.email.inbox.mark_as_trash", + args: { + communication: frm.doc.name, + }, + freeze: true, + callback: function (r) { + xhiveframework.msgprint(__("Email has been moved to trash")); + }, + }); + }, +}); diff --git a/xhiveframework/core/doctype/communication/communication.json b/xhiveframework/core/doctype/communication/communication.json new file mode 100644 index 0000000..3e15774 --- /dev/null +++ b/xhiveframework/core/doctype/communication/communication.json @@ -0,0 +1,468 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2013-01-29 10:47:14", + "default_view": "List", + "description": "Keeps track of all communications", + "doctype": "DocType", + "document_type": "Setup", + "email_append_to": 1, + "engine": "InnoDB", + "field_order": [ + "subject", + "section_break_10", + "communication_medium", + "sender", + "column_break_4", + "recipients", + "cc", + "bcc", + "phone_no", + "delivery_status", + "section_break_8", + "content", + "status_section", + "text_content", + "communication_type", + "comment_type", + "column_break_5", + "status", + "sent_or_received", + "additional_info", + "communication_date", + "read_receipt", + "send_after", + "column_break_14", + "sender_full_name", + "read_by_recipient", + "read_by_recipient_on", + "reference_section", + "reference_doctype", + "reference_name", + "reference_owner", + "email_account", + "in_reply_to", + "user", + "column_break_27", + "email_template", + "unread_notification_sent", + "seen", + "_user_tags", + "timeline_links_sections", + "timeline_links", + "email_inbox", + "message_id", + "uid", + "imap_folder", + "email_status", + "has_attachment", + "feedback_section", + "rating", + "feedback_request" + ], + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Subject", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "label": "To and CC" + }, + { + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "communication_medium", + "fieldtype": "Select", + "label": "Type", + "options": "\nEmail\nChat\nPhone\nSMS\nEvent\nMeeting\nVisit\nOther" + }, + { + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "sender", + "fieldtype": "Data", + "in_global_search": 1, + "label": "From", + "length": 255, + "options": "Email" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "recipients", + "fieldtype": "Code", + "label": "To", + "options": "Email" + }, + { + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "cc", + "fieldtype": "Code", + "label": "CC", + "options": "Email" + }, + { + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "bcc", + "fieldtype": "Code", + "label": "BCC", + "options": "Email" + }, + { + "depends_on": "eval:in_list([\"Phone\",\"SMS\"],doc.communication_medium)", + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No." + }, + { + "description": "Integrations can use this field to set email delivery status", + "fieldname": "delivery_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Delivery Status", + "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending\nRead\nScheduled" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Message", + "width": "400" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "text_content", + "fieldtype": "Code", + "hidden": 1, + "label": "Text Content" + }, + { + "default": "Communication", + "fieldname": "communication_type", + "fieldtype": "Select", + "label": "Communication Type", + "options": "Communication\nComment\nChat\nNotification\nFeedback\nAutomated Message", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "comment_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Comment Type", + "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nRelinked", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nReplied\nClosed\nLinked", + "reqd": 1 + }, + { + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "sent_or_received", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sent or Received", + "options": "Sent\nReceived", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "More Information" + }, + { + "default": "Now", + "fieldname": "communication_date", + "fieldtype": "Datetime", + "label": "Date" + }, + { + "default": "0", + "fieldname": "read_receipt", + "fieldtype": "Check", + "label": "Sent Read Receipt", + "read_only": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "sender_full_name", + "fieldtype": "Data", + "label": "From Full Name", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "read_by_recipient", + "fieldtype": "Check", + "label": "Read by Recipient", + "read_only": 1 + }, + { + "fieldname": "read_by_recipient_on", + "fieldtype": "Datetime", + "label": "Read by Recipient On", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, + { + "fetch_from": "reference_name.owner", + "fieldname": "reference_owner", + "fieldtype": "Read Only", + "label": "Reference Owner", + "search_index": 1 + }, + { + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account", + "read_only": 1 + }, + { + "fieldname": "in_reply_to", + "fieldtype": "Link", + "label": "In Reply To", + "options": "Communication", + "read_only": 1 + }, + { + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "unread_notification_sent", + "fieldtype": "Check", + "label": "Unread Notification Sent", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "label": "Seen", + "read_only": 1 + }, + { + "fieldname": "_user_tags", + "fieldtype": "Data", + "hidden": 1, + "label": "User Tags", + "no_copy": 1, + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "email_inbox", + "fieldtype": "Section Break", + "label": "Email Inbox", + "permlevel": 1 + }, + { + "fieldname": "message_id", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "label": "Message ID", + "length": 995, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "uid", + "fieldtype": "Int", + "hidden": 1, + "label": "UID", + "no_copy": 1 + }, + { + "fieldname": "email_status", + "fieldtype": "Select", + "label": "Email Status", + "options": "Open\nSpam\nTrash" + }, + { + "default": "0", + "fieldname": "has_attachment", + "fieldtype": "Check", + "hidden": 1, + "label": "Has Attachment" + }, + { + "collapsible": 1, + "depends_on": "eval: doc.rating > 0", + "fieldname": "feedback_section", + "fieldtype": "Section Break", + "label": "Feedback" + }, + { + "fieldname": "rating", + "fieldtype": "Int", + "label": "Rating", + "read_only": 1 + }, + { + "fieldname": "feedback_request", + "fieldtype": "Data", + "label": "Feedback Request", + "read_only": 1 + }, + { + "fieldname": "email_template", + "fieldtype": "Link", + "label": "Email Template", + "options": "Email Template", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "timeline_links_sections", + "fieldtype": "Section Break", + "label": "Timeline Links" + }, + { + "fieldname": "timeline_links", + "fieldtype": "Table", + "label": "Timeline Links", + "options": "Communication Link", + "permlevel": 2 + }, + { + "fieldname": "imap_folder", + "fieldtype": "Data", + "hidden": 1, + "label": "IMAP Folder", + "read_only": 1 + }, + { + "fieldname": "send_after", + "fieldtype": "Datetime", + "label": "Send After" + } + ], + "icon": "fa fa-comment", + "idx": 1, + "links": [], + "modified": "2023-11-27 20:38:27.467076", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 2, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "role": "Inbox User" + }, + { + "delete": 1, + "email": 1, + "if_owner": 1, + "read": 1, + "role": "All" + } + ], + "search_fields": "subject", + "sender_field": "sender", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "subject_field": "subject", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/communication/communication.py b/xhiveframework/core/doctype/communication/communication.py new file mode 100644 index 0000000..b3c7946 --- /dev/null +++ b/xhiveframework/core/doctype/communication/communication.py @@ -0,0 +1,702 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from collections import Counter +from email.utils import getaddresses +from urllib.parse import unquote + +from bs4 import BeautifulSoup + +import xhiveframework +from xhiveframework import _ +from xhiveframework.automation.doctype.assignment_rule.assignment_rule import ( + apply as apply_assignment_rule, +) +from xhiveframework.contacts.doctype.contact.contact import get_contact_name +from xhiveframework.core.doctype.comment.comment import update_comment_in_doc +from xhiveframework.core.doctype.communication.email import validate_email +from xhiveframework.core.doctype.communication.mixins import CommunicationEmailMixin +from xhiveframework.core.utils import get_parent_doc +from xhiveframework.model.document import Document +from xhiveframework.utils import ( + cstr, + parse_addr, + split_emails, + strip_html, + time_diff_in_seconds, + validate_email_address, +) +from xhiveframework.utils.user import is_system_user + +exclude_from_linked_with = True + + +class Communication(Document, CommunicationEmailMixin): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.communication_link.communication_link import CommunicationLink + from xhiveframework.types import DF + + _user_tags: DF.Data | None + bcc: DF.Code | None + cc: DF.Code | None + comment_type: DF.Literal[ + "", + "Comment", + "Like", + "Info", + "Label", + "Workflow", + "Created", + "Submitted", + "Cancelled", + "Updated", + "Deleted", + "Assigned", + "Assignment Completed", + "Attachment", + "Attachment Removed", + "Shared", + "Unshared", + "Relinked", + ] + communication_date: DF.Datetime | None + communication_medium: DF.Literal[ + "", "Email", "Chat", "Phone", "SMS", "Event", "Meeting", "Visit", "Other" + ] + communication_type: DF.Literal[ + "Communication", "Comment", "Chat", "Notification", "Feedback", "Automated Message" + ] + content: DF.TextEditor | None + delivery_status: DF.Literal[ + "", + "Sent", + "Bounced", + "Opened", + "Marked As Spam", + "Rejected", + "Delayed", + "Soft-Bounced", + "Clicked", + "Recipient Unsubscribed", + "Error", + "Expired", + "Sending", + "Read", + "Scheduled", + ] + email_account: DF.Link | None + email_status: DF.Literal["Open", "Spam", "Trash"] + email_template: DF.Link | None + feedback_request: DF.Data | None + has_attachment: DF.Check + imap_folder: DF.Data | None + in_reply_to: DF.Link | None + message_id: DF.SmallText | None + phone_no: DF.Data | None + rating: DF.Int + read_by_recipient: DF.Check + read_by_recipient_on: DF.Datetime | None + read_receipt: DF.Check + recipients: DF.Code | None + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + reference_owner: DF.ReadOnly | None + seen: DF.Check + send_after: DF.Datetime | None + sender: DF.Data | None + sender_full_name: DF.Data | None + sent_or_received: DF.Literal["Sent", "Received"] + status: DF.Literal["Open", "Replied", "Closed", "Linked"] + subject: DF.SmallText + text_content: DF.Code | None + timeline_links: DF.Table[CommunicationLink] + uid: DF.Int + unread_notification_sent: DF.Check + user: DF.Link | None + # end: auto-generated types + """Communication represents an external communication like Email.""" + + no_feed_on_delete = True + DOCTYPE = "Communication" + + def onload(self): + """create email flag queue""" + if ( + self.communication_type == "Communication" + and self.communication_medium == "Email" + and self.sent_or_received == "Received" + and self.uid + and self.uid != -1 + ): + email_flag_queue = xhiveframework.db.get_value( + "Email Flag Queue", {"communication": self.name, "is_completed": 0} + ) + if email_flag_queue: + return + + xhiveframework.get_doc( + { + "doctype": "Email Flag Queue", + "action": "Read", + "communication": self.name, + "uid": self.uid, + "email_account": self.email_account, + } + ).insert(ignore_permissions=True) + xhiveframework.db.commit() + + def validate(self): + self.validate_reference() + + if not self.user: + self.user = xhiveframework.session.user + + if not self.subject: + self.subject = strip_html((self.content or "")[:141]) + + if not self.sent_or_received: + self.seen = 1 + self.sent_or_received = "Sent" + + if not self.send_after: # Handle empty string, always set NULL + self.send_after = None + + validate_email(self) + + if self.communication_medium == "Email": + self.parse_email_for_timeline_links() + self.set_timeline_links() + self.deduplicate_timeline_links() + + self.set_sender_full_name() + + if self.is_new(): + self.set_status() + self.mark_email_as_spam() + + def validate_reference(self): + if self.reference_doctype and self.reference_name: + if not self.reference_owner: + self.reference_owner = xhiveframework.db.get_value( + self.reference_doctype, self.reference_name, "owner" + ) + + # prevent communication against a child table + if xhiveframework.get_meta(self.reference_doctype).istable: + xhiveframework.throw( + _("Cannot create a {0} against a child document: {1}").format( + _(self.communication_type), _(self.reference_doctype) + ) + ) + + # Prevent circular linking of Communication DocTypes + if self.reference_doctype == "Communication": + circular_linking = False + doc = get_parent_doc(self) + while doc.reference_doctype == "Communication": + if get_parent_doc(doc).name == self.name: + circular_linking = True + break + doc = get_parent_doc(doc) + + if circular_linking: + xhiveframework.throw( + _("Please make sure the Reference Communication Docs are not circularly linked."), + xhiveframework.CircularLinkingError, + ) + + def after_insert(self): + if not (self.reference_doctype and self.reference_name): + return + + if self.reference_doctype == "Communication" and self.sent_or_received == "Sent": + xhiveframework.db.set_value("Communication", self.reference_name, "status", "Replied") + + if self.communication_type == "Communication": + self.notify_change("add") + + elif self.communication_type in ("Chat", "Notification"): + if self.reference_name == xhiveframework.session.user: + message = self.as_dict() + message["broadcast"] = True + xhiveframework.publish_realtime("new_message", message, after_commit=True) + else: + # reference_name contains the user who is addressed in the messages' page comment + xhiveframework.publish_realtime( + "new_message", self.as_dict(), user=self.reference_name, after_commit=True + ) + + def set_signature_in_email_content(self): + """Set sender's User.email_signature or default outgoing's EmailAccount.signature to the email""" + if not self.content: + return + + soup = BeautifulSoup(self.content, "html.parser") + email_body = soup.find("div", {"class": "ql-editor read-mode"}) + + if not email_body: + return + + user_email_signature = ( + xhiveframework.db.get_value( + "User", + self.sender, + "email_signature", + ) + if self.sender + else None + ) + + signature = user_email_signature or xhiveframework.db.get_value( + "Email Account", + {"default_outgoing": 1, "add_signature": 1}, + "signature", + ) + + if not signature: + return + + soup = BeautifulSoup(signature, "html.parser") + html_signature = soup.find("div", {"class": "ql-editor read-mode"}) + _signature = None + if html_signature: + _signature = html_signature.renderContents() + + if (cstr(_signature) or signature) not in self.content: + self.content = f'{self.content}


    {signature}' + + def before_save(self): + if not self.flags.skip_add_signature: + self.set_signature_in_email_content() + + def on_update(self): + # add to _comment property of the doctype, so it shows up in + # comments count for the list view + update_comment_in_doc(self) + + parent = get_parent_doc(self) + if (method := getattr(parent, "on_communication_update", None)) and callable(method): + parent.on_communication_update(self) + return + + if self.comment_type != "Updated": + update_parent_document_on_communication(self) + + def on_trash(self): + if self.communication_type == "Communication": + self.notify_change("delete") + + @property + def sender_mailid(self): + return parse_addr(self.sender)[1] if self.sender else "" + + @staticmethod + def _get_emails_list(emails=None, exclude_displayname=False): + """Returns list of emails from given email string. + + * Removes duplicate mailids + * Removes display name from email address if exclude_displayname is True + """ + emails = split_emails(emails) if isinstance(emails, str) else (emails or []) + if exclude_displayname: + return [email.lower() for email in {parse_addr(email)[1] for email in emails} if email] + return [email for email in set(emails) if email] + + def to_list(self, exclude_displayname=True): + """Returns to list.""" + return self._get_emails_list(self.recipients, exclude_displayname=exclude_displayname) + + def cc_list(self, exclude_displayname=True): + """Returns cc list.""" + return self._get_emails_list(self.cc, exclude_displayname=exclude_displayname) + + def bcc_list(self, exclude_displayname=True): + """Returns bcc list.""" + return self._get_emails_list(self.bcc, exclude_displayname=exclude_displayname) + + def get_attachments(self): + return xhiveframework.get_all( + "File", + fields=["name", "file_name", "file_url", "is_private"], + filters={ + "attached_to_name": self.name, + "attached_to_doctype": self.DOCTYPE, + }, + ) + + def notify_change(self, action): + xhiveframework.publish_realtime( + "docinfo_update", + {"doc": self.as_dict(), "key": "communications", "action": action}, + doctype=self.reference_doctype, + docname=self.reference_name, + after_commit=True, + ) + + def set_status(self): + if self.reference_doctype and self.reference_name: + self.status = "Linked" + elif self.communication_type == "Communication": + self.status = "Open" + else: + self.status = "Closed" + + if self.send_after and self.is_new(): + self.delivery_status = "Scheduled" + + def mark_email_as_spam(self): + if ( + self.communication_type == "Communication" + and self.communication_medium == "Email" + and self.sent_or_received == "Received" + and xhiveframework.db.exists("Email Rule", {"email_id": self.sender, "is_spam": 1}) + ): + self.email_status = "Spam" + + @classmethod + def find(cls, name, ignore_error=False): + try: + return xhiveframework.get_doc(cls.DOCTYPE, name) + except xhiveframework.DoesNotExistError: + if ignore_error: + return + raise + + @classmethod + def find_one_by_filters(cls, *, order_by=None, **kwargs): + name = xhiveframework.db.get_value(cls.DOCTYPE, kwargs, order_by=order_by) + return cls.find(name) if name else None + + def update_db(self, **kwargs): + xhiveframework.db.set_value(self.DOCTYPE, self.name, kwargs) + + def set_sender_full_name(self): + if not self.sender_full_name and self.sender: + if self.sender == "Administrator": + self.sender_full_name = xhiveframework.db.get_value("User", "Administrator", "full_name") + self.sender = xhiveframework.db.get_value("User", "Administrator", "email") + elif self.sender == "Guest": + self.sender_full_name = self.sender + self.sender = None + else: + if self.sent_or_received == "Sent": + validate_email_address(self.sender, throw=True) + sender_name, sender_email = parse_addr(self.sender) + if sender_name == sender_email: + sender_name = None + + self.sender = sender_email + self.sender_full_name = sender_name + + if not self.sender_full_name: + self.sender_full_name = xhiveframework.db.get_value("User", self.sender, "full_name") + + if not self.sender_full_name: + first_name, last_name = xhiveframework.db.get_value( + "Contact", filters={"email_id": sender_email}, fieldname=["first_name", "last_name"] + ) or [None, None] + self.sender_full_name = (first_name or "") + (last_name or "") + + if not self.sender_full_name: + self.sender_full_name = sender_email + + def set_delivery_status(self, commit=False): + """Look into the status of Email Queue linked to this Communication and set the Delivery Status of this Communication""" + delivery_status = None + status_counts = Counter( + xhiveframework.get_all("Email Queue", pluck="status", filters={"communication": self.name}) + ) + if self.sent_or_received == "Received": + return + + if status_counts.get("Not Sent") or status_counts.get("Sending"): + delivery_status = "Sending" + + elif status_counts.get("Error"): + delivery_status = "Error" + + elif status_counts.get("Expired"): + delivery_status = "Expired" + + elif status_counts.get("Sent"): + delivery_status = "Sent" + + if delivery_status: + self.db_set("delivery_status", delivery_status) + self.notify_change("update") + + # for list views and forms + self.notify_update() + + if commit: + xhiveframework.db.commit() + + def parse_email_for_timeline_links(self): + if not xhiveframework.db.get_value("Email Account", filters={"enable_automatic_linking": 1}): + return + + for doctype, docname in parse_email([self.recipients, self.cc, self.bcc]): + if not xhiveframework.db.get_value(doctype, docname, ignore=True): + continue + + self.add_link(doctype, docname) + + if not self.reference_doctype: + self.reference_doctype = doctype + self.reference_name = docname + + # Timeline Links + def set_timeline_links(self): + contacts = [] + create_contact_enabled = self.email_account and xhiveframework.db.get_value( + "Email Account", self.email_account, "create_contact" + ) + contacts = get_contacts( + [self.sender, self.recipients, self.cc, self.bcc], auto_create_contact=create_contact_enabled + ) + + for contact_name in contacts: + self.add_link("Contact", contact_name) + + # link contact's dynamic links to communication + add_contact_links_to_communication(self, contact_name) + + def deduplicate_timeline_links(self): + if not self.timeline_links: + return + + unique_links = {(link.link_doctype, link.link_name) for link in self.timeline_links} + self.timeline_links = [] + for doctype, name in unique_links: + self.add_link(doctype, name) + + def add_link(self, link_doctype, link_name, autosave=False): + self.append("timeline_links", {"link_doctype": link_doctype, "link_name": link_name}) + + if autosave: + self.save(ignore_permissions=True) + + def get_links(self): + return self.timeline_links + + def remove_link(self, link_doctype, link_name, autosave=False, ignore_permissions=True): + for l in list(self.timeline_links): + if l.link_doctype == link_doctype and l.link_name == link_name: + self.timeline_links.remove(l) + + if autosave: + self.save(ignore_permissions=ignore_permissions) + + +def on_doctype_update(): + """Add indexes in `tabCommunication`""" + xhiveframework.db.add_index("Communication", ["reference_doctype", "reference_name"]) + xhiveframework.db.add_index("Communication", ["status", "communication_type"]) + xhiveframework.db.add_index("Communication", ["message_id(140)"]) + + +def has_permission(doc, ptype, user=None, debug=False): + if ptype == "read": + if doc.reference_doctype == "Communication" and doc.reference_name == doc.name: + return + + if doc.reference_doctype and doc.reference_name: + return xhiveframework.has_permission( + doc.reference_doctype, ptype="read", doc=doc.reference_name, user=user, debug=debug + ) + + +def get_permission_query_conditions_for_communication(user): + if not user: + user = xhiveframework.session.user + + roles = xhiveframework.get_roles(user) + + if "Super Email User" in roles or "System Manager" in roles: + return None + else: + accounts = xhiveframework.get_all( + "User Email", filters={"parent": user}, fields=["email_account"], distinct=True, order_by="idx" + ) + + if not accounts: + return """`tabCommunication`.communication_medium!='Email'""" + + email_accounts = ['"%s"' % account.get("email_account") for account in accounts] + return """`tabCommunication`.email_account in ({email_accounts})""".format( + email_accounts=",".join(email_accounts) + ) + + +def get_contacts(email_strings: list[str], auto_create_contact=False) -> list[str]: + email_addrs = get_emails(email_strings) + contacts = [] + for email in email_addrs: + email = get_email_without_link(email) + contact_name = get_contact_name(email) + + if not contact_name and email and auto_create_contact: + email_parts = email.split("@") + first_name = xhiveframework.unscrub(email_parts[0]) + + try: + contact_name = f"{first_name}-{email_parts[1]}" if first_name == "Contact" else first_name + contact = xhiveframework.get_doc( + {"doctype": "Contact", "first_name": contact_name, "name": contact_name} + ) + contact.add_email(email_id=email, is_primary=True) + contact.insert(ignore_permissions=True) + contact_name = contact.name + except Exception: + contact_name = None + contact.log_error("Unable to add contact") + + if contact_name: + contacts.append(contact_name) + + return contacts + + +def get_emails(email_strings: list[str]) -> list[str]: + email_addrs = [] + + for email_string in email_strings: + if email_string: + result = getaddresses([email_string]) + email_addrs.extend(email[1] for email in result) + return email_addrs + + +def add_contact_links_to_communication(communication, contact_name): + contact_links = xhiveframework.get_all( + "Dynamic Link", + filters={"parenttype": "Contact", "parent": contact_name}, + fields=["link_doctype", "link_name"], + ) + + if contact_links: + for contact_link in contact_links: + communication.add_link(contact_link.link_doctype, contact_link.link_name) + + +def parse_email(email_strings): + """ + Parse email to add timeline links. + When automatic email linking is enabled, an email from email_strings can contain + a doctype and docname ie in the format `admin+doctype+docname@example.com` or `admin+doctype=docname@example.com`, + the email is parsed and doctype and docname is extracted. + """ + for email_string in email_strings: + if not email_string: + continue + + for email in email_string.split(","): + email_username = email.split("@", 1)[0] + email_local_parts = email_username.split("+") + docname = doctype = None + if len(email_local_parts) == 3: + doctype = unquote(email_local_parts[1]) + docname = unquote(email_local_parts[2]) + + elif len(email_local_parts) == 2: + document_parts = email_local_parts[1].split("=", 1) + if len(document_parts) != 2: + continue + + doctype = unquote(document_parts[0]) + docname = unquote(document_parts[1]) + + if doctype and docname: + yield doctype, docname + + +def get_email_without_link(email): + """ + returns email address without doctype links + returns admin@example.com for email admin+doctype+docname@example.com + """ + if not xhiveframework.get_all("Email Account", filters={"enable_automatic_linking": 1}): + return email + + try: + _email = email.split("@") + email_id = _email[0].split("+", 1)[0] + email_host = _email[1] + except IndexError: + return email + + return f"{email_id}@{email_host}" + + +def update_parent_document_on_communication(doc): + """Update mins_to_first_communication of parent document based on who is replying.""" + + parent = get_parent_doc(doc) + if not parent: + return + + # update parent mins_to_first_communication only if we create the Email communication + # ignore in case of only Comment is added + if doc.communication_type == "Comment": + return + + status_field = parent.meta.get_field("status") + if status_field: + options = (status_field.options or "").splitlines() + + # if status has a "Replied" option, then update the status for received communication + if ("Replied" in options) and doc.sent_or_received == "Received": + parent.db_set("status", "Open") + parent.run_method("handle_hold_time", "Replied") + apply_assignment_rule(parent) + + update_first_response_time(parent, doc) + set_avg_response_time(parent, doc) + parent.run_method("notify_communication", doc) + parent.notify_update() + + +def update_first_response_time(parent, communication): + if parent.meta.has_field("first_response_time") and not parent.get("first_response_time"): + if ( + is_system_user(communication.sender) + or xhiveframework.get_cached_value("User", xhiveframework.session.user, "user_type") == "System User" + ): + if communication.sent_or_received == "Sent": + first_responded_on = communication.creation + if parent.meta.has_field("first_responded_on"): + parent.db_set("first_responded_on", first_responded_on) + first_response_time = round(time_diff_in_seconds(first_responded_on, parent.creation), 2) + parent.db_set("first_response_time", first_response_time) + + +def set_avg_response_time(parent, communication): + if parent.meta.has_field("avg_response_time") and communication.sent_or_received == "Sent": + # avg response time for all the responses + communications = xhiveframework.get_list( + "Communication", + filters={"reference_doctype": parent.doctype, "reference_name": parent.name}, + fields=["sent_or_received", "name", "creation"], + order_by="creation", + ) + + if len(communications): + response_times = [] + for i in range(len(communications)): + if ( + communications[i].sent_or_received == "Sent" + and communications[i - 1].sent_or_received == "Received" + ): + response_time = round( + time_diff_in_seconds(communications[i].creation, communications[i - 1].creation), 2 + ) + if response_time > 0: + response_times.append(response_time) + if response_times: + avg_response_time = sum(response_times) / len(response_times) + parent.db_set("avg_response_time", avg_response_time) diff --git a/xhiveframework/core/doctype/communication/communication_list.js b/xhiveframework/core/doctype/communication/communication_list.js new file mode 100644 index 0000000..9afa412 --- /dev/null +++ b/xhiveframework/core/doctype/communication/communication_list.js @@ -0,0 +1,30 @@ +xhiveframework.listview_settings["Communication"] = { + add_fields: [ + "sent_or_received", + "recipients", + "subject", + "communication_medium", + "communication_type", + "sender", + "seen", + "reference_doctype", + "reference_name", + "has_attachment", + "communication_date", + ], + + onload: function (list_view) { + let method = "xhiveframework.email.inbox.create_email_flag_queue"; + + list_view.page.add_menu_item(__("Mark as Read"), function () { + list_view.call_for_selected_items(method, { action: "Read" }); + }); + list_view.page.add_menu_item(__("Mark as Unread"), function () { + list_view.call_for_selected_items(method, { action: "Unread" }); + }); + }, + + primary_action: function () { + new xhiveframework.views.CommunicationComposer(); + }, +}; diff --git a/xhiveframework/core/doctype/communication/email.py b/xhiveframework/core/doctype/communication/email.py new file mode 100755 index 0000000..bfae47e --- /dev/null +++ b/xhiveframework/core/doctype/communication/email.py @@ -0,0 +1,300 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +from collections.abc import Iterable +from typing import TYPE_CHECKING + +import xhiveframework +import xhiveframework.email.smtp +from xhiveframework import _ +from xhiveframework.email.email_body import get_message_id +from xhiveframework.utils import ( + cint, + get_datetime, + get_formatted_email, + get_imaginary_pixel_response, + get_string_between, + list_to_str, + split_emails, + validate_email_address, +) + +if TYPE_CHECKING: + from xhiveframework.core.doctype.communication.communication import Communication + + +@xhiveframework.whitelist() +def make( + doctype=None, + name=None, + content=None, + subject=None, + sent_or_received="Sent", + sender=None, + sender_full_name=None, + recipients=None, + communication_medium="Email", + send_email=False, + print_html=None, + print_format=None, + attachments=None, + send_me_a_copy=False, + cc=None, + bcc=None, + read_receipt=None, + print_letterhead=True, + email_template=None, + communication_type=None, + send_after=None, + print_language=None, + now=False, + **kwargs, +) -> dict[str, str]: + """Make a new communication. Checks for email permissions for specified Document. + + :param doctype: Reference DocType. + :param name: Reference Document name. + :param content: Communication body. + :param subject: Communication subject. + :param sent_or_received: Sent or Received (default **Sent**). + :param sender: Communcation sender (default current user). + :param recipients: Communication recipients as list. + :param communication_medium: Medium of communication (default **Email**). + :param send_email: Send via email (default **False**). + :param print_html: HTML Print format to be sent as attachment. + :param print_format: Print Format name of parent document to be sent as attachment. + :param attachments: List of File names or dicts with keys "fname" and "fcontent" + :param send_me_a_copy: Send a copy to the sender (default **False**). + :param email_template: Template which is used to compose mail . + :param send_after: Send after the given datetime. + """ + if kwargs: + from xhiveframework.utils.commands import warn + + warn( + f"Options {kwargs} used in xhiveframework.core.doctype.communication.email.make " + "are deprecated or unsupported", + category=DeprecationWarning, + ) + + if doctype and name and not xhiveframework.has_permission(doctype=doctype, ptype="email", doc=name): + raise xhiveframework.PermissionError(f"You are not allowed to send emails related to: {doctype} {name}") + + return _make( + doctype=doctype, + name=name, + content=content, + subject=subject, + sent_or_received=sent_or_received, + sender=sender, + sender_full_name=sender_full_name, + recipients=recipients, + communication_medium=communication_medium, + send_email=send_email, + print_html=print_html, + print_format=print_format, + attachments=attachments, + send_me_a_copy=cint(send_me_a_copy), + cc=cc, + bcc=bcc, + read_receipt=cint(read_receipt), + print_letterhead=print_letterhead, + email_template=email_template, + communication_type=communication_type, + add_signature=False, + send_after=send_after, + print_language=print_language, + now=now, + ) + + +def _make( + doctype=None, + name=None, + content=None, + subject=None, + sent_or_received="Sent", + sender=None, + sender_full_name=None, + recipients=None, + communication_medium="Email", + send_email=False, + print_html=None, + print_format=None, + attachments=None, + send_me_a_copy=False, + cc=None, + bcc=None, + read_receipt=None, + print_letterhead=True, + email_template=None, + communication_type=None, + add_signature=True, + send_after=None, + print_language=None, + now=False, +) -> dict[str, str]: + """Internal method to make a new communication that ignores Permission checks.""" + + sender = sender or get_formatted_email(xhiveframework.session.user) + recipients = list_to_str(recipients) if isinstance(recipients, list) else recipients + cc = list_to_str(cc) if isinstance(cc, list) else cc + bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc + + comm: "Communication" = xhiveframework.get_doc( + { + "doctype": "Communication", + "subject": subject, + "content": content, + "sender": sender, + "sender_full_name": sender_full_name, + "recipients": recipients, + "cc": cc or None, + "bcc": bcc or None, + "communication_medium": communication_medium, + "sent_or_received": sent_or_received, + "reference_doctype": doctype, + "reference_name": name, + "email_template": email_template, + "message_id": get_string_between("<", get_message_id(), ">"), + "read_receipt": read_receipt, + "has_attachment": 1 if attachments else 0, + "communication_type": communication_type, + "send_after": send_after, + } + ) + comm.flags.skip_add_signature = not add_signature + comm.insert(ignore_permissions=True) + + # if not committed, delayed task doesn't find the communication + if attachments: + if isinstance(attachments, str): + attachments = json.loads(attachments) + add_attachments(comm.name, attachments) + + if cint(send_email): + if not comm.get_outgoing_email_account(): + xhiveframework.throw( + _( + "Unable to send mail because of a missing email account. Please setup default Email Account from Settings > Email Account" + ), + exc=xhiveframework.OutgoingEmailError, + ) + + comm.send_email( + print_html=print_html, + print_format=print_format, + send_me_a_copy=send_me_a_copy, + print_letterhead=print_letterhead, + print_language=print_language, + now=now, + ) + + emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) + + return {"name": comm.name, "emails_not_sent_to": ", ".join(emails_not_sent_to)} + + +def validate_email(doc: "Communication") -> None: + """Validate Email Addresses of Recipients and CC""" + if ( + not (doc.communication_type == "Communication" and doc.communication_medium == "Email") + or doc.flags.in_receive + ): + return + + # validate recipients + for email in split_emails(doc.recipients): + validate_email_address(email, throw=True) + + # validate CC + for email in split_emails(doc.cc): + validate_email_address(email, throw=True) + + for email in split_emails(doc.bcc): + validate_email_address(email, throw=True) + + +def set_incoming_outgoing_accounts(doc): + from xhiveframework.email.doctype.email_account.email_account import EmailAccount + + incoming_email_account = EmailAccount.find_incoming( + match_by_email=doc.sender, match_by_doctype=doc.reference_doctype + ) + doc.incoming_email_account = incoming_email_account.email_id if incoming_email_account else None + + doc.outgoing_email_account = EmailAccount.find_outgoing( + match_by_email=doc.sender, match_by_doctype=doc.reference_doctype + ) + + if doc.sent_or_received == "Sent": + doc.db_set("email_account", doc.outgoing_email_account.name) + + +def add_attachments(name: str, attachments: Iterable[str | dict]) -> None: + """Add attachments to the given Communication + + :param name: Communication name + :param attachments: File names or dicts with keys "fname" and "fcontent" + """ + # loop through attachments + for a in attachments: + if isinstance(a, str): + attach = xhiveframework.db.get_value("File", {"name": a}, ["file_url", "is_private"], as_dict=1) + file_args = { + "file_url": attach.file_url, + "is_private": attach.is_private, + } + elif isinstance(a, dict) and "fcontent" in a and "fname" in a: + # dict returned by xhiveframework.attach_print() + file_args = { + "file_name": a["fname"], + "content": a["fcontent"], + "is_private": 1, + } + else: + continue + + file_args.update( + { + "attached_to_doctype": "Communication", + "attached_to_name": name, + "folder": "Home/Attachments", + } + ) + + _file = xhiveframework.new_doc("File") + _file.update(file_args) + _file.save(ignore_permissions=True) + + +@xhiveframework.whitelist(allow_guest=True, methods=("GET",)) +def mark_email_as_seen(name: str | None = None): + xhiveframework.request.after_response.add(lambda: _mark_email_as_seen(name)) + xhiveframework.response.update(xhiveframework.utils.get_imaginary_pixel_response()) + + +def _mark_email_as_seen(name): + try: + update_communication_as_read(name) + except Exception: + xhiveframework.log_error("Unable to mark as seen", None, "Communication", name) + + xhiveframework.db.commit() # nosemgrep: after_response requires explicit commit + + +def update_communication_as_read(name): + if not name or not isinstance(name, str): + return + + communication = xhiveframework.db.get_value("Communication", name, "read_by_recipient", as_dict=True) + + if not communication or communication.read_by_recipient: + return + + xhiveframework.db.set_value( + "Communication", + name, + {"read_by_recipient": 1, "delivery_status": "Read", "read_by_recipient_on": get_datetime()}, + ) diff --git a/xhiveframework/core/doctype/communication/mixins.py b/xhiveframework/core/doctype/communication/mixins.py new file mode 100644 index 0000000..a256213 --- /dev/null +++ b/xhiveframework/core/doctype/communication/mixins.py @@ -0,0 +1,321 @@ +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.utils import get_parent_doc +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + is_email_notifications_enabled_for_type, +) +from xhiveframework.desk.doctype.todo.todo import ToDo +from xhiveframework.email.doctype.email_account.email_account import EmailAccount +from xhiveframework.utils import get_formatted_email, get_url, parse_addr + + +class CommunicationEmailMixin: + """Mixin class to handle communication mails.""" + + def is_email_communication(self): + return self.communication_type == "Communication" and self.communication_medium == "Email" + + def get_owner(self): + """Get owner of the communication docs parent.""" + parent_doc = get_parent_doc(self) + return parent_doc.owner if parent_doc else None + + def get_all_email_addresses(self, exclude_displayname=False): + """Get all Email addresses mentioned in the doc along with display name.""" + return ( + self.to_list(exclude_displayname=exclude_displayname) + + self.cc_list(exclude_displayname=exclude_displayname) + + self.bcc_list(exclude_displayname=exclude_displayname) + ) + + def get_email_with_displayname(self, email_address): + """Returns email address after adding displayname.""" + display_name, email = parse_addr(email_address) + if display_name and display_name != email: + return email_address + + # emailid to emailid with display name map. + email_map = {parse_addr(email)[1]: email for email in self.get_all_email_addresses()} + return email_map.get(email, email) + + def mail_recipients(self, is_inbound_mail_communcation=False): + """Build to(recipient) list to send an email.""" + # Incase of inbound mail, recipients already received the mail, no need to send again. + if is_inbound_mail_communcation: + return [] + + if hasattr(self, "_final_recipients"): + return self._final_recipients + + to = self.to_list() + self._final_recipients = list(filter(lambda id: id != "Administrator", to)) + return self._final_recipients + + def get_mail_recipients_with_displayname(self, is_inbound_mail_communcation=False): + """Build to(recipient) list to send an email including displayname in email.""" + to_list = self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation) + return [self.get_email_with_displayname(email) for email in to_list] + + def mail_cc(self, is_inbound_mail_communcation=False, include_sender=False): + """Build cc list to send an email. + + * if email copy is requested by sender, then add sender to CC. + * If this doc is created through inbound mail, then add doc owner to cc list + * remove all the thread_notify disabled users. + * Remove standard users from email list + """ + if hasattr(self, "_final_cc"): + return self._final_cc + + cc = self.cc_list() + + if include_sender: + sender = self.sender_mailid + # if user has selected send_me_a_copy, use their email as sender + if xhiveframework.session.user not in xhiveframework.STANDARD_USERS: + sender = xhiveframework.db.get_value("User", xhiveframework.session.user, "email") + cc.append(sender) + + if is_inbound_mail_communcation: + # inform parent document owner incase communication is created through inbound mail + if doc_owner := self.get_owner(): + cc.append(doc_owner) + cc = set(cc) - {self.sender_mailid} + assignees = set(self.get_assignees()) + # Check and remove If user disabled notifications for incoming emails on assigned document. + for assignee in assignees.copy(): + if not is_email_notifications_enabled_for_type(assignee, "threads_on_assigned_document"): + assignees.remove(assignee) + cc.update(assignees) + + cc = set(cc) - set(self.filter_thread_notification_disbled_users(cc)) + cc = cc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation)) + + # # Incase of inbound mail, to and cc already received the mail, no need to send again. + if is_inbound_mail_communcation: + cc = cc - set(self.cc_list() + self.to_list()) + + self._final_cc = [m for m in cc if m and m not in xhiveframework.STANDARD_USERS] + return self._final_cc + + def get_mail_cc_with_displayname(self, is_inbound_mail_communcation=False, include_sender=False): + cc_list = self.mail_cc( + is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender + ) + return [self.get_email_with_displayname(email) for email in cc_list if email] + + def mail_bcc(self, is_inbound_mail_communcation=False): + """ + * Thread_notify check + * Email unsubscribe list + * remove standard users. + """ + if hasattr(self, "_final_bcc"): + return self._final_bcc + + bcc = set(self.bcc_list()) + if is_inbound_mail_communcation: + bcc = bcc - {self.sender_mailid} + bcc = bcc - set(self.filter_thread_notification_disbled_users(bcc)) + bcc = bcc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation)) + + # Incase of inbound mail, to and cc & bcc already received the mail, no need to send again. + if is_inbound_mail_communcation: + bcc = bcc - set(self.bcc_list() + self.to_list()) + + self._final_bcc = [m for m in bcc if m not in xhiveframework.STANDARD_USERS] + return self._final_bcc + + def get_mail_bcc_with_displayname(self, is_inbound_mail_communcation=False): + bcc_list = self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation) + return [self.get_email_with_displayname(email) for email in bcc_list if email] + + def mail_sender(self): + email_account = self.get_outgoing_email_account() + if not self.sender_mailid and email_account: + return email_account.email_id + return self.sender_mailid + + def mail_sender_fullname(self): + email_account = self.get_outgoing_email_account() + if not self.sender_full_name: + return (email_account and email_account.name) or _("Notification") + return self.sender_full_name + + def get_mail_sender_with_displayname(self): + return get_formatted_email(self.mail_sender_fullname(), mail=self.mail_sender()) + + def get_content(self, print_format=None): + if print_format and xhiveframework.db.get_single_value("System Settings", "attach_view_link"): + return self.content + self.get_attach_link(print_format) + return self.content + + def get_attach_link(self, print_format): + """Returns public link for the attachment via `templates/emails/print_link.html`.""" + return xhiveframework.get_template("templates/emails/print_link.html").render( + { + "url": get_url(), + "doctype": self.reference_doctype, + "name": self.reference_name, + "print_format": print_format, + "key": get_parent_doc(self).get_document_share_key(), + } + ) + + def get_outgoing_email_account(self): + if not hasattr(self, "_outgoing_email_account"): + if self.email_account: + self._outgoing_email_account = EmailAccount.find(self.email_account) + else: + self._outgoing_email_account = EmailAccount.find_outgoing( + match_by_email=self.sender_mailid, match_by_doctype=self.reference_doctype + ) + + if self.sent_or_received == "Sent" and self._outgoing_email_account: + if xhiveframework.db.exists("Email Account", self._outgoing_email_account.name): + self.db_set("email_account", self._outgoing_email_account.name) + + return self._outgoing_email_account + + def get_incoming_email_account(self): + if not hasattr(self, "_incoming_email_account"): + self._incoming_email_account = EmailAccount.find_incoming( + match_by_email=self.sender_mailid, match_by_doctype=self.reference_doctype + ) + return self._incoming_email_account + + def mail_attachments(self, print_format=None, print_html=None, print_language=None): + final_attachments = [] + + if print_format or print_html: + d = { + "print_format": print_format, + "html": print_html, + "print_format_attachment": 1, + "doctype": self.reference_doctype, + "name": self.reference_name, + "lang": print_language or xhiveframework.local.lang, + } + final_attachments.append(d) + + final_attachments.extend({"fid": a["name"]} for a in self.get_attachments() or []) + return final_attachments + + def get_unsubscribe_message(self): + email_account = self.get_outgoing_email_account() + if email_account and email_account.send_unsubscribe_message: + return _("Leave this conversation") + return "" + + def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False) -> list: + """List of mail id's excluded while sending mail.""" + all_ids = self.get_all_email_addresses(exclude_displayname=True) + + final_ids = ( + self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation) + + self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation) + + self.mail_cc( + is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender + ) + ) + + return list(set(all_ids) - set(final_ids)) + + def get_assignees(self): + """Get owners of the reference document.""" + filters = { + "status": "Open", + "reference_name": self.reference_name, + "reference_type": self.reference_doctype, + } + + if self.reference_doctype and self.reference_name: + return ToDo.get_owners(filters) + else: + return [] + + @staticmethod + def filter_thread_notification_disbled_users(emails): + """Filter users based on notifications for email threads setting is disabled.""" + if not emails: + return [] + + return xhiveframework.get_all("User", pluck="email", filters={"email": ["in", emails], "thread_notify": 0}) + + @staticmethod + def filter_disabled_users(emails): + """ """ + if not emails: + return [] + + return xhiveframework.get_all("User", pluck="email", filters={"email": ["in", emails], "enabled": 0}) + + def sendmail_input_dict( + self, + print_html=None, + print_format=None, + send_me_a_copy=None, + print_letterhead=None, + is_inbound_mail_communcation=None, + print_language=None, + ) -> dict: + outgoing_email_account = self.get_outgoing_email_account() + if not outgoing_email_account: + return {} + + recipients = self.get_mail_recipients_with_displayname( + is_inbound_mail_communcation=is_inbound_mail_communcation + ) + cc = self.get_mail_cc_with_displayname( + is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=send_me_a_copy + ) + bcc = self.get_mail_bcc_with_displayname(is_inbound_mail_communcation=is_inbound_mail_communcation) + + if not (recipients or cc): + return {} + + final_attachments = self.mail_attachments( + print_format=print_format, print_html=print_html, print_language=print_language + ) + incoming_email_account = self.get_incoming_email_account() + return { + "recipients": recipients, + "cc": cc, + "bcc": bcc, + "expose_recipients": "header", + "sender": self.get_mail_sender_with_displayname(), + "reply_to": incoming_email_account and incoming_email_account.email_id, + "subject": self.subject, + "content": self.get_content(print_format=print_format), + "reference_doctype": self.reference_doctype, + "reference_name": self.reference_name, + "attachments": final_attachments, + "message_id": self.message_id, + "unsubscribe_message": self.get_unsubscribe_message(), + "delayed": True, + "communication": self.name, + "read_receipt": self.read_receipt, + "is_notification": (self.sent_or_received == "Received" and True) or False, + "print_letterhead": print_letterhead, + "send_after": self.send_after, + } + + def send_email( + self, + print_html=None, + print_format=None, + send_me_a_copy=None, + print_letterhead=None, + is_inbound_mail_communcation=None, + print_language=None, + now=False, + ): + if input_dict := self.sendmail_input_dict( + print_html=print_html, + print_format=print_format, + send_me_a_copy=send_me_a_copy, + print_letterhead=print_letterhead, + is_inbound_mail_communcation=is_inbound_mail_communcation, + print_language=print_language, + ): + xhiveframework.sendmail(now=now, **input_dict) diff --git a/xhiveframework/core/doctype/communication/test_communication.py b/xhiveframework/core/doctype/communication/test_communication.py new file mode 100644 index 0000000..edc98e0 --- /dev/null +++ b/xhiveframework/core/doctype/communication/test_communication.py @@ -0,0 +1,465 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework.core.doctype.communication.communication import Communication, get_emails, parse_email +from xhiveframework.core.doctype.communication.email import add_attachments, make +from xhiveframework.email.doctype.email_queue.email_queue import EmailQueue +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +if TYPE_CHECKING: + from xhiveframework.contacts.doctype.contact.contact import Contact + from xhiveframework.email.doctype.email_account.email_account import EmailAccount + +test_records = xhiveframework.get_test_records("Communication") + + +class TestCommunication(XhiveFrameworkTestCase): + def test_email(self): + valid_email_list = [ + "Full Name ", + '"Full Name with quotes and " ', + "Surname, Name ", + "Purchase@ABC ", + "xyz@abc2.com ", + "Name [something else] ", + ] + + invalid_email_list = [ + "[invalid!email]", + "invalid-email", + "tes2", + "e", + "rrrrrrrr", + "manas", + "[[[sample]]]", + "[invalid!email].com", + ] + + for i, x in enumerate(valid_email_list): + with self.subTest(i=i, x=x): + self.assertTrue(xhiveframework.utils.parse_addr(x)[1]) + + for i, x in enumerate(invalid_email_list): + with self.subTest(i=i, x=x): + self.assertFalse(xhiveframework.utils.parse_addr(x)[0]) + + def test_name(self): + valid_email_list = [ + "Full Name ", + '"Full Name with quotes and " ', + "Surname, Name ", + "Purchase@ABC ", + "xyz@abc2.com ", + "Name [something else] ", + ] + + invalid_email_list = [ + "[invalid!email]", + "invalid-email", + "tes2", + "e", + "rrrrrrrr", + "manas", + "[[[sample]]]", + "[invalid!email].com", + ] + + for x in valid_email_list: + self.assertTrue(xhiveframework.utils.parse_addr(x)[0]) + + for x in invalid_email_list: + self.assertFalse(xhiveframework.utils.parse_addr(x)[0]) + + def test_circular_linking(self): + a = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "This was created to test circular linking: Communication A", + } + ).insert(ignore_permissions=True) + + b = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "This was created to test circular linking: Communication B", + "reference_doctype": "Communication", + "reference_name": a.name, + } + ).insert(ignore_permissions=True) + + c = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "This was created to test circular linking: Communication C", + "reference_doctype": "Communication", + "reference_name": b.name, + } + ).insert(ignore_permissions=True) + + a = xhiveframework.get_doc("Communication", a.name) + a.reference_doctype = "Communication" + a.reference_name = c.name + + self.assertRaises(xhiveframework.CircularLinkingError, a.save) + + def test_deduplication_timeline_links(self): + xhiveframework.delete_doc_if_exists("Note", "deduplication timeline links") + + note = xhiveframework.get_doc( + { + "doctype": "Note", + "title": "deduplication timeline links", + "content": "deduplication timeline links", + } + ).insert(ignore_permissions=True) + + comm = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "Deduplication of Links", + "communication_medium": "Email", + } + ).insert(ignore_permissions=True) + + # adding same link twice + comm.add_link(link_doctype="Note", link_name=note.name, autosave=True) + comm.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comm = xhiveframework.get_doc("Communication", comm.name) + + self.assertNotEqual(2, len(comm.timeline_links)) + + def test_contacts_attached(self): + contact_sender: "Contact" = xhiveframework.get_doc( + { + "doctype": "Contact", + "first_name": "contact_sender", + } + ) + contact_sender.add_email("comm_sender@example.com") + contact_sender.insert(ignore_permissions=True) + + contact_recipient: "Contact" = xhiveframework.get_doc( + { + "doctype": "Contact", + "first_name": "contact_recipient", + } + ) + contact_recipient.add_email("comm_recipient@example.com") + contact_recipient.insert(ignore_permissions=True) + + contact_cc: "Contact" = xhiveframework.get_doc( + { + "doctype": "Contact", + "first_name": "contact_cc", + } + ) + contact_cc.add_email("comm_cc@example.com") + contact_cc.insert(ignore_permissions=True) + + comm: Communication = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_medium": "Email", + "subject": "Contacts Attached Test", + "sender": "comm_sender@example.com", + "recipients": "comm_recipient@example.com", + "cc": "comm_cc@example.com", + } + ).insert(ignore_permissions=True) + + comm = xhiveframework.get_doc("Communication", comm.name) + contact_links = [x.link_name for x in comm.timeline_links] + + self.assertIn(contact_sender.name, contact_links) + self.assertIn(contact_recipient.name, contact_links) + self.assertIn(contact_cc.name, contact_links) + + def test_get_communication_data(self): + from xhiveframework.desk.form.load import get_communication_data + + xhiveframework.delete_doc_if_exists("Note", "get communication data") + + note = xhiveframework.get_doc( + {"doctype": "Note", "title": "get communication data", "content": "get communication data"} + ).insert(ignore_permissions=True) + + comm_note_1 = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "Test Get Communication Data 1", + "communication_medium": "Email", + } + ).insert(ignore_permissions=True) + + comm_note_1.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comm_note_2 = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "content": "Test Get Communication Data 2", + "communication_medium": "Email", + } + ).insert(ignore_permissions=True) + + comm_note_2.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comms = get_communication_data("Note", note.name, as_dict=True) + + data = [comm.name for comm in comms] + self.assertIn(comm_note_1.name, data) + self.assertIn(comm_note_2.name, data) + + def test_parse_email(self): + to = "Jon Doe " + cc = """=?UTF-8?Q?Max_Mu=C3=9F?= , + erp+Customer+that%20company@example.org""" + bcc = "" + + results = list(parse_email([to, cc, bcc])) + self.assertEqual([("Customer", "that company")], results) + + results = list(parse_email([to, bcc])) + self.assertEqual(results, []) + + to = "jane.doe+A+Test@example.org" + cc = "" + bcc = "=?UTF-8?Q?Max_Mu=C3=9F?= " + results = list(parse_email([to, cc, bcc])) + self.assertEqual([("A", "Test"), ("Note", "Very important")], results) + + def test_get_emails(self): + emails = get_emails( + [ + "comm_recipient+DocType+DocName@example.com", + '"First, LastName" ', + "test@user.com", + ] + ) + + self.assertEqual(emails[0], "comm_recipient+DocType+DocName@example.com") + self.assertEqual(emails[1], "first.lastname@email.com") + self.assertEqual(emails[2], "test@user.com") + + def test_signature_in_email_content(self): + email_account = create_email_account() + signature = email_account.signature + base_communication = { + "doctype": "Communication", + "communication_medium": "Email", + "subject": "Document Link in Email", + "sender": "comm_sender@example.com", + } + comm_with_signature = xhiveframework.get_doc( + base_communication + | { + "content": f"""

    + Hi, + How are you? +


    {signature}

    """, + } + ).insert(ignore_permissions=True) + comm_without_signature = xhiveframework.get_doc( + base_communication + | { + "content": """
    + Hi, + How are you? +
    """ + } + ).insert(ignore_permissions=True) + + self.assertEqual(comm_with_signature.content, comm_without_signature.content) + self.assertEqual(comm_with_signature.content.count(signature), 1) + self.assertEqual(comm_without_signature.content.count(signature), 1) + + def test_mark_as_spam(self): + xhiveframework.get_doc( + { + "doctype": "Email Rule", + "email_id": "spammer@example.com", + "is_spam": 1, + } + ).insert(ignore_permissions=True) + + spam_comm: Communication = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_medium": "Email", + "subject": "This is spam", + "sender": "spammer@example.com", + "recipients": "comm_recipient@example.com", + "sent_or_received": "Received", + } + ).insert(ignore_permissions=True) + + self.assertEqual(spam_comm.email_status, "Spam") + + normal_comm: Communication = xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_medium": "Email", + "subject": "This is spam", + "sender": "friendlyhuman@example.com", + "recipients": "comm_recipient@example.com", + "sent_or_received": "Received", + } + ).insert(ignore_permissions=True) + self.assertNotEqual(normal_comm.email_status, "Spam") + + +class TestCommunicationEmailMixin(XhiveFrameworkTestCase): + def new_communication(self, recipients=None, cc=None, bcc=None) -> Communication: + recipients = ", ".join(recipients or []) + cc = ", ".join(cc or []) + bcc = ", ".join(bcc or []) + + return xhiveframework.get_doc( + { + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "content": "Test content", + "recipients": recipients, + "cc": cc, + "bcc": bcc, + "sender": "sender@test.com", + } + ).insert(ignore_permissions=True) + + def new_user(self, email, **user_data): + user_data.setdefault("first_name", "first_name") + user = xhiveframework.new_doc("User") + user.email = email + user.update(user_data) + user.insert(ignore_permissions=True, ignore_if_duplicate=True) + return user + + def test_recipients(self): + to_list = ["to@test.com", "receiver ", "to@test.com"] + comm = self.new_communication(recipients=to_list) + res = comm.get_mail_recipients_with_displayname() + self.assertCountEqual(res, ["to@test.com", "receiver "]) + comm.delete() + + def test_cc(self): + def test(assertion, cc_list=None, set_user_as=None, include_sender=False, thread_notify=False): + if set_user_as: + xhiveframework.set_user(set_user_as) + + user = self.new_user(email="cc+1@test.com", thread_notify=thread_notify) + comm = self.new_communication(recipients=["to@test.com"], cc=cc_list) + res = comm.get_mail_cc_with_displayname(include_sender=include_sender) + + xhiveframework.set_user("Administrator") + user.delete() + comm.delete() + + self.assertEqual(res, assertion) + + # test filter_thread_notification_disbled_users and filter_mail_recipients + test(["cc "], cc_list=["cc+1@test.com", "cc ", "to@test.com"]) + + # test include_sender + test(["sender@test.com"], include_sender=True, thread_notify=True) + test(["cc+1@test.com"], include_sender=True, thread_notify=True, set_user_as="cc+1@test.com") + + def test_bcc(self): + bcc_list = [ + "bcc+1@test.com", + "cc ", + ] + user = self.new_user(email="bcc+2@test.com", enabled=0) + comm = self.new_communication(bcc=bcc_list) + res = comm.get_mail_bcc_with_displayname() + self.assertCountEqual(res, bcc_list) + user.delete() + comm.delete() + + def test_sendmail(self): + to_list = ["to "] + cc_list = ["cc ", "cc "] + + comm = self.new_communication(recipients=to_list, cc=cc_list) + comm.send_email() + doc = EmailQueue.find_one_by_filters(communication=comm.name) + mail_receivers = [each.recipient for each in doc.recipients] + self.assertIsNotNone(doc) + self.assertCountEqual(to_list + cc_list, mail_receivers) + doc.delete() + comm.delete() + + def test_add_attachments_by_filename(self): + to_list = ["to "] + comm = self.new_communication(recipients=to_list) + + file = xhiveframework.new_doc("File") + file.file_name = "test_add_attachments_by_filename.txt" + file.content = "test_add_attachments_by_filename" + file.insert(ignore_permissions=True) + + add_attachments(comm.name, [file.name]) + + attached_file_name, attached_content_hash = xhiveframework.db.get_value( + "File", + {"attached_to_name": comm.name, "attached_to_doctype": comm.doctype}, + ["file_name", "content_hash"], + ) + self.assertEqual(attached_content_hash, file.content_hash) + self.assertEqual(attached_file_name, file.file_name) + + def test_add_attachments_by_file_content(self): + to_list = ["to "] + comm = self.new_communication(recipients=to_list) + file_name = "test_add_attachments_by_file_content.txt" + file_content = "test_add_attachments_by_file_content" + add_attachments(comm.name, [{"fcontent": file_content, "fname": file_name}]) + attached_file_name = xhiveframework.db.get_value( + "File", + {"attached_to_name": comm.name, "attached_to_doctype": comm.doctype}, + ) + attached_file = xhiveframework.get_doc("File", attached_file_name) + self.assertEqual(attached_file.file_name, file_name) + self.assertEqual(attached_file.get_content(), file_content) + + +def create_email_account() -> "EmailAccount": + xhiveframework.delete_doc_if_exists("Email Account", "_Test Comm Account 1") + + xhiveframework.flags.mute_emails = False + xhiveframework.flags.sent_mail = None + + return xhiveframework.get_doc( + { + "is_default": 1, + "is_global": 1, + "doctype": "Email Account", + "domain": "example.com", + "append_to": "ToDo", + "email_account_name": "_Test Comm Account 1", + "enable_outgoing": 1, + "default_outgoing": 1, + "smtp_server": "test.example.com", + "email_id": "test_comm@example.com", + "password": "password", + "add_signature": 1, + "signature": "\nBest Wishes\nTest Signature", + "enable_auto_reply": 1, + "auto_reply_message": "", + "enable_incoming": 1, + "notify_if_unreplied": 1, + "unreplied_for_mins": 20, + "send_notification_to": "test_comm@example.com", + "pop3_server": "pop.test.example.com", + "imap_folder": [{"folder_name": "INBOX", "append_to": "ToDo"}], + "enable_automatic_linking": 1, + } + ).insert(ignore_permissions=True) diff --git a/xhiveframework/core/doctype/communication/test_records.json b/xhiveframework/core/doctype/communication/test_records.json new file mode 100644 index 0000000..a69d3e9 --- /dev/null +++ b/xhiveframework/core/doctype/communication/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doctype": "Communication", + "name": "_Test Communication 1", + "subject": "Test Subject", + "sent_or_received": "Received", + "parenttype": "User", + "parent": "Administrator" + } +] diff --git a/xhiveframework/core/doctype/communication_link/__init__.py b/xhiveframework/core/doctype/communication_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/communication_link/communication_link.json b/xhiveframework/core/doctype/communication_link/communication_link.json new file mode 100644 index 0000000..1dd051b --- /dev/null +++ b/xhiveframework/core/doctype/communication_link/communication_link.json @@ -0,0 +1,47 @@ +{ + "creation": "2019-05-21 09:47:23.043960", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "link_doctype", + "link_name", + "link_title" + ], + "fields": [ + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Link DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "link_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Link Name", + "options": "link_doctype", + "reqd": 1 + }, + { + "fieldname": "link_title", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Link Title", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-05-21 09:47:23.043960", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/communication_link/communication_link.py b/xhiveframework/core/doctype/communication_link/communication_link.py new file mode 100644 index 0000000..99d3243 --- /dev/null +++ b/xhiveframework/core/doctype/communication_link/communication_link.py @@ -0,0 +1,28 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class CommunicationLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + link_doctype: DF.Link + link_name: DF.DynamicLink + link_title: DF.ReadOnly | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass + + +def on_doctype_update(): + xhiveframework.db.add_index("Communication Link", ["link_doctype", "link_name"]) diff --git a/xhiveframework/core/doctype/custom_docperm/__init__.py b/xhiveframework/core/doctype/custom_docperm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/custom_docperm/custom_docperm.js b/xhiveframework/core/doctype/custom_docperm/custom_docperm.js new file mode 100644 index 0000000..d7055e3 --- /dev/null +++ b/xhiveframework/core/doctype/custom_docperm/custom_docperm.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Custom DocPerm", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/custom_docperm/custom_docperm.json b/xhiveframework/core/doctype/custom_docperm/custom_docperm.json new file mode 100644 index 0000000..208b0be --- /dev/null +++ b/xhiveframework/core/doctype/custom_docperm/custom_docperm.json @@ -0,0 +1,241 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "hash", + "creation": "2017-01-11 04:21:35.217943", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parent", + "role_and_level", + "role", + "if_owner", + "column_break_2", + "permlevel", + "section_break_4", + "select", + "read", + "write", + "create", + "delete", + "column_break_8", + "submit", + "cancel", + "amend", + "additional_permissions", + "report", + "export", + "import", + "column_break_19", + "share", + "print", + "email" + ], + "fields": [ + { + "fieldname": "role_and_level", + "fieldtype": "Section Break", + "label": "Role and Level" + }, + { + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "default": "0", + "description": "Apply this rule if the User is the Owner", + "fieldname": "if_owner", + "fieldtype": "Check", + "label": "If user is the owner" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "print_width": "40px", + "width": "40px" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Permissions" + }, + { + "default": "1", + "fieldname": "read", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Read", + "oldfieldname": "read", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "write", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Write", + "oldfieldname": "write", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "create", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Create", + "oldfieldname": "create", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "delete", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Delete" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "submit", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Submit", + "oldfieldname": "submit", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "cancel", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Cancel", + "oldfieldname": "cancel", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "amend", + "fieldtype": "Check", + "label": "Amend", + "oldfieldname": "amend", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "fieldname": "additional_permissions", + "fieldtype": "Section Break", + "label": "Additional Permissions" + }, + { + "default": "0", + "fieldname": "report", + "fieldtype": "Check", + "label": "Report", + "print_width": "32px", + "width": "32px" + }, + { + "default": "1", + "fieldname": "export", + "fieldtype": "Check", + "label": "Export" + }, + { + "default": "0", + "fieldname": "import", + "fieldtype": "Check", + "label": "Import" + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "share", + "fieldtype": "Check", + "label": "Share" + }, + { + "default": "0", + "fieldname": "print", + "fieldtype": "Check", + "label": "Print" + }, + { + "default": "0", + "fieldname": "email", + "fieldtype": "Check", + "label": "Email" + }, + { + "fieldname": "parent", + "fieldtype": "Data", + "label": "Reference Document Type", + "read_only": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "select", + "fieldtype": "Check", + "label": "Select" + } + ], + "links": [], + "modified": "2023-02-20 13:19:04.889081", + "modified_by": "Administrator", + "module": "Core", + "name": "Custom DocPerm", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "parent" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/custom_docperm/custom_docperm.py b/xhiveframework/core/doctype/custom_docperm/custom_docperm.py new file mode 100644 index 0000000..386fa40 --- /dev/null +++ b/xhiveframework/core/doctype/custom_docperm/custom_docperm.py @@ -0,0 +1,37 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class CustomDocPerm(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + amend: DF.Check + cancel: DF.Check + create: DF.Check + delete: DF.Check + email: DF.Check + export: DF.Check + if_owner: DF.Check + parent: DF.Data | None + permlevel: DF.Int + print: DF.Check + read: DF.Check + report: DF.Check + role: DF.Link + select: DF.Check + share: DF.Check + submit: DF.Check + write: DF.Check + + # end: auto-generated types + def on_update(self): + xhiveframework.clear_cache(doctype=self.parent) diff --git a/xhiveframework/core/doctype/custom_docperm/test_custom_docperm.py b/xhiveframework/core/doctype/custom_docperm/test_custom_docperm.py new file mode 100644 index 0000000..16c3974 --- /dev/null +++ b/xhiveframework/core/doctype/custom_docperm/test_custom_docperm.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Custom DocPerm') + + +class TestCustomDocPerm(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/custom_role/__init__.py b/xhiveframework/core/doctype/custom_role/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/custom_role/custom_role.js b/xhiveframework/core/doctype/custom_role/custom_role.js new file mode 100644 index 0000000..2162394 --- /dev/null +++ b/xhiveframework/core/doctype/custom_role/custom_role.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Custom Role", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/custom_role/custom_role.json b/xhiveframework/core/doctype/custom_role/custom_role.json new file mode 100644 index 0000000..7504882 --- /dev/null +++ b/xhiveframework/core/doctype/custom_role/custom_role.json @@ -0,0 +1,76 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "hash", + "creation": "2017-02-13 14:53:36.240122", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "page", + "report", + "permission_rules", + "roles", + "response", + "ref_doctype" + ], + "fields": [ + { + "fieldname": "page", + "fieldtype": "Link", + "label": "Page", + "options": "Page" + }, + { + "fieldname": "report", + "fieldtype": "Link", + "label": "Report", + "options": "Report" + }, + { + "fieldname": "permission_rules", + "fieldtype": "Section Break", + "label": "Permission Rules" + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "label": "Role", + "options": "Has Role" + }, + { + "fieldname": "response", + "fieldtype": "HTML", + "label": "response" + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Data", + "label": "Reference Document Type" + } + ], + "links": [], + "modified": "2022-08-03 12:20:52.985554", + "modified_by": "Administrator", + "module": "Core", + "name": "Custom Role", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/custom_role/custom_role.py b/xhiveframework/core/doctype/custom_role/custom_role.py new file mode 100644 index 0000000..d930dd8 --- /dev/null +++ b/xhiveframework/core/doctype/custom_role/custom_role.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class CustomRole(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.types import DF + + page: DF.Link | None + ref_doctype: DF.Data | None + report: DF.Link | None + roles: DF.Table[HasRole] + + # end: auto-generated types + def validate(self): + if self.report and not self.ref_doctype: + self.ref_doctype = xhiveframework.db.get_value("Report", self.report, "ref_doctype") + + +def get_custom_allowed_roles(field, name): + allowed_roles = [] + custom_role = xhiveframework.db.get_value("Custom Role", {field: name}, "name") + if custom_role: + custom_role_doc = xhiveframework.get_doc("Custom Role", custom_role) + allowed_roles = [d.role for d in custom_role_doc.roles] + + return allowed_roles diff --git a/xhiveframework/core/doctype/custom_role/test_custom_role.py b/xhiveframework/core/doctype/custom_role/test_custom_role.py new file mode 100644 index 0000000..a3d2a8c --- /dev/null +++ b/xhiveframework/core/doctype/custom_role/test_custom_role.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Custom Role') + + +class TestCustomRole(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/data_export/__init__.py b/xhiveframework/core/doctype/data_export/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/data_export/data_export.js b/xhiveframework/core/doctype/data_export/data_export.js new file mode 100644 index 0000000..c90d7ef --- /dev/null +++ b/xhiveframework/core/doctype/data_export/data_export.js @@ -0,0 +1,180 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Data Export", { + refresh: (frm) => { + frm.disable_save(); + frm.page.set_primary_action("Export", () => { + can_export(frm) ? export_data(frm) : null; + }); + }, + onload: (frm) => { + frm.set_query("reference_doctype", () => { + return { + filters: { + issingle: 0, + istable: 0, + name: ["in", xhiveframework.boot.user.can_export], + }, + }; + }); + }, + reference_doctype: (frm) => { + const doctype = frm.doc.reference_doctype; + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => set_field_options(frm)); + } else { + reset_filter_and_field(frm); + } + }, + export_without_main_header: (frm) => { + frm.refresh(); + }, +}); + +const can_export = (frm) => { + const doctype = frm.doc.reference_doctype; + const parent_multicheck_options = frm.fields_multicheck[doctype] + ? frm.fields_multicheck[doctype].get_checked_options() + : []; + let is_valid_form = false; + if (!doctype) { + xhiveframework.msgprint(__("Please select the Document Type.")); + } else if (!parent_multicheck_options.length) { + xhiveframework.msgprint(__("Atleast one field of Parent Document Type is mandatory")); + } else { + is_valid_form = true; + } + return is_valid_form; +}; + +const export_data = (frm) => { + let get_template_url = "/api/method/xhiveframework.core.doctype.data_export.exporter.export_data"; + var export_params = () => { + let columns = {}; + Object.keys(frm.fields_multicheck).forEach((dt) => { + const options = frm.fields_multicheck[dt].get_checked_options(); + columns[dt] = options; + }); + return { + doctype: frm.doc.reference_doctype, + select_columns: JSON.stringify(columns), + filters: frm.filter_list.get_filters().map((filter) => filter.slice(1, 4)), + file_type: frm.doc.file_type, + template: !frm.doc.export_without_main_header, + with_data: 1, + export_without_column_meta: frm.doc.export_without_main_header ? true : false, + }; + }; + + open_url_post(get_template_url, export_params()); +}; + +const reset_filter_and_field = (frm) => { + const parent_wrapper = frm.fields_dict.fields_multicheck.$wrapper; + const filter_wrapper = frm.fields_dict.filter_list.$wrapper; + parent_wrapper.empty(); + filter_wrapper.empty(); + frm.filter_list = []; + frm.fields_multicheck = {}; +}; + +const set_field_options = (frm) => { + const parent_wrapper = frm.fields_dict.fields_multicheck.$wrapper; + const filter_wrapper = frm.fields_dict.filter_list.$wrapper; + const doctype = frm.doc.reference_doctype; + const related_doctypes = get_doctypes(doctype); + + parent_wrapper.empty(); + filter_wrapper.empty(); + + frm.filter_list = new xhiveframework.ui.FilterGroup({ + parent: filter_wrapper, + doctype: doctype, + on_change: () => {}, + }); + + // Add 'Select All' and 'Unselect All' button + make_multiselect_buttons(parent_wrapper); + + frm.fields_multicheck = {}; + related_doctypes.forEach((dt) => { + frm.fields_multicheck[dt] = add_doctype_field_multicheck_control(dt, parent_wrapper); + }); + + frm.refresh(); +}; + +const make_multiselect_buttons = (parent_wrapper) => { + const button_container = $(parent_wrapper).append('
    ').find(".flex"); + + ["Select All", "Unselect All"].map((d) => { + xhiveframework.ui.form.make_control({ + parent: $(button_container), + df: { + label: __(d), + fieldname: xhiveframework.scrub(d), + fieldtype: "Button", + click: () => { + checkbox_toggle(d !== "Select All"); + }, + }, + render_input: true, + }); + }); + + $(button_container) + .find(".xhiveframework-control") + .map((index, button) => { + $(button).css({ "margin-right": "1em" }); + }); + + function checkbox_toggle(checked) { + $(parent_wrapper) + .find('[data-fieldtype="MultiCheck"]') + .map((index, element) => { + $(element).find(`:checkbox`).prop("checked", checked).trigger("click"); + }); + } +}; + +const get_doctypes = (parentdt) => { + return [parentdt].concat(xhiveframework.meta.get_table_fields(parentdt).map((df) => df.options)); +}; + +const add_doctype_field_multicheck_control = (doctype, parent_wrapper) => { + const fields = get_fields(doctype); + + xhiveframework.model.std_fields + .filter((df) => ["owner", "creation"].includes(df.fieldname)) + .forEach((df) => { + fields.push(df); + }); + + const options = fields.map((df) => { + return { + label: __(df.label, null, df.parent), + value: df.fieldname, + danger: df.reqd, + checked: 1, + }; + }); + + const multicheck_control = xhiveframework.ui.form.make_control({ + parent: parent_wrapper, + df: { + label: doctype, + fieldname: doctype + "_fields", + fieldtype: "MultiCheck", + options: options, + columns: 3, + }, + render_input: true, + }); + + multicheck_control.refresh_input(); + return multicheck_control; +}; + +const filter_fields = (df) => xhiveframework.model.is_value_type(df) && !df.hidden; +const get_fields = (dt) => xhiveframework.meta.get_docfields(dt).filter(filter_fields); diff --git a/xhiveframework/core/doctype/data_export/data_export.json b/xhiveframework/core/doctype/data_export/data_export.json new file mode 100644 index 0000000..f63d939 --- /dev/null +++ b/xhiveframework/core/doctype/data_export/data_export.json @@ -0,0 +1,84 @@ +{ + "actions": [], + "creation": "2018-03-07 10:09:49.794764", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "export_without_main_header", + "column_break_2", + "file_type", + "section_break", + "filter_list", + "fields_multicheck" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Select Doctype", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "CSV", + "fieldname": "file_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "File Type", + "options": "Excel\nCSV", + "reqd": 1 + }, + { + "depends_on": "reference_doctype", + "fieldname": "section_break", + "fieldtype": "Section Break" + }, + { + "fieldname": "filter_list", + "fieldtype": "HTML", + "label": "Filter List" + }, + { + "fieldname": "fields_multicheck", + "fieldtype": "HTML", + "label": "Fields Multicheck" + }, + { + "default": "0", + "description": "Export the data without any header notes and column descriptions", + "fieldname": "export_without_main_header", + "fieldtype": "Check", + "label": "Export without main header" + } + ], + "hide_toolbar": 1, + "issingle": 1, + "links": [], + "modified": "2022-09-28 03:51:02.404681", + "modified_by": "Administrator", + "module": "Core", + "name": "Data Export", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/data_export/data_export.py b/xhiveframework/core/doctype/data_export/data_export.py new file mode 100644 index 0000000..04ffa40 --- /dev/null +++ b/xhiveframework/core/doctype/data_export/data_export.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class DataExport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + export_without_main_header: DF.Check + file_type: DF.Literal["Excel", "CSV"] + reference_doctype: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/data_export/exporter.py b/xhiveframework/core/doctype/data_export/exporter.py new file mode 100644 index 0000000..8b176ce --- /dev/null +++ b/xhiveframework/core/doctype/data_export/exporter.py @@ -0,0 +1,480 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import csv +import os +import re + +import xhiveframework +import xhiveframework.permissions +from xhiveframework import _ +from xhiveframework.core.doctype.access_log.access_log import make_access_log +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.utils import cint, cstr, format_datetime, format_duration, formatdate, parse_json +from xhiveframework.utils.csvutils import UnicodeWriter + +reflags = {"I": re.I, "L": re.L, "M": re.M, "U": re.U, "S": re.S, "X": re.X, "D": re.DEBUG} + + +def get_data_keys(): + return xhiveframework._dict( + { + "data_separator": _("Start entering data below this line"), + "main_table": _("Table") + ":", + "parent_table": _("Parent Table") + ":", + "columns": _("Column Name") + ":", + "doctype": _("DocType") + ":", + } + ) + + +@xhiveframework.whitelist() +def export_data( + doctype=None, + parent_doctype=None, + all_doctypes=True, + with_data=False, + select_columns=None, + file_type="CSV", + template=False, + filters=None, + export_without_column_meta=False, +): + _doctype = doctype + if isinstance(_doctype, list): + _doctype = _doctype[0] + make_access_log( + doctype=_doctype, + file_type=file_type, + columns=select_columns, + filters=filters, + method=parent_doctype, + ) + + template_bool = template + if isinstance(template, str): + template_bool = template.lower() == "true" + + export_without_column_meta_bool = export_without_column_meta + if isinstance(export_without_column_meta, str): + export_without_column_meta_bool = export_without_column_meta.lower() == "true" + + exporter = DataExporter( + doctype=doctype, + parent_doctype=parent_doctype, + all_doctypes=all_doctypes, + with_data=with_data, + select_columns=select_columns, + file_type=file_type, + template=template_bool, + filters=filters, + export_without_column_meta=export_without_column_meta_bool, + ) + exporter.build_response() + + +class DataExporter: + def __init__( + self, + doctype=None, + parent_doctype=None, + all_doctypes=True, + with_data=False, + select_columns=None, + file_type="CSV", + template=False, + filters=None, + export_without_column_meta=False, + ): + self.doctype = doctype + self.parent_doctype = parent_doctype + self.all_doctypes = all_doctypes + self.with_data = cint(with_data) + self.select_columns = select_columns + self.file_type = file_type + self.template = template + self.filters = filters + self.export_without_column_meta = export_without_column_meta + self.data_keys = get_data_keys() + + self.prepare_args() + + def prepare_args(self): + if self.select_columns: + self.select_columns = parse_json(self.select_columns) + if self.filters: + self.filters = parse_json(self.filters) + + self.docs_to_export = {} + if self.doctype: + if isinstance(self.doctype, str): + self.doctype = [self.doctype] + + if len(self.doctype) > 1: + self.docs_to_export = self.doctype[1] + self.doctype = self.doctype[0] + + if not self.parent_doctype: + self.parent_doctype = self.doctype + + self.column_start_end = {} + + if self.all_doctypes: + self.child_doctypes = [ + dict(doctype=df.options, parentfield=df.fieldname) + for df in xhiveframework.get_meta(self.doctype).get_table_fields() + ] + + def build_response(self): + self.writer = UnicodeWriter() + self.name_field = "parent" if self.parent_doctype != self.doctype else "name" + + if self.template: + self.add_main_header() + + # No need of empty row at the start + if not self.export_without_column_meta: + self.writer.writerow([""]) + + self.tablerow = [self.data_keys.doctype] + self.labelrow = [_("Column Labels:")] + self.fieldrow = [self.data_keys.columns] + self.mandatoryrow = [_("Mandatory:")] + self.typerow = [_("Type:")] + self.inforow = [_("Info:")] + self.columns = [] + + self.build_field_columns(self.doctype) + + if self.all_doctypes: + for d in self.child_doctypes: + self.append_empty_field_column() + if ( + self.select_columns and self.select_columns.get(d["doctype"], None) + ) or not self.select_columns: + # if atleast one column is selected for this doctype + self.build_field_columns(d["doctype"], d["parentfield"]) + + self.add_field_headings() + self.add_data() + if self.with_data and not self.data: + xhiveframework.respond_as_web_page( + _("No Data"), _("There is no data to be exported"), indicator_color="orange" + ) + + if self.file_type == "Excel": + self.build_response_as_excel() + else: + # write out response as a type csv + xhiveframework.response["result"] = cstr(self.writer.getvalue()) + xhiveframework.response["type"] = "csv" + xhiveframework.response["doctype"] = self.doctype + + def add_main_header(self): + self.writer.writerow([_("Data Import Template")]) + self.writer.writerow([self.data_keys.main_table, self.doctype]) + + if self.parent_doctype != self.doctype: + self.writer.writerow([self.data_keys.parent_table, self.parent_doctype]) + else: + self.writer.writerow([""]) + + self.writer.writerow([""]) + self.writer.writerow([_("Notes:")]) + self.writer.writerow([_("Please do not change the template headings.")]) + self.writer.writerow([_("First data column must be blank.")]) + self.writer.writerow([_('If you are uploading new records, leave the "name" (ID) column blank.')]) + self.writer.writerow( + [_('If you are uploading new records, "Naming Series" becomes mandatory, if present.')] + ) + self.writer.writerow( + [ + _( + "Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish." + ) + ] + ) + self.writer.writerow([_("For updating, you can update only selective columns.")]) + self.writer.writerow( + [_("You can only upload upto 5000 records in one go. (may be less in some cases)")] + ) + if self.name_field == "parent": + self.writer.writerow([_('"Parent" signifies the parent table in which this row must be added')]) + self.writer.writerow( + [_('If you are updating, please select "Overwrite" else existing rows will not be deleted.')] + ) + + def build_field_columns(self, dt, parentfield=None): + meta = xhiveframework.get_meta(dt) + + # build list of valid docfields + tablecolumns = [] + table_name = "tab" + dt + + for f in xhiveframework.db.get_table_columns_description(table_name): + field = meta.get_field(f.name) + if f.name in ["owner", "creation"]: + std_field = next((x for x in xhiveframework.model.std_fields if x["fieldname"] == f.name), None) + if std_field: + field = xhiveframework._dict( + { + "fieldname": std_field.get("fieldname"), + "label": std_field.get("label"), + "fieldtype": std_field.get("fieldtype"), + "options": std_field.get("options"), + "idx": 0, + "parent": dt, + } + ) + + if field and ( + (self.select_columns and f.name in self.select_columns[dt]) or not self.select_columns + ): + tablecolumns.append(field) + + tablecolumns.sort(key=lambda a: int(a.idx)) + + _column_start_end = xhiveframework._dict(start=0) + + if dt == self.doctype: + if (meta.get("autoname") and meta.get("autoname").lower() == "prompt") or (self.with_data): + self._append_name_column() + + # if importing only child table for new record, add parent field + if meta.get("istable") and not self.with_data: + self.append_field_column( + xhiveframework._dict( + { + "fieldname": "parent", + "parent": "", + "label": "Parent", + "fieldtype": "Data", + "reqd": 1, + "info": _( + "Parent is the name of the document to which the data will get added to." + ), + } + ), + True, + ) + + _column_start_end = xhiveframework._dict(start=0) + else: + _column_start_end = xhiveframework._dict(start=len(self.columns)) + + if self.with_data: + self._append_name_column(dt) + + for docfield in tablecolumns: + self.append_field_column(docfield, True) + + # all non mandatory fields + for docfield in tablecolumns: + self.append_field_column(docfield, False) + + # if there is one column, add a blank column (?) + if len(self.columns) - _column_start_end.start == 1: + self.append_empty_field_column() + + # append DocType name + self.tablerow[_column_start_end.start + 1] = dt + + if parentfield: + self.tablerow[_column_start_end.start + 2] = parentfield + + _column_start_end.end = len(self.columns) + 1 + + self.column_start_end[(dt, parentfield)] = _column_start_end + + def append_field_column(self, docfield, for_mandatory): + if not docfield: + return + if for_mandatory and not docfield.reqd: + return + if not for_mandatory and docfield.reqd: + return + if docfield.fieldname in ("parenttype", "trash_reason"): + return + if docfield.hidden: + return + if ( + self.select_columns + and docfield.fieldname not in self.select_columns.get(docfield.parent, []) + and docfield.fieldname != "name" + ): + return + + self.tablerow.append("") + self.fieldrow.append(docfield.fieldname) + self.labelrow.append(_(docfield.label, context=docfield.parent)) + self.mandatoryrow.append(docfield.reqd and "Yes" or "No") + self.typerow.append(docfield.fieldtype) + self.inforow.append(self.getinforow(docfield)) + self.columns.append(docfield.fieldname) + + def append_empty_field_column(self): + self.tablerow.append("~") + self.fieldrow.append("~") + self.labelrow.append("") + self.mandatoryrow.append("") + self.typerow.append("") + self.inforow.append("") + self.columns.append("") + + @staticmethod + def getinforow(docfield): + """make info comment for options, links etc.""" + if docfield.fieldtype == "Select": + if not docfield.options: + return "" + else: + return _("One of") + ": %s" % ", ".join(filter(None, docfield.options.split("\n"))) + elif docfield.fieldtype == "Link": + return "Valid %s" % docfield.options + elif docfield.fieldtype == "Int": + return "Integer" + elif docfield.fieldtype == "Check": + return "0 or 1" + elif docfield.fieldtype in ["Date", "Datetime"]: + return cstr(xhiveframework.defaults.get_defaults().date_format) + elif hasattr(docfield, "info"): + return docfield.info + else: + return "" + + def add_field_headings(self): + if not self.export_without_column_meta: + self.writer.writerow(self.tablerow) + + # Just include Labels in the first row + self.writer.writerow(self.labelrow) + + if not self.export_without_column_meta: + self.writer.writerow(self.fieldrow) + self.writer.writerow(self.mandatoryrow) + self.writer.writerow(self.typerow) + self.writer.writerow(self.inforow) + + if self.template: + self.writer.writerow([self.data_keys.data_separator]) + + def add_data(self): + from xhiveframework.query_builder import DocType + + if self.template and not self.with_data: + return + + xhiveframework.permissions.can_export(self.parent_doctype, raise_exception=True) + + # sort nested set doctypes by `lft asc` + order_by = None + table_columns = xhiveframework.db.get_table_columns(self.parent_doctype) + if "lft" in table_columns and "rgt" in table_columns: + order_by = f"`tab{self.parent_doctype}`.`lft` asc" + # get permitted data only + self.data = xhiveframework.get_list( + self.doctype, fields=["*"], filters=self.filters, limit_page_length=None, order_by=order_by + ) + + for doc in self.data: + op = self.docs_to_export.get("op") + names = self.docs_to_export.get("name") + + if names and op: + if op == "=" and doc.name not in names: + continue + elif op == "!=" and doc.name in names: + continue + elif names: + try: + sflags = self.docs_to_export.get("flags", "I,U").upper() + flags = 0 + for a in re.split(r"\W+", sflags): + flags = flags | reflags.get(a, 0) + + c = re.compile(names, flags) + m = c.match(doc.name) + if not m: + continue + except Exception: + if doc.name not in names: + continue + # add main table + rows = [] + + self.add_data_row(rows, self.doctype, None, doc, 0) + + if self.all_doctypes: + # add child tables + for c in self.child_doctypes: + if is_virtual_doctype(c["doctype"]): + continue + child_doctype_table = DocType(c["doctype"]) + data_row = ( + xhiveframework.qb.from_(child_doctype_table) + .select("*") + .where(child_doctype_table.parent == doc.name) + .where(child_doctype_table.parentfield == c["parentfield"]) + .orderby(child_doctype_table.idx) + ) + for ci, child in enumerate(data_row.run(as_dict=True)): + self.add_data_row(rows, c["doctype"], c["parentfield"], child, ci) + for row in rows: + self.writer.writerow(row) + + def add_data_row(self, rows, dt, parentfield, doc, rowidx): + d = doc.copy() + meta = xhiveframework.get_meta(dt) + if self.all_doctypes: + d.name = f'"{d.name}"' + + if len(rows) < rowidx + 1: + rows.append([""] * (len(self.columns) + 1)) + row = rows[rowidx] + + _column_start_end = self.column_start_end.get((dt, parentfield)) + + if _column_start_end: + for i, c in enumerate(self.columns[_column_start_end.start : _column_start_end.end]): + df = meta.get_field(c) + fieldtype = df.fieldtype if df else "Data" + value = d.get(c, "") + if value: + if fieldtype == "Date": + value = formatdate(value) + elif fieldtype == "Datetime": + value = format_datetime(value) + elif fieldtype == "Duration": + value = format_duration(value, df.hide_days) + + row[_column_start_end.start + i + 1] = value + + def build_response_as_excel(self): + from xhiveframework.desk.utils import provide_binary_file + from xhiveframework.utils.xlsxutils import make_xlsx + + filename = xhiveframework.generate_hash(length=10) + with open(filename, "wb") as f: + f.write(cstr(self.writer.getvalue()).encode("utf-8")) + f = open(filename) + reader = csv.reader(f) + xlsx_file = make_xlsx(reader, "Data Import Template" if self.template else "Data Export") + + f.close() + os.remove(filename) + + provide_binary_file(self.doctype, "xlsx", xlsx_file.getvalue()) + + def _append_name_column(self, dt=None): + self.append_field_column( + xhiveframework._dict( + { + "fieldname": "name" if dt else self.name_field, + "parent": dt or "", + "label": "ID", + "fieldtype": "Data", + "reqd": 1, + } + ), + True, + ) diff --git a/xhiveframework/core/doctype/data_export/test_data_exporter.py b/xhiveframework/core/doctype/data_export/test_data_exporter.py new file mode 100644 index 0000000..b571476 --- /dev/null +++ b/xhiveframework/core/doctype/data_export/test_data_exporter.py @@ -0,0 +1,113 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.data_export.exporter import DataExporter +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDataExporter(XhiveFrameworkTestCase): + def setUp(self): + self.doctype_name = "Test DocType for Export Tool" + self.doc_name = "Test Data for Export Tool" + self.create_doctype_if_not_exists(doctype_name=self.doctype_name) + self.create_test_data() + + def create_doctype_if_not_exists(self, doctype_name, force=False): + """ + Helper Function for setting up doctypes + """ + if force: + xhiveframework.delete_doc_if_exists("DocType", doctype_name) + xhiveframework.delete_doc_if_exists("DocType", "Child 1 of " + doctype_name) + + if xhiveframework.db.exists("DocType", doctype_name): + return + + # Child Table 1 + table_1_name = "Child 1 of " + doctype_name + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": table_1_name, + "module": "Custom", + "custom": 1, + "istable": 1, + "fields": [ + {"label": "Child Title", "fieldname": "child_title", "reqd": 1, "fieldtype": "Data"}, + {"label": "Child Number", "fieldname": "child_number", "fieldtype": "Int"}, + ], + } + ).insert() + + # Main Table + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": doctype_name, + "module": "Custom", + "custom": 1, + "autoname": "field:title", + "fields": [ + {"label": "Title", "fieldname": "title", "reqd": 1, "fieldtype": "Data"}, + {"label": "Number", "fieldname": "number", "fieldtype": "Int"}, + { + "label": "Table Field 1", + "fieldname": "table_field_1", + "fieldtype": "Table", + "options": table_1_name, + }, + ], + "permissions": [{"role": "System Manager"}], + } + ).insert() + + def create_test_data(self, force=False): + """ + Helper Function creating test data + """ + if force: + xhiveframework.delete_doc(self.doctype_name, self.doc_name) + + if not xhiveframework.db.exists(self.doctype_name, self.doc_name): + self.doc = xhiveframework.get_doc( + doctype=self.doctype_name, + title=self.doc_name, + number="100", + table_field_1=[ + {"child_title": "Child Title 1", "child_number": "50"}, + {"child_title": "Child Title 2", "child_number": "51"}, + ], + ).insert() + else: + self.doc = xhiveframework.get_doc(self.doctype_name, self.doc_name) + + def test_export_content(self): + exp = DataExporter(doctype=self.doctype_name, file_type="CSV") + exp.build_response() + + self.assertEqual(xhiveframework.response["type"], "csv") + self.assertEqual(xhiveframework.response["doctype"], self.doctype_name) + self.assertTrue(xhiveframework.response["result"]) + self.assertRegex(xhiveframework.response["result"], r"Child Title 1.*?,50") + self.assertRegex(xhiveframework.response["result"], r"Child Title 2.*?,51") + + def test_export_type(self): + for type in ["csv", "Excel"]: + with self.subTest(type=type): + exp = DataExporter(doctype=self.doctype_name, file_type=type) + exp.build_response() + + self.assertEqual(xhiveframework.response["doctype"], self.doctype_name) + self.assertTrue(xhiveframework.response["result"]) + + if type == "csv": + self.assertEqual(xhiveframework.response["type"], "csv") + elif type == "Excel": + self.assertEqual(xhiveframework.response["type"], "binary") + self.assertEqual( + xhiveframework.response["filename"], self.doctype_name + ".xlsx" + ) # 'Test DocType for Export Tool.xlsx') + self.assertTrue(xhiveframework.response["filecontent"]) + + def tearDown(self): + pass diff --git a/xhiveframework/core/doctype/data_import/__init__.py b/xhiveframework/core/doctype/data_import/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/data_import/data_import.css b/xhiveframework/core/doctype/data_import/data_import.css new file mode 100644 index 0000000..5206540 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/data_import.css @@ -0,0 +1,3 @@ +.warnings .warning { + margin-bottom: 40px; +} diff --git a/xhiveframework/core/doctype/data_import/data_import.js b/xhiveframework/core/doctype/data_import/data_import.js new file mode 100644 index 0000000..38681a0 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/data_import.js @@ -0,0 +1,536 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Data Import", { + setup(frm) { + xhiveframework.realtime.on("data_import_refresh", ({ data_import }) => { + frm.import_in_progress = false; + if (data_import !== frm.doc.name) return; + xhiveframework.model.clear_doc("Data Import", frm.doc.name); + xhiveframework.model.with_doc("Data Import", frm.doc.name).then(() => { + frm.refresh(); + }); + }); + xhiveframework.realtime.on("data_import_progress", (data) => { + frm.import_in_progress = true; + if (data.data_import !== frm.doc.name) { + return; + } + let percent = Math.floor((data.current * 100) / data.total); + let seconds = Math.floor(data.eta); + let minutes = Math.floor(data.eta / 60); + let eta_message = + // prettier-ignore + seconds < 60 + ? __('About {0} seconds remaining', [seconds]) + : minutes === 1 + ? __('About {0} minute remaining', [minutes]) + : __('About {0} minutes remaining', [minutes]); + + let message; + if (data.success) { + let message_args = [data.current, data.total, eta_message]; + message = + frm.doc.import_type === "Insert New Records" + ? __("Importing {0} of {1}, {2}", message_args) + : __("Updating {0} of {1}, {2}", message_args); + } + if (data.skipping) { + message = __("Skipping {0} of {1}, {2}", [data.current, data.total, eta_message]); + } + frm.dashboard.show_progress(__("Import Progress"), percent, message); + frm.page.set_indicator(__("In Progress"), "orange"); + frm.trigger("update_primary_action"); + + // hide progress when complete + if (data.current === data.total) { + setTimeout(() => { + frm.dashboard.hide(); + frm.refresh(); + }, 2000); + } + }); + + frm.set_query("reference_doctype", () => { + return { + filters: { + name: ["in", xhiveframework.boot.user.can_import], + }, + }; + }); + + frm.get_field("import_file").df.options = { + restrictions: { + allowed_file_types: [".csv", ".xls", ".xlsx"], + }, + }; + + frm.has_import_file = () => { + return frm.doc.import_file || frm.doc.google_sheets_url; + }; + }, + + refresh(frm) { + frm.page.hide_icon_group(); + frm.trigger("update_indicators"); + frm.trigger("import_file"); + frm.trigger("show_import_log"); + frm.trigger("show_import_warnings"); + frm.trigger("toggle_submit_after_import"); + + if (frm.doc.status != "Pending") frm.trigger("show_import_status"); + + frm.trigger("show_report_error_button"); + + if (frm.doc.status === "Partial Success") { + frm.add_custom_button(__("Export Errored Rows"), () => + frm.trigger("export_errored_rows") + ); + } + + if (frm.doc.status.includes("Success")) { + frm.add_custom_button(__("Go to {0} List", [__(frm.doc.reference_doctype)]), () => + xhiveframework.set_route("List", frm.doc.reference_doctype) + ); + } + }, + + onload_post_render(frm) { + frm.trigger("update_primary_action"); + }, + + update_primary_action(frm) { + if (frm.is_dirty()) { + frm.enable_save(); + return; + } + frm.disable_save(); + if (frm.doc.status !== "Success") { + if (!frm.is_new() && frm.has_import_file()) { + let label = frm.doc.status === "Pending" ? __("Start Import") : __("Retry"); + frm.page.set_primary_action(label, () => frm.events.start_import(frm)); + } else { + frm.page.set_primary_action(__("Save"), () => frm.save()); + } + } + }, + + update_indicators(frm) { + const indicator = xhiveframework.get_indicator(frm.doc); + if (indicator) { + frm.page.set_indicator(indicator[0], indicator[1]); + } else { + frm.page.clear_indicator(); + } + }, + + show_import_status(frm) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.data_import.data_import.get_import_status", + args: { + data_import_name: frm.doc.name, + }, + callback: function (r) { + let successful_records = cint(r.message.success); + let failed_records = cint(r.message.failed); + let total_records = cint(r.message.total_records); + + if (!total_records) return; + let action, message; + if (frm.doc.import_type === "Insert New Records") { + action = "imported"; + } else { + action = "updated"; + } + + if (failed_records === 0) { + let message_args = [action, successful_records]; + if (successful_records === 1) { + message = __("Successfully {0} 1 record.", message_args); + } else { + message = __("Successfully {0} {1} records.", message_args); + } + } else { + let message_args = [action, successful_records, total_records]; + if (successful_records === 1) { + message = __( + "Successfully {0} {1} record out of {2}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); + } else { + message = __( + "Successfully {0} {1} records out of {2}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); + } + } + + // If the job timed out, display an extra hint + if (r.message.status === "Timed Out") { + message += "
    " + __("Import timed out, please re-try."); + } + + frm.dashboard.set_headline(message); + }, + }); + }, + + show_report_error_button(frm) { + if (frm.doc.status === "Error") { + xhiveframework.db + .get_list("Error Log", { + filters: { method: frm.doc.name }, + fields: ["method", "error"], + order_by: "creation desc", + limit: 1, + }) + .then((result) => { + if (result.length > 0) { + frm.add_custom_button("Report Error", () => { + let fake_xhr = { + responseText: JSON.stringify({ + exc: result[0].error, + }), + }; + xhiveframework.request.report_error(fake_xhr, {}); + }); + } + }); + } + }, + + start_import(frm) { + frm.call({ + method: "form_start_import", + args: { data_import: frm.doc.name }, + btn: frm.page.btn_primary, + }).then((r) => { + if (r.message === true) { + frm.disable_save(); + } + }); + }, + + download_template(frm) { + xhiveframework.require("data_import_tools.bundle.js", () => { + frm.data_exporter = new xhiveframework.data_import.DataExporter( + frm.doc.reference_doctype, + frm.doc.import_type + ); + }); + }, + + reference_doctype(frm) { + frm.trigger("toggle_submit_after_import"); + }, + + toggle_submit_after_import(frm) { + frm.toggle_display("submit_after_import", false); + let doctype = frm.doc.reference_doctype; + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => { + let meta = xhiveframework.get_meta(doctype); + frm.toggle_display("submit_after_import", meta.is_submittable); + }); + } + }, + + google_sheets_url(frm) { + if (!frm.is_dirty()) { + frm.trigger("import_file"); + } else { + frm.trigger("update_primary_action"); + } + }, + + refresh_google_sheet(frm) { + frm.trigger("import_file"); + }, + + import_file(frm) { + frm.toggle_display("section_import_preview", frm.has_import_file()); + if (!frm.has_import_file()) { + frm.get_field("import_preview").$wrapper.empty(); + return; + } else { + frm.trigger("update_primary_action"); + } + + // load import preview + frm.get_field("import_preview").$wrapper.empty(); + $('') + .html(__("Loading import file...")) + .appendTo(frm.get_field("import_preview").$wrapper); + + frm.call({ + method: "get_preview_from_template", + args: { + data_import: frm.doc.name, + import_file: frm.doc.import_file, + google_sheets_url: frm.doc.google_sheets_url, + }, + error_handlers: { + TimestampMismatchError() { + // ignore this error + }, + }, + }).then((r) => { + let preview_data = r.message; + frm.events.show_import_preview(frm, preview_data); + frm.events.show_import_warnings(frm, preview_data); + }); + }, + + show_import_preview(frm, preview_data) { + let import_log = preview_data.import_log; + + if (frm.import_preview && frm.import_preview.doctype === frm.doc.reference_doctype) { + frm.import_preview.preview_data = preview_data; + frm.import_preview.import_log = import_log; + frm.import_preview.refresh(); + return; + } + + xhiveframework.require("data_import_tools.bundle.js", () => { + frm.import_preview = new xhiveframework.data_import.ImportPreview({ + wrapper: frm.get_field("import_preview").$wrapper, + doctype: frm.doc.reference_doctype, + preview_data, + import_log, + frm, + events: { + remap_column(changed_map) { + let template_options = JSON.parse(frm.doc.template_options || "{}"); + template_options.column_to_field_map = + template_options.column_to_field_map || {}; + Object.assign(template_options.column_to_field_map, changed_map); + frm.set_value("template_options", JSON.stringify(template_options)); + frm.save().then(() => frm.trigger("import_file")); + }, + }, + }); + }); + }, + + export_errored_rows(frm) { + open_url_post( + "/api/method/xhiveframework.core.doctype.data_import.data_import.download_errored_template", + { + data_import_name: frm.doc.name, + } + ); + }, + + export_import_log(frm) { + open_url_post( + "/api/method/xhiveframework.core.doctype.data_import.data_import.download_import_log", + { + data_import_name: frm.doc.name, + } + ); + }, + + show_import_warnings(frm, preview_data) { + let columns = preview_data.columns; + let warnings = JSON.parse(frm.doc.template_warnings || "[]"); + warnings = warnings.concat(preview_data.warnings || []); + + frm.toggle_display("import_warnings_section", warnings.length > 0); + if (warnings.length === 0) { + frm.get_field("import_warnings").$wrapper.html(""); + return; + } + + // group warnings by row + let warnings_by_row = {}; + let other_warnings = []; + for (let warning of warnings) { + if (warning.row) { + warnings_by_row[warning.row] = warnings_by_row[warning.row] || []; + warnings_by_row[warning.row].push(warning); + } else { + other_warnings.push(warning); + } + } + + let html = ""; + html += Object.keys(warnings_by_row) + .map((row_number) => { + let message = warnings_by_row[row_number] + .map((w) => { + if (w.field) { + let label = + w.field.label + + (w.field.parent !== frm.doc.reference_doctype + ? ` (${w.field.parent})` + : ""); + return `
  1. ${label}: ${w.message}
  2. `; + } + return `
  3. ${w.message}
  4. `; + }) + .join(""); + return ` +
    +
    ${__("Row {0}", [row_number])}
    +
      ${message}
    +
    + `; + }) + .join(""); + + html += other_warnings + .map((warning) => { + let header = ""; + if (warning.col) { + let column_number = `${__("Column {0}", [ + warning.col, + ])}`; + let column_header = columns[warning.col].header_title; + header = `${column_number} (${column_header})`; + } + return ` +
    +
    ${header}
    +
    ${warning.message}
    +
    + `; + }) + .join(""); + frm.get_field("import_warnings").$wrapper.html(` +
    +
    ${html}
    +
    + `); + }, + + show_failed_logs(frm) { + frm.trigger("show_import_log"); + }, + + render_import_log(frm) { + xhiveframework.call({ + method: "xhiveframework.client.get_list", + args: { + doctype: "Data Import Log", + filters: { + data_import: frm.doc.name, + }, + fields: ["success", "docname", "messages", "exception", "row_indexes"], + limit_page_length: 5000, + order_by: "log_index", + }, + callback: function (r) { + let logs = r.message; + + if (logs.length === 0) return; + + frm.toggle_display("import_log_section", true); + + let rows = logs + .map((log) => { + let html = ""; + if (log.success) { + if (frm.doc.import_type === "Insert New Records") { + html = __("Successfully imported {0}", [ + `${xhiveframework.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ]); + } else { + html = __("Successfully updated {0}", [ + `${xhiveframework.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ]); + } + } else { + let messages = JSON.parse(log.messages || "[]") + .map((m) => { + let title = m.title ? `${m.title}` : ""; + let message = m.message ? `
    ${m.message}
    ` : ""; + return title + message; + }) + .join(""); + let id = xhiveframework.dom.get_unique_id(); + html = `${messages} + +
    +
    +
    ${log.exception}
    +
    +
    `; + } + let indicator_color = log.success ? "green" : "red"; + let title = log.success ? __("Success") : __("Failure"); + + if (frm.doc.show_failed_logs && log.success) { + return ""; + } + + return ` + ${JSON.parse(log.row_indexes).join(", ")} + +
    ${title}
    + + + ${html} + + `; + }) + .join(""); + + if (!rows && frm.doc.show_failed_logs) { + rows = ` + ${__("No failed logs")} + `; + } + + frm.get_field("import_log_preview").$wrapper.html(` + + + + + + + ${rows} +
    ${__("Row Number")}${__("Status")}${__("Message")}
    + `); + }, + }); + }, + + show_import_log(frm) { + frm.toggle_display("import_log_section", false); + + if (frm.import_in_progress) { + return; + } + + xhiveframework.call({ + method: "xhiveframework.client.get_count", + args: { + doctype: "Data Import Log", + filters: { + data_import: frm.doc.name, + }, + }, + callback: function (r) { + let count = r.message; + if (count < 5000) { + frm.trigger("render_import_log"); + } else { + frm.toggle_display("import_log_section", false); + frm.add_custom_button(__("Export Import Log"), () => + frm.trigger("export_import_log") + ); + } + }, + }); + }, +}); diff --git a/xhiveframework/core/doctype/data_import/data_import.json b/xhiveframework/core/doctype/data_import/data_import.json new file mode 100644 index 0000000..02d65c8 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/data_import.json @@ -0,0 +1,198 @@ +{ + "actions": [], + "autoname": "format:{reference_doctype} Import on {creation}", + "beta": 1, + "creation": "2019-08-04 14:16:08.318714", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "import_type", + "download_template", + "import_file", + "payload_count", + "html_5", + "google_sheets_url", + "refresh_google_sheet", + "column_break_5", + "status", + "submit_after_import", + "mute_emails", + "template_options", + "import_warnings_section", + "template_warnings", + "import_warnings", + "section_import_preview", + "import_preview", + "import_log_section", + "show_failed_logs", + "import_log_preview" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "import_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Import Type", + "options": "\nInsert New Records\nUpdate Existing Records", + "reqd": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "import_file", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Import File", + "read_only_depends_on": "eval: ['Success', 'Partial Success'].includes(doc.status)" + }, + { + "fieldname": "import_preview", + "fieldtype": "HTML", + "label": "Import Preview" + }, + { + "fieldname": "section_import_preview", + "fieldtype": "Section Break", + "label": "Preview" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "template_options", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Options", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "import_log_preview", + "fieldtype": "HTML", + "label": "Import Log Preview" + }, + { + "default": "Pending", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "label": "Status", + "no_copy": 1, + "options": "Pending\nSuccess\nPartial Success\nError\nTimed Out", + "read_only": 1 + }, + { + "fieldname": "template_warnings", + "fieldtype": "Code", + "hidden": 1, + "label": "Template Warnings", + "options": "JSON" + }, + { + "default": "0", + "fieldname": "submit_after_import", + "fieldtype": "Check", + "label": "Submit After Import", + "set_only_once": 1 + }, + { + "fieldname": "import_warnings_section", + "fieldtype": "Section Break", + "label": "Import File Errors and Warnings" + }, + { + "fieldname": "import_warnings", + "fieldtype": "HTML", + "label": "Import Warnings" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "download_template", + "fieldtype": "Button", + "label": "Download Template" + }, + { + "default": "1", + "fieldname": "mute_emails", + "fieldtype": "Check", + "label": "Don't Send Emails", + "set_only_once": 1 + }, + { + "default": "0", + "fieldname": "show_failed_logs", + "fieldtype": "Check", + "label": "Show Only Failed Logs" + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file", + "fieldname": "html_5", + "fieldtype": "HTML", + "options": "
    Or
    " + }, + { + "depends_on": "eval:!doc.__islocal && !doc.import_file\n", + "description": "Must be a publicly accessible Google Sheets URL", + "fieldname": "google_sheets_url", + "fieldtype": "Data", + "label": "Import from Google Sheets", + "read_only_depends_on": "eval: ['Success', 'Partial Success'].includes(doc.status)" + }, + { + "depends_on": "eval:doc.google_sheets_url && !doc.__unsaved && ['Success', 'Partial Success'].includes(doc.status)", + "fieldname": "refresh_google_sheet", + "fieldtype": "Button", + "label": "Refresh Google Sheet" + }, + { + "fieldname": "payload_count", + "fieldtype": "Int", + "hidden": 1, + "label": "Payload Count", + "read_only": 1 + } + ], + "hide_toolbar": 1, + "links": [], + "modified": "2024-01-30 17:08:05.566686", + "modified_by": "Administrator", + "module": "Core", + "name": "Data Import", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/data_import/data_import.py b/xhiveframework/core/doctype/data_import/data_import.py new file mode 100644 index 0000000..0b6de00 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/data_import.py @@ -0,0 +1,308 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import os + +from rq.timeouts import JobTimeoutException + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.doctype.data_import.exporter import Exporter +from xhiveframework.core.doctype.data_import.importer import Importer +from xhiveframework.model import core_doctypes_list +from xhiveframework.model.document import Document +from xhiveframework.modules.import_file import import_file_by_path +from xhiveframework.utils.background_jobs import enqueue, is_job_enqueued +from xhiveframework.utils.csvutils import validate_google_sheets_url + +BLOCKED_DOCTYPES = set(core_doctypes_list) - {"User", "Role"} + + +class DataImport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + google_sheets_url: DF.Data | None + import_file: DF.Attach | None + import_type: DF.Literal["", "Insert New Records", "Update Existing Records"] + mute_emails: DF.Check + payload_count: DF.Int + reference_doctype: DF.Link + show_failed_logs: DF.Check + status: DF.Literal["Pending", "Success", "Partial Success", "Error", "Timed Out"] + submit_after_import: DF.Check + template_options: DF.Code | None + template_warnings: DF.Code | None + # end: auto-generated types + + def validate(self): + doc_before_save = self.get_doc_before_save() + if ( + not (self.import_file or self.google_sheets_url) + or (doc_before_save and doc_before_save.import_file != self.import_file) + or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url) + ): + self.template_options = "" + self.template_warnings = "" + + self.validate_doctype() + self.validate_import_file() + self.validate_google_sheets_url() + self.set_payload_count() + + def validate_doctype(self): + if self.reference_doctype in BLOCKED_DOCTYPES: + xhiveframework.throw(_("Importing {0} is not allowed.").format(self.reference_doctype)) + + def validate_import_file(self): + if self.import_file: + # validate template + self.get_importer() + + def validate_google_sheets_url(self): + if not self.google_sheets_url: + return + validate_google_sheets_url(self.google_sheets_url) + + def set_payload_count(self): + if self.import_file: + i = self.get_importer() + payloads = i.import_file.get_payloads_for_import() + self.payload_count = len(payloads) + + @xhiveframework.whitelist() + def get_preview_from_template(self, import_file=None, google_sheets_url=None): + if import_file: + self.import_file = import_file + + if google_sheets_url: + self.google_sheets_url = google_sheets_url + + if not (self.import_file or self.google_sheets_url): + return + + i = self.get_importer() + return i.get_data_for_import_preview() + + def start_import(self): + from xhiveframework.utils.scheduler import is_scheduler_inactive + + if is_scheduler_inactive() and not xhiveframework.flags.in_test: + xhiveframework.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")) + + job_id = f"data_import::{self.name}" + + if not is_job_enqueued(job_id): + enqueue( + start_import, + queue="default", + timeout=10000, + event="data_import", + job_id=job_id, + data_import=self.name, + now=xhiveframework.conf.developer_mode or xhiveframework.flags.in_test, + ) + return True + + return False + + def export_errored_rows(self): + return self.get_importer().export_errored_rows() + + def download_import_log(self): + return self.get_importer().export_import_log() + + def get_importer(self): + return Importer(self.reference_doctype, data_import=self) + + +@xhiveframework.whitelist() +def get_preview_from_template(data_import, import_file=None, google_sheets_url=None): + return xhiveframework.get_doc("Data Import", data_import).get_preview_from_template( + import_file, google_sheets_url + ) + + +@xhiveframework.whitelist() +def form_start_import(data_import): + return xhiveframework.get_doc("Data Import", data_import).start_import() + + +def start_import(data_import): + """This method runs in background job""" + data_import = xhiveframework.get_doc("Data Import", data_import) + try: + i = Importer(data_import.reference_doctype, data_import=data_import) + i.import_data() + except JobTimeoutException: + xhiveframework.db.rollback() + data_import.db_set("status", "Timed Out") + except Exception: + xhiveframework.db.rollback() + data_import.db_set("status", "Error") + data_import.log_error("Data import failed") + finally: + xhiveframework.flags.in_import = False + + xhiveframework.publish_realtime("data_import_refresh", {"data_import": data_import.name}) + + +@xhiveframework.whitelist() +def download_template(doctype, export_fields=None, export_records=None, export_filters=None, file_type="CSV"): + """ + Download template from Exporter + :param doctype: Document Type + :param export_fields=None: Fields to export as dict {'Sales Invoice': ['name', 'customer'], 'Sales Invoice Item': ['item_code']} + :param export_records=None: One of 'all', 'by_filter', 'blank_template' + :param export_filters: Filter dict + :param file_type: File type to export into + """ + + export_fields = xhiveframework.parse_json(export_fields) + export_filters = xhiveframework.parse_json(export_filters) + export_data = export_records != "blank_template" + + e = Exporter( + doctype, + export_fields=export_fields, + export_data=export_data, + export_filters=export_filters, + file_type=file_type, + export_page_length=5 if export_records == "5_records" else None, + ) + e.build_response() + + +@xhiveframework.whitelist() +def download_errored_template(data_import_name): + data_import = xhiveframework.get_doc("Data Import", data_import_name) + data_import.export_errored_rows() + + +@xhiveframework.whitelist() +def download_import_log(data_import_name): + data_import = xhiveframework.get_doc("Data Import", data_import_name) + data_import.download_import_log() + + +@xhiveframework.whitelist() +def get_import_status(data_import_name): + import_status = {} + + data_import = xhiveframework.get_doc("Data Import", data_import_name) + import_status["status"] = data_import.status + + logs = xhiveframework.get_all( + "Data Import Log", + fields=["count(*) as count", "success"], + filters={"data_import": data_import_name}, + group_by="success", + ) + + total_payload_count = xhiveframework.db.get_value("Data Import", data_import_name, "payload_count") + + for log in logs: + if log.get("success"): + import_status["success"] = log.get("count") + else: + import_status["failed"] = log.get("count") + + import_status["total_records"] = total_payload_count + + return import_status + + +def import_file(doctype, file_path, import_type, submit_after_import=False, console=False): + """ + Import documents in from CSV or XLSX using data import. + + :param doctype: DocType to import + :param file_path: Path to .csv, .xls, or .xlsx file to import + :param import_type: One of "Insert" or "Update" + :param submit_after_import: Whether to submit documents after import + :param console: Set to true if this is to be used from command line. Will print errors or progress to stdout. + """ + + data_import = xhiveframework.new_doc("Data Import") + data_import.submit_after_import = submit_after_import + data_import.import_type = ( + "Insert New Records" if import_type.lower() == "insert" else "Update Existing Records" + ) + + i = Importer(doctype=doctype, file_path=file_path, data_import=data_import, console=console) + i.import_data() + + +def import_doc(path, pre_process=None): + if os.path.isdir(path): + files = [os.path.join(path, f) for f in os.listdir(path)] + else: + files = [path] + + for f in files: + if f.endswith(".json"): + xhiveframework.flags.mute_emails = True + import_file_by_path( + f, data_import=True, force=True, pre_process=pre_process, reset_permissions=True + ) + xhiveframework.flags.mute_emails = False + xhiveframework.db.commit() + else: + raise NotImplementedError("Only .json files can be imported") + + +def export_json(doctype, path, filters=None, or_filters=None, name=None, order_by="creation asc"): + def post_process(out): + # Note on Tree DocTypes: + # The tree structure is maintained in the database via the fields "lft" + # and "rgt". They are automatically set and kept up-to-date. Importing + # them would destroy any existing tree structure. For this reason they + # are not exported as well. + del_keys = ("modified_by", "creation", "owner", "idx", "lft", "rgt") + for doc in out: + for key in del_keys: + if key in doc: + del doc[key] + for v in doc.values(): + if isinstance(v, list): + for child in v: + for key in (*del_keys, "docstatus", "doctype", "modified", "name"): + if key in child: + del child[key] + + out = [] + if name: + out.append(xhiveframework.get_doc(doctype, name).as_dict()) + elif xhiveframework.db.get_value("DocType", doctype, "issingle"): + out.append(xhiveframework.get_doc(doctype).as_dict()) + else: + for doc in xhiveframework.get_all( + doctype, + fields=["name"], + filters=filters, + or_filters=or_filters, + limit_page_length=0, + order_by=order_by, + ): + out.append(xhiveframework.get_doc(doctype, doc.name).as_dict()) + post_process(out) + + dirname = os.path.dirname(path) + if not os.path.exists(dirname): + path = os.path.join("..", path) + + with open(path, "w") as outfile: + outfile.write(xhiveframework.as_json(out, ensure_ascii=False)) + + +def export_csv(doctype, path): + from xhiveframework.core.doctype.data_export.exporter import export_data + + with open(path, "wb") as csvfile: + export_data(doctype=doctype, all_doctypes=True, template=True, with_data=True) + csvfile.write(xhiveframework.response.result.encode("utf-8")) diff --git a/xhiveframework/core/doctype/data_import/data_import_list.js b/xhiveframework/core/doctype/data_import/data_import_list.js new file mode 100644 index 0000000..d3deb14 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/data_import_list.js @@ -0,0 +1,45 @@ +let imports_in_progress = []; + +xhiveframework.listview_settings["Data Import"] = { + onload(listview) { + xhiveframework.realtime.on("data_import_progress", (data) => { + if (!imports_in_progress.includes(data.data_import)) { + imports_in_progress.push(data.data_import); + } + }); + xhiveframework.realtime.on("data_import_refresh", (data) => { + imports_in_progress = imports_in_progress.filter((d) => d !== data.data_import); + listview.refresh(); + }); + }, + get_indicator: function (doc) { + var colors = { + Pending: "orange", + "Not Started": "orange", + "Partial Success": "orange", + Success: "green", + "In Progress": "orange", + Error: "red", + "Timed Out": "orange", + }; + let status = doc.status; + + if (imports_in_progress.includes(doc.name)) { + status = "In Progress"; + } + if (status === "Pending") { + status = "Not Started"; + } + + return [__(status), colors[status], "status,=," + doc.status]; + }, + formatters: { + import_type(value) { + return { + "Insert New Records": __("Insert"), + "Update Existing Records": __("Update"), + }[value]; + }, + }, + hide_name_column: true, +}; diff --git a/xhiveframework/core/doctype/data_import/exporter.py b/xhiveframework/core/doctype/data_import/exporter.py new file mode 100644 index 0000000..7d507d9 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/exporter.py @@ -0,0 +1,249 @@ +# Copyright (c) 2019, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import display_fieldtypes, no_value_fields +from xhiveframework.model import table_fields as table_fieldtypes +from xhiveframework.utils import flt, format_duration, groupby_metric +from xhiveframework.utils.csvutils import build_csv_response +from xhiveframework.utils.xlsxutils import build_xlsx_response + + +class Exporter: + def __init__( + self, + doctype, + export_fields=None, + export_data=False, + export_filters=None, + export_page_length=None, + file_type="CSV", + ): + """ + Exports records of a DocType for use with Importer + :param doctype: Document Type to export + :param export_fields=None: One of 'All', 'Mandatory' or {'DocType': ['field1', 'field2'], 'Child DocType': ['childfield1']} + :param export_data=False: Whether to export data as well + :param export_filters=None: The filters (dict or list) which is used to query the records + :param file_type: One of 'Excel' or 'CSV' + """ + self.doctype = doctype + self.meta = xhiveframework.get_meta(doctype) + self.export_fields = export_fields + self.export_filters = export_filters + self.export_page_length = export_page_length + self.file_type = file_type + + # this will contain the csv content + self.csv_array = [] + + # fields that get exported + self.exportable_fields = self.get_all_exportable_fields() + self.fields = self.serialize_exportable_fields() + self.add_header() + + if export_data: + self.data = self.get_data_to_export() + else: + self.data = [] + self.add_data() + + def get_all_exportable_fields(self): + child_table_fields = [df.fieldname for df in self.meta.fields if df.fieldtype in table_fieldtypes] + + meta = xhiveframework.get_meta(self.doctype) + exportable_fields = xhiveframework._dict({}) + + for key, fieldnames in self.export_fields.items(): + if key == self.doctype: + # parent fields + exportable_fields[key] = self.get_exportable_fields(key, fieldnames) + + elif key in child_table_fields: + # child fields + child_df = meta.get_field(key) + child_doctype = child_df.options + exportable_fields[key] = self.get_exportable_fields(child_doctype, fieldnames) + + return exportable_fields + + def serialize_exportable_fields(self): + fields = [] + for key, exportable_fields in self.exportable_fields.items(): + for _df in exportable_fields: + # make a copy of df dict to avoid reference mutation + if isinstance(_df, xhiveframework.core.doctype.docfield.docfield.DocField): + df = _df.as_dict() + else: + df = _df.copy() + + df.is_child_table_field = key != self.doctype + if df.is_child_table_field: + df.child_table_df = self.meta.get_field(key) + fields.append(df) + return fields + + def get_exportable_fields(self, doctype, fieldnames): + meta = xhiveframework.get_meta(doctype) + + def is_exportable(df): + return df and df.fieldtype not in (display_fieldtypes + no_value_fields) + + # add name field + name_field = xhiveframework._dict( + { + "fieldtype": "Data", + "fieldname": "name", + "label": "ID", + "reqd": 1, + "parent": doctype, + } + ) + + fields = [meta.get_field(fieldname) for fieldname in fieldnames] + fields = [df for df in fields if is_exportable(df)] + + if "name" in fieldnames: + fields = [name_field, *fields] + + return fields or [] + + def get_data_to_export(self): + xhiveframework.permissions.can_export(self.doctype, raise_exception=True) + + table_fields = [f for f in self.exportable_fields if f != self.doctype] + data = self.get_data_as_docs() + + for doc in data: + rows = [] + rows = self.add_data_row(self.doctype, None, doc, rows, 0) + + if table_fields: + # add child table data + for f in table_fields: + for i, child_row in enumerate(doc.get(f, [])): + table_df = self.meta.get_field(f) + child_doctype = table_df.options + rows = self.add_data_row(child_doctype, child_row.parentfield, child_row, rows, i) + + yield from rows + + def add_data_row(self, doctype, parentfield, doc, rows, row_idx): + if len(rows) < row_idx + 1: + rows.append([""] * len(self.fields)) + + row = rows[row_idx] + + for i, df in enumerate(self.fields): + if df.parent == doctype: + if df.is_child_table_field and df.child_table_df.fieldname != parentfield: + continue + value = doc.get(df.fieldname, None) + + if df.fieldtype == "Duration": + value = flt(value or 0) + value = format_duration(value, df.hide_days) + + row[i] = value + return rows + + def get_data_as_docs(self): + def format_column_name(df): + return f"`tab{df.parent}`.`{df.fieldname}`" + + filters = self.export_filters + + if self.meta.is_nested_set(): + order_by = f"`tab{self.doctype}`.`lft` ASC" + else: + order_by = f"`tab{self.doctype}`.`creation` DESC" + + parent_fields = [format_column_name(df) for df in self.fields if df.parent == self.doctype] + parent_data = xhiveframework.db.get_list( + self.doctype, + filters=filters, + fields=["name", *parent_fields], + limit_page_length=self.export_page_length, + order_by=order_by, + as_list=0, + ) + parent_names = [p.name for p in parent_data] + + child_data = {} + for key in self.exportable_fields: + if key == self.doctype: + continue + child_table_df = self.meta.get_field(key) + child_table_doctype = child_table_df.options + child_fields = [ + "name", + "idx", + "parent", + "parentfield", + *list({format_column_name(df) for df in self.fields if df.parent == child_table_doctype}), + ] + data = xhiveframework.get_all( + child_table_doctype, + filters={ + "parent": ("in", parent_names), + "parentfield": child_table_df.fieldname, + "parenttype": self.doctype, + }, + fields=child_fields, + order_by="idx asc", + as_list=0, + ) + child_data[key] = data + + # Group children data by parent name + grouped_children_data = self.group_children_data_by_parent(child_data) + for doc in parent_data: + related_children_docs = grouped_children_data.get(str(doc.name), {}) + yield {**doc, **related_children_docs} + + def add_header(self): + header = [] + for df in self.fields: + is_parent = not df.is_child_table_field + if is_parent: + label = _(df.label or df.fieldname) + else: + label = f"{_(df.label or df.fieldname)} ({_(df.child_table_df.label or df.child_table_df.fieldname)})" + + if label in header: + # this label is already in the header, + # which means two fields with the same label + # add the fieldname to avoid clash + if is_parent: + label = f"{df.fieldname}" + else: + label = f"{df.child_table_df.fieldname}.{df.fieldname}" + + header.append(label) + + self.csv_array.append(header) + + def add_data(self): + self.csv_array += self.data + + def get_csv_array(self): + return self.csv_array + + def get_csv_array_for_export(self): + csv_array = self.csv_array + + if not self.data: + # add 2 empty rows + csv_array += [[]] * 2 + + return csv_array + + def build_response(self): + if self.file_type == "CSV": + build_csv_response(self.get_csv_array_for_export(), _(self.doctype)) + elif self.file_type == "Excel": + build_xlsx_response(self.get_csv_array_for_export(), _(self.doctype)) + + def group_children_data_by_parent(self, children_data: dict[str, list]): + return groupby_metric(children_data, key="parent") diff --git a/xhiveframework/core/doctype/data_import/fixtures/sample_import_file.csv b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file.csv new file mode 100644 index 0000000..a432a45 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file.csv @@ -0,0 +1,5 @@ +Title ,Description ,Number ,Duration,another_number ,ID (Table Field 1),Child Title (Table Field 1),Child Description (Table Field 1),Child 2 Title (Table Field 2),Child 2 Date (Table Field 2),Child 2 Number (Table Field 2),Child Title (Table Field 1 Again),Child Date (Table Field 1 Again),Child Number (Table Field 1 Again),table_field_1_again.child_another_number +Test ,test description ,1,3h,2, ,child title ,child description ,child title ,14-08-2019,4,child title again ,22-09-2020,5,7 + , , ,, , ,child title 2,child description 2,title child ,30-10-2019,5,child title again 2,22-09-2021, , +Test 2,test description 2,1,4d 3h,2, ,child mandatory title , ,title child man , , ,child mandatory again , , , +Test 3,test description 3,4,5d 5h 45m,5, ,child title asdf ,child description asdf ,child title asdf adsf ,15-08-2019,6,child title again asdf ,22-09-2022,9,71 \ No newline at end of file diff --git a/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_for_update.csv b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_for_update.csv new file mode 100644 index 0000000..e48208e --- /dev/null +++ b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_for_update.csv @@ -0,0 +1,2 @@ +Title,Description,Number,another_number,ID (Table Field 1),Child Title (Table Field 1),Child Description (Table Field 1),Child 2 Title (Table Field 2),Child 2 Date (Table Field 2),Child 2 Number (Table Field 2),Child Title (Table Field 1 Again),Child Date (Table Field 1 Again),Child Number (Table Field 1 Again),table_field_1_again.child_another_number +Test 26,test description,1,2,"",child title,child description,child title,14-08-2019,4,child title again,22-09-2020,5,7 diff --git a/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_without_mandatory.csv b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_without_mandatory.csv new file mode 100644 index 0000000..c6bff5c --- /dev/null +++ b/xhiveframework/core/doctype/data_import/fixtures/sample_import_file_without_mandatory.csv @@ -0,0 +1,5 @@ +Title ,Description ,Number ,another_number ,ID (Table Field 1) ,Child Title (Table Field 1) ,Child Description (Table Field 1) ,Child 2 Title (Table Field 2) ,Child 2 Date (Table Field 2) ,Child 2 Number (Table Field 2) ,Child Title (Table Field 1 Again) ,Child Date (Table Field 1 Again) ,Child Number (Table Field 1 Again) ,table_field_1_again.child_another_number +Test 5 ,test description ,1 ,2 ,"" , ,child description ,child title ,14-08-2019 ,4 ,child title again ,22-09-2020 ,5 , 7 + , , , , ,child title 2 ,child description 2 ,title child ,30-10-2019 ,5 , ,22-09-2021 , , + ,test description 2 ,1 ,2 , ,child mandatory title , ,title child man , , ,child mandatory again , , , +Test 4 ,test description 3 ,4 ,5 ,"" ,child title asdf ,child description asdf ,child title asdf adsf ,15-08-2019 ,6 ,child title again asdf ,22-09-2022 ,9 , 71 diff --git a/xhiveframework/core/doctype/data_import/importer.py b/xhiveframework/core/doctype/data_import/importer.py new file mode 100644 index 0000000..1751e08 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/importer.py @@ -0,0 +1,1263 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import os +import re +import timeit +from datetime import date, datetime, time + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.doctype.version.version import get_diff +from xhiveframework.model import no_value_fields +from xhiveframework.utils import cint, cstr, duration_to_seconds, flt, update_progress_bar +from xhiveframework.utils.csvutils import get_csv_content_from_google_sheets, read_csv_content +from xhiveframework.utils.xlsxutils import ( + read_xls_file_from_attached_file, + read_xlsx_file_from_attached_file, +) + +INVALID_VALUES = ("", None) +MAX_ROWS_IN_PREVIEW = 10 +INSERT = "Insert New Records" +UPDATE = "Update Existing Records" +DURATION_PATTERN = re.compile(r"^(?:(\d+d)?((^|\s)\d+h)?((^|\s)\d+m)?((^|\s)\d+s)?)$") + + +class Importer: + def __init__(self, doctype, data_import=None, file_path=None, import_type=None, console=False): + self.doctype = doctype + self.console = console + + self.data_import = data_import + if not self.data_import: + self.data_import = xhiveframework.get_doc(doctype="Data Import") + if import_type: + self.data_import.import_type = import_type + + self.template_options = xhiveframework.parse_json(self.data_import.template_options or "{}") + self.import_type = self.data_import.import_type + + self.import_file = ImportFile( + doctype, + file_path or data_import.google_sheets_url or data_import.import_file, + self.template_options, + self.import_type, + console=self.console, + ) + + def get_data_for_import_preview(self): + out = self.import_file.get_data_for_import_preview() + + out.import_log = xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success"], + filters={"data_import": self.data_import.name}, + order_by="log_index", + limit=10, + ) + + return out + + def before_import(self): + # set user lang for translations + xhiveframework.cache.hdel("lang", xhiveframework.session.user) + xhiveframework.set_user_lang(xhiveframework.session.user) + + # set flags + xhiveframework.flags.in_import = True + xhiveframework.flags.mute_emails = self.data_import.mute_emails + + self.data_import.db_set("template_warnings", "") + + def import_data(self): + self.before_import() + + # parse docs from rows + payloads = self.import_file.get_payloads_for_import() + + # dont import if there are non-ignorable warnings + warnings = self.import_file.get_warnings() + warnings = [w for w in warnings if w.get("type") != "info"] + + if warnings: + if self.console: + self.print_grouped_warnings(warnings) + else: + self.data_import.db_set("template_warnings", json.dumps(warnings)) + return + + # setup import log + import_log = ( + xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success", "log_index"], + filters={"data_import": self.data_import.name}, + order_by="log_index", + ) + or [] + ) + + log_index = 0 + + # Do not remove rows in case of retry after an error or pending data import + if self.data_import.status == "Partial Success" and len(import_log) >= self.data_import.payload_count: + # remove previous failures from import log only in case of retry after partial success + import_log = [log for log in import_log if log.get("success")] + + # get successfully imported rows + imported_rows = [] + for log in import_log: + log = xhiveframework._dict(log) + if log.success or len(import_log) < self.data_import.payload_count: + imported_rows += json.loads(log.row_indexes) + + log_index = log.log_index + + # start import + total_payload_count = len(payloads) + batch_size = xhiveframework.conf.data_import_batch_size or 1000 + + for batch_index, batched_payloads in enumerate(xhiveframework.utils.create_batch(payloads, batch_size)): + for i, payload in enumerate(batched_payloads): + doc = payload.doc + row_indexes = [row.row_number for row in payload.rows] + current_index = (i + 1) + (batch_index * batch_size) + + if set(row_indexes).intersection(set(imported_rows)): + print("Skipping imported rows", row_indexes) + if total_payload_count > 5: + xhiveframework.publish_realtime( + "data_import_progress", + { + "current": current_index, + "total": total_payload_count, + "skipping": True, + "data_import": self.data_import.name, + }, + user=xhiveframework.session.user, + ) + continue + + try: + start = timeit.default_timer() + doc = self.process_doc(doc) + processing_time = timeit.default_timer() - start + eta = self.get_eta(current_index, total_payload_count, processing_time) + + if self.console: + update_progress_bar( + f"Importing {self.doctype}: {total_payload_count} records", + current_index - 1, + total_payload_count, + ) + elif total_payload_count > 5: + xhiveframework.publish_realtime( + "data_import_progress", + { + "current": current_index, + "total": total_payload_count, + "docname": doc.name, + "data_import": self.data_import.name, + "success": True, + "row_indexes": row_indexes, + "eta": eta, + }, + user=xhiveframework.session.user, + ) + + create_import_log( + self.data_import.name, + log_index, + {"success": True, "docname": doc.name, "row_indexes": row_indexes}, + ) + + log_index += 1 + + if not self.data_import.status == "Partial Success": + self.data_import.db_set("status", "Partial Success") + + # commit after every successful import + xhiveframework.db.commit() + + except Exception: + messages = xhiveframework.local.message_log + xhiveframework.clear_messages() + + # rollback if exception + xhiveframework.db.rollback() + + create_import_log( + self.data_import.name, + log_index, + { + "success": False, + "exception": xhiveframework.get_traceback(), + "messages": messages, + "row_indexes": row_indexes, + }, + ) + + log_index += 1 + + # Logs are db inserted directly so will have to be fetched again + import_log = ( + xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success", "log_index"], + filters={"data_import": self.data_import.name}, + order_by="log_index", + ) + or [] + ) + + # set status + failures = [log for log in import_log if not log.get("success")] + if len(failures) == total_payload_count: + status = "Pending" + elif len(failures) > 0: + status = "Partial Success" + else: + status = "Success" + + if self.console: + self.print_import_log(import_log) + else: + self.data_import.db_set("status", status) + + self.after_import() + + return import_log + + def after_import(self): + xhiveframework.flags.in_import = False + xhiveframework.flags.mute_emails = False + + def process_doc(self, doc): + if self.import_type == INSERT: + return self.insert_record(doc) + elif self.import_type == UPDATE: + return self.update_record(doc) + + def insert_record(self, doc): + meta = xhiveframework.get_meta(self.doctype) + new_doc = xhiveframework.new_doc(self.doctype) + new_doc.update(doc) + + if not doc.name and (meta.autoname or "").lower() != "prompt": + # name can only be set directly if autoname is prompt + new_doc.set("name", None) + + new_doc.flags.updater_reference = { + "doctype": self.data_import.doctype, + "docname": self.data_import.name, + "label": _("via Data Import"), + } + + new_doc.insert() + if meta.is_submittable and self.data_import.submit_after_import: + new_doc.submit() + return new_doc + + def update_record(self, doc): + id_field = get_id_field(self.doctype) + existing_doc = xhiveframework.get_doc(self.doctype, doc.get(id_field.fieldname)) + + updated_doc = xhiveframework.get_doc(self.doctype, doc.get(id_field.fieldname)) + + updated_doc.update(doc) + + if get_diff(existing_doc, updated_doc): + # update doc if there are changes + updated_doc.flags.updater_reference = { + "doctype": self.data_import.doctype, + "docname": self.data_import.name, + "label": _("via Data Import"), + } + updated_doc.save() + return updated_doc + else: + # throw if no changes + xhiveframework.throw(_("No changes to update")) + + def get_eta(self, current, total, processing_time): + self.last_eta = getattr(self, "last_eta", 0) + remaining = total - current + eta = processing_time * remaining + if not self.last_eta or eta < self.last_eta: + self.last_eta = eta + return self.last_eta + + def export_errored_rows(self): + from xhiveframework.utils.csvutils import build_csv_response + + if not self.data_import: + return + + import_log = ( + xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success"], + filters={"data_import": self.data_import.name}, + order_by="log_index", + ) + or [] + ) + + failures = [log for log in import_log if not log.get("success")] + row_indexes = [] + for f in failures: + row_indexes.extend(json.loads(f.get("row_indexes", []))) + + # de duplicate + row_indexes = list(set(row_indexes)) + row_indexes.sort() + + header_row = [col.header_title for col in self.import_file.columns] + rows = [header_row] + rows += [row.data for row in self.import_file.data if row.row_number in row_indexes] + + build_csv_response(rows, _(self.doctype)) + + def export_import_log(self): + from xhiveframework.utils.csvutils import build_csv_response + + if not self.data_import: + return + + import_log = xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success", "messages", "exception", "docname"], + filters={"data_import": self.data_import.name}, + order_by="log_index", + ) + + header_row = ["Row Numbers", "Status", "Message", "Exception"] + + rows = [header_row] + + for log in import_log: + row_number = json.loads(log.get("row_indexes"))[0] + status = "Success" if log.get("success") else "Failure" + message = ( + "Successfully Imported {}".format(log.get("docname")) + if log.get("success") + else log.get("messages") + ) + exception = xhiveframework.utils.cstr(log.get("exception", "")) + rows += [[row_number, status, message, exception]] + + build_csv_response(rows, self.doctype) + + def print_import_log(self, import_log): + failed_records = [log for log in import_log if not log.success] + successful_records = [log for log in import_log if log.success] + + if successful_records: + print() + print(f"Successfully imported {len(successful_records)} records out of {len(import_log)}") + + if failed_records: + print(f"Failed to import {len(failed_records)} records") + file_name = f"{self.doctype}_import_on_{xhiveframework.utils.now()}.txt" + print("Check {} for errors".format(os.path.join("sites", file_name))) + text = "" + for w in failed_records: + text += "Row Indexes: {}\n".format(str(w.get("row_indexes", []))) + text += "Messages:\n{}\n".format("\n".join(w.get("messages", []))) + text += "Traceback:\n{}\n\n".format(w.get("exception")) + + with open(file_name, "w") as f: + f.write(text) + + def print_grouped_warnings(self, warnings): + warnings_by_row = {} + other_warnings = [] + for w in warnings: + if w.get("row"): + warnings_by_row.setdefault(w.get("row"), []).append(w) + else: + other_warnings.append(w) + + for row_number, warnings in warnings_by_row.items(): + print(f"Row {row_number}") + for w in warnings: + print(w.get("message")) + + for w in other_warnings: + print(w.get("message")) + + +class ImportFile: + def __init__(self, doctype, file, template_options=None, import_type=None, *, console=False): + self.doctype = doctype + self.template_options = template_options or xhiveframework._dict(column_to_field_map=xhiveframework._dict()) + self.column_to_field_map = self.template_options.column_to_field_map + self.import_type = import_type + self.warnings = [] + self.console = console + + self.file_doc = self.file_path = self.google_sheets_url = None + if isinstance(file, str): + if xhiveframework.db.exists("File", {"file_url": file}): + self.file_doc = xhiveframework.get_doc("File", {"file_url": file}) + elif "docs.google.com/spreadsheets" in file: + self.google_sheets_url = file + elif os.path.exists(file): + self.file_path = file + + if not self.file_doc and not self.file_path and not self.google_sheets_url: + xhiveframework.throw(_("Invalid template file for import")) + + self.raw_data = self.get_data_from_template_file() + self.parse_data_from_template() + + def get_data_from_template_file(self): + content = None + extension = None + + if self.file_doc: + parts = self.file_doc.get_extension() + extension = parts[1] + content = self.file_doc.get_content() + extension = extension.lstrip(".") + + elif self.file_path: + content, extension = self.read_file(self.file_path) + + elif self.google_sheets_url: + content = get_csv_content_from_google_sheets(self.google_sheets_url) + extension = "csv" + + if not content: + xhiveframework.throw(_("Invalid or corrupted content for import")) + + if not extension: + extension = "csv" + + if content: + return self.read_content(content, extension) + + def parse_data_from_template(self): + header = None + data = [] + + for i, row in enumerate(self.raw_data): + if all(v in INVALID_VALUES for v in row): + # empty row + continue + + if not header: + header = Header(i, row, self.doctype, self.raw_data[1:], self.column_to_field_map) + else: + row_obj = Row(i, row, self.doctype, header, self.import_type) + data.append(row_obj) + + self.header = header + self.columns = self.header.columns + self.data = data + + if len(data) < 1: + xhiveframework.throw( + _("Import template should contain a Header and atleast one row."), + title=_("Template Error"), + ) + + def get_data_for_import_preview(self): + """Adds a serial number column as the first column""" + + columns = [xhiveframework._dict({"header_title": "Sr. No", "skip_import": True})] + columns += [col.as_dict() for col in self.columns] + for col in columns: + # only pick useful fields in docfields to minimise the payload + if col.df: + col.df = { + "fieldtype": col.df.fieldtype, + "fieldname": col.df.fieldname, + "label": col.df.label, + "options": col.df.options, + "parent": col.df.parent, + "reqd": col.df.reqd, + "default": col.df.default, + "read_only": col.df.read_only, + } + + data = [[row.row_number, *row.as_list()] for row in self.data] + + warnings = self.get_warnings() + + out = xhiveframework._dict() + out.data = data + out.columns = columns + out.warnings = warnings + total_number_of_rows = len(out.data) + if total_number_of_rows > MAX_ROWS_IN_PREVIEW: + out.data = out.data[:MAX_ROWS_IN_PREVIEW] + out.max_rows_exceeded = True + out.max_rows_in_preview = MAX_ROWS_IN_PREVIEW + out.total_number_of_rows = total_number_of_rows + return out + + def get_payloads_for_import(self): + payloads = [] + # make a copy + data = list(self.data) + while data: + doc, rows, data = self.parse_next_row_for_import(data) + payloads.append(xhiveframework._dict(doc=doc, rows=rows)) + return payloads + + def parse_next_row_for_import(self, data): + """ + Parses rows that make up a doc. A doc maybe built from a single row or multiple rows. + Returns the doc, rows, and data without the rows. + """ + doctypes = self.header.doctypes + + # first row is included by default + first_row = data[0] + rows = [first_row] + + # if there are child doctypes, find the subsequent rows + if len(doctypes) > 1: + # subsequent rows that have blank values in parent columns + # are considered as child rows + parent_column_indexes = self.header.get_column_indexes(self.doctype) + + data_without_first_row = data[1:] + for row in data_without_first_row: + row_values = row.get_values(parent_column_indexes) + # if the row is blank, it's a child row doc + if all(v in INVALID_VALUES for v in row_values): + rows.append(row) + continue + # if we encounter a row which has values in parent columns, + # then it is the next doc + break + + parent_doc = None + for row in rows: + for doctype, table_df in doctypes: + if doctype == self.doctype and not parent_doc: + parent_doc = row.parse_doc(doctype) + + if doctype != self.doctype and table_df: + child_doc = row.parse_doc(doctype, parent_doc, table_df) + if child_doc is None: + continue + parent_doc[table_df.fieldname] = parent_doc.get(table_df.fieldname, []) + parent_doc[table_df.fieldname].append(child_doc) + + doc = parent_doc + + return doc, rows, data[len(rows) :] + + def get_warnings(self): + warnings = [] + + # ImportFile warnings + warnings += self.warnings + + # Column warnings + for col in self.header.columns: + warnings += col.warnings + + # Row warnings + for row in self.data: + warnings += row.warnings + + return warnings + + ###### + + def read_file(self, file_path: str): + extn = os.path.splitext(file_path)[1][1:] + + file_content = None + + if self.console: + file_content = xhiveframework.read_file(file_path, True) + return file_content, extn + + file_name = xhiveframework.db.get_value("File", {"file_url": file_path}) + if file_name: + file = xhiveframework.get_doc("File", file_name) + file_content = file.get_content() + + return file_content, extn + + def read_content(self, content, extension): + error_title = _("Template Error") + if extension not in ("csv", "xlsx", "xls"): + xhiveframework.throw(_("Import template should be of type .csv, .xlsx or .xls"), title=error_title) + + if extension == "csv": + data = read_csv_content(content) + elif extension == "xlsx": + data = read_xlsx_file_from_attached_file(fcontent=content) + elif extension == "xls": + data = read_xls_file_from_attached_file(content) + + return data + + +class Row: + def __init__(self, index, row, doctype, header, import_type): + self.index = index + self.row_number = index + 1 + self.doctype = doctype + self.data = row + self.header = header + self.import_type = import_type + self.warnings = [] + + len_row = len(self.data) + len_columns = len(self.header.columns) + if len_row != len_columns: + less_than_columns = len_row < len_columns + message = ( + "Row has less values than columns" + if less_than_columns + else "Row has more values than columns" + ) + self.warnings.append( + { + "row": self.row_number, + "message": message, + } + ) + + def parse_doc(self, doctype, parent_doc=None, table_df=None): + col_indexes = self.header.get_column_indexes(doctype, table_df) + values = self.get_values(col_indexes) + + if all(v in INVALID_VALUES for v in values): + # if all values are invalid, no need to parse it + return None + + columns = self.header.get_columns(col_indexes) + return self._parse_doc(doctype, columns, values, parent_doc, table_df) + + def _parse_doc(self, doctype, columns, values, parent_doc=None, table_df=None): + doc = xhiveframework._dict() + if self.import_type == INSERT: + # new_doc returns a dict with default values set + doc = xhiveframework.new_doc( + doctype, + parent_doc=parent_doc, + parentfield=table_df.fieldname if table_df else None, + as_dict=True, + ) + + # remove standard fields and __islocal + for key in xhiveframework.model.default_fields + xhiveframework.model.child_table_fields + ("__islocal",): + doc.pop(key, None) + + for col, value in zip(columns, values, strict=False): + df = col.df + if value in INVALID_VALUES: + value = None + + if value is not None: + value = self.validate_value(value, col) + + if value is not None: + doc[df.fieldname] = self.parse_value(value, col) + + is_table = xhiveframework.get_meta(doctype).istable + is_update = self.import_type == UPDATE + if is_table and is_update: + # check if the row already exists + # if yes, fetch the original doc so that it is not updated + # if no, create a new doc + id_field = get_id_field(doctype) + id_value = doc.get(id_field.fieldname) + if id_value and xhiveframework.db.exists(doctype, id_value): + existing_doc = xhiveframework.get_doc(doctype, id_value) + existing_doc.update(doc) + doc = existing_doc + else: + # for table rows being inserted in update + # create a new doc with defaults set + new_doc = xhiveframework.new_doc(doctype, as_dict=True) + new_doc.update(doc) + doc = new_doc + + return doc + + def validate_value(self, value, col): + df = col.df + if df.fieldtype == "Select": + select_options = get_select_options(df) + if select_options and cstr(value) not in select_options: + options_string = ", ".join(xhiveframework.bold(d) for d in select_options) + msg = _("Value must be one of {0}").format(options_string) + self.warnings.append( + { + "row": self.row_number, + "field": df_as_json(df), + "message": msg, + } + ) + return + + elif df.fieldtype == "Link": + exists = self.link_exists(value, df) + if not exists: + msg = _("Value {0} missing for {1}").format(xhiveframework.bold(value), xhiveframework.bold(df.options)) + self.warnings.append( + { + "row": self.row_number, + "field": df_as_json(df), + "message": msg, + } + ) + return + elif df.fieldtype in ["Date", "Datetime"]: + value = self.get_date(value, col) + if isinstance(value, str): + # value was not parsed as datetime object + self.warnings.append( + { + "row": self.row_number, + "col": col.column_number, + "field": df_as_json(df), + "message": _("Value {0} must in {1} format").format( + xhiveframework.bold(value), xhiveframework.bold(get_user_format(col.date_format)) + ), + } + ) + return + elif df.fieldtype == "Duration": + if not DURATION_PATTERN.match(value): + self.warnings.append( + { + "row": self.row_number, + "col": col.column_number, + "field": df_as_json(df), + "message": _("Value {0} must be in the valid duration format: d h m s").format( + xhiveframework.bold(value) + ), + } + ) + + return value + + def link_exists(self, value, df): + return bool(xhiveframework.db.exists(df.options, value, cache=True)) + + def parse_value(self, value, col): + df = col.df + if isinstance(value, datetime | date) and df.fieldtype in ["Date", "Datetime"]: + return value + + value = cstr(value) + + # convert boolean values to 0 or 1 + valid_check_values = ["t", "f", "true", "false", "yes", "no", "y", "n"] + if df.fieldtype == "Check" and value.lower().strip() in valid_check_values: + value = value.lower().strip() + value = 1 if value in ["t", "true", "y", "yes"] else 0 + + if df.fieldtype in ["Int", "Check"]: + value = cint(value) + elif df.fieldtype in ["Float", "Percent", "Currency"]: + value = flt(value) + elif df.fieldtype in ["Date", "Datetime"]: + value = self.get_date(value, col) + elif df.fieldtype == "Duration": + value = duration_to_seconds(value) + + return value + + def get_date(self, value, column): + if isinstance(value, datetime | date): + return value + + date_format = column.date_format + if date_format: + try: + return datetime.strptime(value, date_format) + except ValueError: + # ignore date values that dont match the format + # import will break for these values later + pass + return value + + def get_values(self, indexes): + return [self.data[i] for i in indexes] + + def get(self, index): + return self.data[index] + + def as_list(self): + return self.data + + +class Header(Row): + def __init__(self, index, row, doctype, raw_data, column_to_field_map=None): + self.index = index + self.row_number = index + 1 + self.data = row + self.doctype = doctype + column_to_field_map = column_to_field_map or xhiveframework._dict() + + self.seen = [] + self.columns = [] + + for j, header in enumerate(row): + column_values = [get_item_at_index(r, j) for r in raw_data] + map_to_field = column_to_field_map.get(str(j)) + column = Column(j, header, self.doctype, column_values, map_to_field, self.seen) + self.seen.append(header) + self.columns.append(column) + + doctypes = [] + for col in self.columns: + if not col.df: + continue + if col.df.parent == self.doctype: + doctypes.append((col.df.parent, None)) + else: + doctypes.append((col.df.parent, col.df.child_table_df)) + + self.doctypes = sorted(list(set(doctypes)), key=lambda x: -1 if x[0] == self.doctype else 1) + + def get_column_indexes(self, doctype, tablefield=None): + def is_table_field(df): + if tablefield: + return df.child_table_df.fieldname == tablefield.fieldname + return True + + return [ + col.index + for col in self.columns + if not col.skip_import and col.df and col.df.parent == doctype and is_table_field(col.df) + ] + + def get_columns(self, indexes): + return [self.columns[i] for i in indexes] + + +class Column: + def __init__(self, index, header, doctype, column_values, map_to_field=None, seen=None): + if seen is None: + seen = [] + self.index = index + self.column_number = index + 1 + self.doctype = doctype + self.header_title = header + self.column_values = column_values + self.map_to_field = map_to_field + self.seen = seen + + self.date_format = None + self.df = None + self.skip_import = None + self.warnings = [] + + self.meta = xhiveframework.get_meta(doctype) + self.parse() + self.validate_values() + + def parse(self): + header_title = self.header_title + column_number = str(self.column_number) + skip_import = False + + if self.map_to_field and self.map_to_field != "Don't Import": + df = get_df_for_column_header(self.doctype, self.map_to_field) + if df: + self.warnings.append( + { + "message": _("Mapping column {0} to field {1}").format( + xhiveframework.bold(header_title or "Untitled Column"), xhiveframework.bold(df.label) + ), + "type": "info", + } + ) + else: + self.warnings.append( + { + "message": _("Could not map column {0} to field {1}").format( + column_number, self.map_to_field + ), + "type": "info", + } + ) + else: + df = get_df_for_column_header(self.doctype, header_title) + # df = df_by_labels_and_fieldnames.get(header_title) + + if not df: + skip_import = True + else: + skip_import = False + + if header_title in self.seen: + self.warnings.append( + { + "col": column_number, + "message": _("Skipping Duplicate Column {0}").format(xhiveframework.bold(header_title)), + "type": "info", + } + ) + df = None + skip_import = True + elif self.map_to_field == "Don't Import": + skip_import = True + self.warnings.append( + { + "col": column_number, + "message": _("Skipping column {0}").format(xhiveframework.bold(header_title)), + "type": "info", + } + ) + elif header_title and not df: + self.warnings.append( + { + "col": column_number, + "message": _("Cannot match column {0} with any field").format(xhiveframework.bold(header_title)), + "type": "info", + } + ) + elif not header_title and not df: + self.warnings.append( + {"col": column_number, "message": _("Skipping Untitled Column"), "type": "info"} + ) + + self.df = df + self.skip_import = skip_import + + def guess_date_format_for_column(self): + """Guesses date format for a column by parsing all the values in the column, + getting the date format and then returning the one which has the maximum frequency + """ + + def guess_date_format(d): + if isinstance(d, datetime | date | time): + if self.df.fieldtype == "Date": + return "%Y-%m-%d" + if self.df.fieldtype == "Datetime": + return "%Y-%m-%d %H:%M:%S" + if self.df.fieldtype == "Time": + return "%H:%M:%S" + if isinstance(d, str): + return xhiveframework.utils.guess_date_format(d) + + date_formats = [guess_date_format(d) for d in self.column_values] + date_formats = [d for d in date_formats if d] + if not date_formats: + return + + unique_date_formats = set(date_formats) + max_occurred_date_format = max(unique_date_formats, key=date_formats.count) + + if len(unique_date_formats) > 1: + # fmt: off + message = _("The column {0} has {1} different date formats. Automatically setting {2} as the default format as it is the most common. Please change other values in this column to this format.") + # fmt: on + user_date_format = get_user_format(max_occurred_date_format) + self.warnings.append( + { + "col": self.column_number, + "message": message.format( + xhiveframework.bold(self.header_title), + len(unique_date_formats), + xhiveframework.bold(user_date_format), + ), + "type": "info", + } + ) + + return max_occurred_date_format + + def validate_values(self): + if not self.df: + return + + if self.skip_import: + return + + if not any(self.column_values): + return + + if self.df.fieldtype == "Link": + # find all values that dont exist + values = list({cstr(v) for v in self.column_values if v}) + exists = [cstr(d.name) for d in xhiveframework.get_all(self.df.options, filters={"name": ("in", values)})] + not_exists = list(set(values) - set(exists)) + if not_exists: + missing_values = ", ".join(not_exists) + message = _("The following values do not exist for {0}: {1}") + self.warnings.append( + { + "col": self.column_number, + "message": message.format(self.df.options, missing_values), + "type": "warning", + } + ) + elif self.df.fieldtype in ("Date", "Time", "Datetime"): + # guess date/time format + self.date_format = self.guess_date_format_for_column() + if not self.date_format: + if self.df.fieldtype == "Time": + self.date_format = "%H:%M:%S" + date_format = "HH:mm:ss" + else: + self.date_format = "%Y-%m-%d" + date_format = "yyyy-mm-dd" + + message = _( + "{0} format could not be determined from the values in this column. Defaulting to {1}." + ) + self.warnings.append( + { + "col": self.column_number, + "message": message.format(self.df.fieldtype, date_format), + "type": "info", + } + ) + elif self.df.fieldtype == "Select": + options = get_select_options(self.df) + if options: + values = {cstr(v) for v in self.column_values if v} + invalid = values - set(options) + if invalid: + valid_values = ", ".join(xhiveframework.bold(o) for o in options) + invalid_values = ", ".join(xhiveframework.bold(i) for i in invalid) + message = _("The following values are invalid: {0}. Values must be one of {1}") + self.warnings.append( + { + "col": self.column_number, + "message": message.format(invalid_values, valid_values), + } + ) + + def as_dict(self): + d = xhiveframework._dict() + d.index = self.index + d.column_number = self.column_number + d.doctype = self.doctype + d.header_title = self.header_title + d.map_to_field = self.map_to_field + d.date_format = self.date_format + d.df = self.df + if hasattr(self.df, "is_child_table_field"): + d.is_child_table_field = self.df.is_child_table_field + d.child_table_df = self.df.child_table_df + d.skip_import = self.skip_import + d.warnings = self.warnings + return d + + +def build_fields_dict_for_column_matching(parent_doctype): + """ + Build a dict with various keys to match with column headers and value as docfield + The keys can be label or fieldname + { + 'Customer': df1, + 'customer': df1, + 'Due Date': df2, + 'due_date': df2, + 'Item Code (Sales Invoice Item)': df3, + 'Sales Invoice Item:item_code': df3, + } + """ + + def get_standard_fields(doctype): + meta = xhiveframework.get_meta(doctype) + if meta.istable: + standard_fields = [ + {"label": "Parent", "fieldname": "parent"}, + {"label": "Parent Type", "fieldname": "parenttype"}, + {"label": "Parent Field", "fieldname": "parentfield"}, + {"label": "Row Index", "fieldname": "idx"}, + ] + else: + standard_fields = [ + {"label": "Owner", "fieldname": "owner"}, + {"label": "Document Status", "fieldname": "docstatus", "fieldtype": "Int"}, + ] + + out = [] + for df in standard_fields: + df = xhiveframework._dict(df) + df.parent = doctype + out.append(df) + return out + + parent_meta = xhiveframework.get_meta(parent_doctype) + out = {} + + # doctypes and fieldname if it is a child doctype + doctypes = [(parent_doctype, None)] + [(df.options, df) for df in parent_meta.get_table_fields()] + + for doctype, table_df in doctypes: + translated_table_label = _(table_df.label) if table_df else None + + # name field + name_df = xhiveframework._dict( + { + "fieldtype": "Data", + "fieldname": "name", + "label": "ID", + "reqd": 1, # self.import_type == UPDATE, + "parent": doctype, + } + ) + + if doctype == parent_doctype: + name_headers = ( + "name", # fieldname + "ID", # label + _("ID"), # translated label + ) + else: + name_headers = ( + f"{table_df.fieldname}.name", # fieldname + f"ID ({table_df.label})", # label + "{} ({})".format(_("ID"), translated_table_label), # translated label + ) + + name_df.is_child_table_field = True + name_df.child_table_df = table_df + + for header in name_headers: + out[header] = name_df + + fields = get_standard_fields(doctype) + xhiveframework.get_meta(doctype).fields + for df in fields: + fieldtype = df.fieldtype or "Data" + if fieldtype in no_value_fields: + continue + + label = (df.label or "").strip() + translated_label = _(label) + + if parent_doctype == doctype: + # for parent doctypes keys will be + # Label, fieldname, Label (fieldname) + + for header in (label, translated_label): + # if Label is already set, don't set it again + # in case of duplicate column headers + if header not in out: + out[header] = df + + for header in ( + df.fieldname, + f"{label} ({df.fieldname})", + f"{translated_label} ({df.fieldname})", + ): + out[header] = df + + else: + # for child doctypes keys will be + # Label (Table Field Label) + # table_field.fieldname + + # create a new df object to avoid mutation problems + if isinstance(df, dict): + new_df = xhiveframework._dict(df.copy()) + else: + new_df = df.as_dict() + + new_df.is_child_table_field = True + new_df.child_table_df = table_df + + for header in ( + # fieldname + f"{table_df.fieldname}.{df.fieldname}", + # label + f"{label} ({table_df.label})", + # translated label + f"{translated_label} ({translated_table_label})", + ): + out[header] = new_df + + # if autoname is based on field + # add an entry for "ID (Autoname Field)" + autoname_field = get_autoname_field(parent_doctype) + if autoname_field: + for header in ( + f"ID ({autoname_field.label})", # label + "{} ({})".format(_("ID"), _(autoname_field.label)), # translated label + # ID field should also map to the autoname field + "ID", + _("ID"), + "name", + ): + out[header] = autoname_field + + return out + + +def get_df_for_column_header(doctype, header): + def build_fields_dict_for_doctype(): + return build_fields_dict_for_column_matching(doctype) + + df_by_labels_and_fieldname = xhiveframework.cache.hget( + "data_import_column_header_map", doctype, generator=build_fields_dict_for_doctype + ) + return df_by_labels_and_fieldname.get(header) + + +# utilities + + +def get_id_field(doctype): + autoname_field = get_autoname_field(doctype) + if autoname_field: + return autoname_field + return xhiveframework._dict({"label": "ID", "fieldname": "name", "fieldtype": "Data"}) + + +def get_autoname_field(doctype): + meta = xhiveframework.get_meta(doctype) + if meta.autoname and meta.autoname.startswith("field:"): + fieldname = meta.autoname[len("field:") :] + return meta.get_field(fieldname) + + +def get_item_at_index(_list, i, default=None): + try: + a = _list[i] + except IndexError: + a = default + return a + + +def get_user_format(date_format): + return date_format.replace("%Y", "yyyy").replace("%y", "yy").replace("%m", "mm").replace("%d", "dd") + + +def df_as_json(df): + return { + "fieldname": df.fieldname, + "fieldtype": df.fieldtype, + "label": df.label, + "options": df.options, + "parent": df.parent, + "default": df.default, + } + + +def get_select_options(df): + return [d for d in (df.options or "").split("\n") if d] + + +def create_import_log(data_import, log_index, log_details): + xhiveframework.get_doc( + { + "doctype": "Data Import Log", + "log_index": log_index, + "success": log_details.get("success"), + "data_import": data_import, + "row_indexes": json.dumps(log_details.get("row_indexes")), + "docname": log_details.get("docname"), + "messages": json.dumps(log_details.get("messages", "[]")), + "exception": log_details.get("exception"), + } + ).db_insert() diff --git a/xhiveframework/core/doctype/data_import/patches/__init__.py b/xhiveframework/core/doctype/data_import/patches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/data_import/patches/remove_stale_docfields_from_legacy_version.py b/xhiveframework/core/doctype/data_import/patches/remove_stale_docfields_from_legacy_version.py new file mode 100644 index 0000000..caf3f20 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/patches/remove_stale_docfields_from_legacy_version.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + """Remove stale docfields from legacy version""" + xhiveframework.db.delete("DocField", {"options": "Data Import", "parent": "Data Import Legacy"}) diff --git a/xhiveframework/core/doctype/data_import/test_data_import.py b/xhiveframework/core/doctype/data_import/test_data_import.py new file mode 100644 index 0000000..d6e72fd --- /dev/null +++ b/xhiveframework/core/doctype/data_import/test_data_import.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDataImport(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/data_import/test_exporter.py b/xhiveframework/core/doctype/data_import/test_exporter.py new file mode 100644 index 0000000..64a7b22 --- /dev/null +++ b/xhiveframework/core/doctype/data_import/test_exporter.py @@ -0,0 +1,100 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.data_import.exporter import Exporter +from xhiveframework.core.doctype.data_import.test_importer import create_doctype_if_not_exists +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +doctype_name = "DocType for Export" + + +class TestExporter(XhiveFrameworkTestCase): + def setUp(self): + create_doctype_if_not_exists(doctype_name) + + def test_exports_specified_fields(self): + if not xhiveframework.db.exists(doctype_name, "Test"): + doc = xhiveframework.get_doc( + doctype=doctype_name, + title="Test", + description="Test Description", + table_field_1=[ + {"child_title": "Child Title 1", "child_description": "Child Description 1"}, + {"child_title": "Child Title 2", "child_description": "Child Description 2"}, + ], + table_field_2=[ + {"child_2_title": "Child Title 1", "child_2_description": "Child Description 1"}, + ], + table_field_1_again=[ + { + "child_title": "Child Title 1 Again", + "child_description": "Child Description 1 Again", + }, + ], + ).insert() + else: + doc = xhiveframework.get_doc(doctype_name, "Test") + + e = Exporter( + doctype_name, + export_fields={ + doctype_name: ["title", "description", "number", "another_number"], + "table_field_1": ["name", "child_title", "child_description"], + "table_field_2": ["child_2_date", "child_2_number"], + "table_field_1_again": [ + "child_title", + "child_date", + "child_number", + "child_another_number", + ], + }, + export_data=True, + ) + csv_array = e.get_csv_array() + header_row = csv_array[0] + + self.assertEqual( + header_row, + [ + "Title", + "Description", + "Number", + "another_number", + "ID (Table Field 1)", + "Child Title (Table Field 1)", + "Child Description (Table Field 1)", + "Child 2 Date (Table Field 2)", + "Child 2 Number (Table Field 2)", + "Child Title (Table Field 1 Again)", + "Child Date (Table Field 1 Again)", + "Child Number (Table Field 1 Again)", + "table_field_1_again.child_another_number", + ], + ) + + table_field_1_row_1_name = doc.table_field_1[0].name + table_field_1_row_2_name = doc.table_field_1[1].name + # fmt: off + self.assertEqual( + csv_array[1], + ["Test", "Test Description", 0, 0, table_field_1_row_1_name, "Child Title 1", "Child Description 1", None, 0, "Child Title 1 Again", None, 0, 0] + ) + self.assertEqual( + csv_array[2], + ["", "", "", "", table_field_1_row_2_name, "Child Title 2", "Child Description 2", "", "", "", "", "", ""], + ) + # fmt: on + self.assertEqual(len(csv_array), 3) + + def test_export_csv_response(self): + e = Exporter( + doctype_name, + export_fields={doctype_name: ["title", "description"]}, + export_data=True, + file_type="CSV", + ) + e.build_response() + + self.assertTrue(xhiveframework.response["result"]) + self.assertEqual(xhiveframework.response["doctype"], doctype_name) + self.assertEqual(xhiveframework.response["type"], "csv") diff --git a/xhiveframework/core/doctype/data_import/test_importer.py b/xhiveframework/core/doctype/data_import/test_importer.py new file mode 100644 index 0000000..5751efc --- /dev/null +++ b/xhiveframework/core/doctype/data_import/test_importer.py @@ -0,0 +1,251 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.data_import.importer import Importer +from xhiveframework.tests.test_query_builder import db_type_is, run_only_if +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import format_duration, getdate + +doctype_name = "DocType for Import" + + +class TestImporter(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + create_doctype_if_not_exists( + doctype_name, + ) + + def test_data_import_from_file(self): + import_file = get_import_file("sample_import_file") + data_import = self.get_importer(doctype_name, import_file) + data_import.start_import() + + doc1 = xhiveframework.get_doc(doctype_name, "Test") + doc2 = xhiveframework.get_doc(doctype_name, "Test 2") + doc3 = xhiveframework.get_doc(doctype_name, "Test 3") + + self.assertEqual(doc1.description, "test description") + self.assertEqual(doc1.number, 1) + self.assertEqual(format_duration(doc1.duration), "3h") + + self.assertEqual(doc1.table_field_1[0].child_title, "child title") + self.assertEqual(doc1.table_field_1[0].child_description, "child description") + + self.assertEqual(doc1.table_field_1[1].child_title, "child title 2") + self.assertEqual(doc1.table_field_1[1].child_description, "child description 2") + + self.assertEqual(doc1.table_field_2[1].child_2_title, "title child") + self.assertEqual(doc1.table_field_2[1].child_2_date, getdate("2019-10-30")) + self.assertEqual(doc1.table_field_2[1].child_2_another_number, 5) + + self.assertEqual(doc1.table_field_1_again[0].child_title, "child title again") + self.assertEqual(doc1.table_field_1_again[1].child_title, "child title again 2") + self.assertEqual(doc1.table_field_1_again[1].child_date, getdate("2021-09-22")) + + self.assertEqual(doc2.description, "test description 2") + self.assertEqual(format_duration(doc2.duration), "4d 3h") + + self.assertEqual(doc3.another_number, 5) + self.assertEqual(format_duration(doc3.duration), "5d 5h 45m") + + def test_data_import_preview(self): + import_file = get_import_file("sample_import_file") + data_import = self.get_importer(doctype_name, import_file) + preview = data_import.get_preview_from_template() + + self.assertEqual(len(preview.data), 4) + self.assertEqual(len(preview.columns), 16) + + # ignored on postgres because myisam doesn't exist on pg + @run_only_if(db_type_is.MARIADB) + def test_data_import_without_mandatory_values(self): + import_file = get_import_file("sample_import_file_without_mandatory") + data_import = self.get_importer(doctype_name, import_file) + xhiveframework.clear_messages() + data_import.start_import() + data_import.reload() + + import_log = xhiveframework.get_all( + "Data Import Log", + fields=["row_indexes", "success", "messages", "exception", "docname"], + filters={"data_import": data_import.name}, + order_by="log_index", + ) + + self.assertEqual(xhiveframework.parse_json(import_log[0]["row_indexes"]), [2, 3]) + expected_error = ( + "Error: Child 1 of DocType for Import Row #1: Value missing for: Child Title" + ) + self.assertEqual( + xhiveframework.parse_json(xhiveframework.parse_json(import_log[0]["messages"])[0])["message"], expected_error + ) + expected_error = ( + "Error: Child 1 of DocType for Import Row #2: Value missing for: Child Title" + ) + self.assertEqual( + xhiveframework.parse_json(xhiveframework.parse_json(import_log[0]["messages"])[1])["message"], expected_error + ) + + self.assertEqual(xhiveframework.parse_json(import_log[1]["row_indexes"]), [4]) + self.assertEqual( + xhiveframework.parse_json(xhiveframework.parse_json(import_log[1]["messages"])[0])["message"], + "Title is required", + ) + + def test_data_import_update(self): + existing_doc = xhiveframework.get_doc( + doctype=doctype_name, + title=xhiveframework.generate_hash(length=8), + table_field_1=[{"child_title": "child title to update"}], + ) + existing_doc.save() + xhiveframework.db.commit() + + import_file = get_import_file("sample_import_file_for_update") + data_import = self.get_importer(doctype_name, import_file, update=True) + i = Importer(data_import.reference_doctype, data_import=data_import) + + # update child table id in template date + i.import_file.raw_data[1][4] = existing_doc.table_field_1[0].name + + # uppercase to check if autoname field isn't replaced in mariadb + if xhiveframework.db.db_type == "mariadb": + i.import_file.raw_data[1][0] = existing_doc.name.upper() + else: + i.import_file.raw_data[1][0] = existing_doc.name + + i.import_file.parse_data_from_template() + i.import_data() + + updated_doc = xhiveframework.get_doc(doctype_name, existing_doc.name) + self.assertEqual(existing_doc.title, updated_doc.title) + self.assertEqual(updated_doc.description, "test description") + self.assertEqual(updated_doc.table_field_1[0].child_title, "child title") + self.assertEqual(updated_doc.table_field_1[0].name, existing_doc.table_field_1[0].name) + self.assertEqual(updated_doc.table_field_1[0].child_description, "child description") + self.assertEqual(updated_doc.table_field_1_again[0].child_title, "child title again") + + def get_importer(self, doctype, import_file, update=False): + data_import = xhiveframework.new_doc("Data Import") + data_import.import_type = "Insert New Records" if not update else "Update Existing Records" + data_import.reference_doctype = doctype + data_import.import_file = import_file.file_url + data_import.insert() + # Commit so that the first import failure does not rollback the Data Import insert. + xhiveframework.db.commit() + + return data_import + + +def create_doctype_if_not_exists(doctype_name, force=False): + if force: + xhiveframework.delete_doc_if_exists("DocType", doctype_name) + xhiveframework.delete_doc_if_exists("DocType", "Child 1 of " + doctype_name) + xhiveframework.delete_doc_if_exists("DocType", "Child 2 of " + doctype_name) + + if xhiveframework.db.exists("DocType", doctype_name): + return + + # Child Table 1 + table_1_name = "Child 1 of " + doctype_name + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": table_1_name, + "module": "Custom", + "custom": 1, + "istable": 1, + "fields": [ + {"label": "Child Title", "fieldname": "child_title", "reqd": 1, "fieldtype": "Data"}, + {"label": "Child Description", "fieldname": "child_description", "fieldtype": "Small Text"}, + {"label": "Child Date", "fieldname": "child_date", "fieldtype": "Date"}, + {"label": "Child Number", "fieldname": "child_number", "fieldtype": "Int"}, + {"label": "Child Number", "fieldname": "child_another_number", "fieldtype": "Int"}, + ], + } + ).insert() + + # Child Table 2 + table_2_name = "Child 2 of " + doctype_name + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": table_2_name, + "module": "Custom", + "custom": 1, + "istable": 1, + "fields": [ + {"label": "Child 2 Title", "fieldname": "child_2_title", "reqd": 1, "fieldtype": "Data"}, + { + "label": "Child 2 Description", + "fieldname": "child_2_description", + "fieldtype": "Small Text", + }, + {"label": "Child 2 Date", "fieldname": "child_2_date", "fieldtype": "Date"}, + {"label": "Child 2 Number", "fieldname": "child_2_number", "fieldtype": "Int"}, + {"label": "Child 2 Number", "fieldname": "child_2_another_number", "fieldtype": "Int"}, + ], + } + ).insert() + + # Main Table + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": doctype_name, + "module": "Custom", + "custom": 1, + "autoname": "field:title", + "fields": [ + {"label": "Title", "fieldname": "title", "reqd": 1, "fieldtype": "Data"}, + {"label": "Description", "fieldname": "description", "fieldtype": "Small Text"}, + {"label": "Date", "fieldname": "date", "fieldtype": "Date"}, + {"label": "Duration", "fieldname": "duration", "fieldtype": "Duration"}, + {"label": "Number", "fieldname": "number", "fieldtype": "Int"}, + {"label": "Number", "fieldname": "another_number", "fieldtype": "Int"}, + { + "label": "Table Field 1", + "fieldname": "table_field_1", + "fieldtype": "Table", + "options": table_1_name, + }, + { + "label": "Table Field 2", + "fieldname": "table_field_2", + "fieldtype": "Table", + "options": table_2_name, + }, + { + "label": "Table Field 1 Again", + "fieldname": "table_field_1_again", + "fieldtype": "Table", + "options": table_1_name, + }, + ], + "permissions": [{"role": "System Manager"}], + } + ).insert() + + +def get_import_file(csv_file_name, force=False): + file_name = csv_file_name + ".csv" + _file = xhiveframework.db.exists("File", {"file_name": file_name}) + if force and _file: + xhiveframework.delete_doc_if_exists("File", _file) + + if xhiveframework.db.exists("File", {"file_name": file_name}): + f = xhiveframework.get_doc("File", {"file_name": file_name}) + else: + full_path = get_csv_file_path(file_name) + f = xhiveframework.get_doc( + doctype="File", content=xhiveframework.read_file(full_path), file_name=file_name, is_private=1 + ) + f.save(ignore_permissions=True) + + return f + + +def get_csv_file_path(file_name): + return xhiveframework.get_app_path("xhiveframework", "core", "doctype", "data_import", "fixtures", file_name) diff --git a/xhiveframework/core/doctype/data_import_log/__init__.py b/xhiveframework/core/doctype/data_import_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/data_import_log/data_import_log.js b/xhiveframework/core/doctype/data_import_log/data_import_log.js new file mode 100644 index 0000000..ff4ae63 --- /dev/null +++ b/xhiveframework/core/doctype/data_import_log/data_import_log.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Data Import Log", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/data_import_log/data_import_log.json b/xhiveframework/core/doctype/data_import_log/data_import_log.json new file mode 100644 index 0000000..b1d991f --- /dev/null +++ b/xhiveframework/core/doctype/data_import_log/data_import_log.json @@ -0,0 +1,84 @@ +{ + "actions": [], + "creation": "2021-12-25 16:12:20.205889", + "doctype": "DocType", + "editable_grid": 1, + "engine": "MyISAM", + "field_order": [ + "data_import", + "row_indexes", + "success", + "docname", + "messages", + "exception", + "log_index" + ], + "fields": [ + { + "fieldname": "data_import", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Data Import", + "options": "Data Import" + }, + { + "fieldname": "docname", + "fieldtype": "Data", + "label": "Reference Name" + }, + { + "fieldname": "exception", + "fieldtype": "Text", + "label": "Exception" + }, + { + "fieldname": "row_indexes", + "fieldtype": "Code", + "label": "Row Indexes", + "options": "JSON" + }, + { + "default": "0", + "fieldname": "success", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Success" + }, + { + "fieldname": "log_index", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Log Index" + }, + { + "fieldname": "messages", + "fieldtype": "Code", + "label": "Messages", + "options": "JSON" + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-12-29 11:19:19.646076", + "modified_by": "Administrator", + "module": "Core", + "name": "Data Import Log", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/data_import_log/data_import_log.py b/xhiveframework/core/doctype/data_import_log/data_import_log.py new file mode 100644 index 0000000..2a1d079 --- /dev/null +++ b/xhiveframework/core/doctype/data_import_log/data_import_log.py @@ -0,0 +1,25 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DataImportLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data_import: DF.Link | None + docname: DF.Data | None + exception: DF.Text | None + log_index: DF.Int + messages: DF.Code | None + row_indexes: DF.Code | None + success: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/data_import_log/test_data_import_log.py b/xhiveframework/core/doctype/data_import_log/test_data_import_log.py new file mode 100644 index 0000000..e7f82d3 --- /dev/null +++ b/xhiveframework/core/doctype/data_import_log/test_data_import_log.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDataImportLog(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/defaultvalue/README.md b/xhiveframework/core/doctype/defaultvalue/README.md new file mode 100644 index 0000000..327fb71 --- /dev/null +++ b/xhiveframework/core/doctype/defaultvalue/README.md @@ -0,0 +1 @@ +Child table for **User** and **Role** where default keys and values are stored. They can also be created from the **User Properties** page. \ No newline at end of file diff --git a/xhiveframework/core/doctype/defaultvalue/__init__.py b/xhiveframework/core/doctype/defaultvalue/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/defaultvalue/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/defaultvalue/defaultvalue.json b/xhiveframework/core/doctype/defaultvalue/defaultvalue.json new file mode 100644 index 0000000..22e2583 --- /dev/null +++ b/xhiveframework/core/doctype/defaultvalue/defaultvalue.json @@ -0,0 +1,47 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:32", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "defkey", + "defvalue" + ], + "fields": [ + { + "fieldname": "defkey", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "oldfieldname": "defkey", + "oldfieldtype": "Data", + "print_width": "200px", + "reqd": 1, + "width": "200px" + }, + { + "fieldname": "defvalue", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Value", + "oldfieldname": "defvalue", + "oldfieldtype": "Text", + "print_width": "200px", + "width": "200px" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:54.832785", + "modified_by": "Administrator", + "module": "Core", + "name": "DefaultValue", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/defaultvalue/defaultvalue.py b/xhiveframework/core/doctype/defaultvalue/defaultvalue.py new file mode 100644 index 0000000..1af509a --- /dev/null +++ b/xhiveframework/core/doctype/defaultvalue/defaultvalue.py @@ -0,0 +1,39 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class DefaultValue(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + defkey: DF.Data + defvalue: DF.Text | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass + + +def on_doctype_update(): + """Create indexes for `tabDefaultValue` on `(parent, defkey)`""" + xhiveframework.db.commit() + xhiveframework.db.add_index( + doctype="DefaultValue", + fields=["parent", "defkey"], + index_name="defaultvalue_parent_defkey_index", + ) + + xhiveframework.db.add_index( + doctype="DefaultValue", + fields=["parent", "parenttype"], + index_name="defaultvalue_parent_parenttype_index", + ) diff --git a/xhiveframework/core/doctype/deleted_document/__init__.py b/xhiveframework/core/doctype/deleted_document/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/deleted_document/deleted_document.js b/xhiveframework/core/doctype/deleted_document/deleted_document.js new file mode 100644 index 0000000..cbb420b --- /dev/null +++ b/xhiveframework/core/doctype/deleted_document/deleted_document.js @@ -0,0 +1,22 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Deleted Document", { + refresh: function (frm) { + if (frm.doc.restored) { + frm.add_custom_button(__("Open"), function () { + xhiveframework.set_route("Form", frm.doc.deleted_doctype, frm.doc.new_name); + }); + } else { + frm.add_custom_button(__("Restore"), function () { + xhiveframework.call({ + method: "xhiveframework.core.doctype.deleted_document.deleted_document.restore", + args: { name: frm.doc.name }, + callback: function (r) { + frm.reload_doc(); + }, + }); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/deleted_document/deleted_document.json b/xhiveframework/core/doctype/deleted_document/deleted_document.json new file mode 100644 index 0000000..6b95a52 --- /dev/null +++ b/xhiveframework/core/doctype/deleted_document/deleted_document.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "creation": "2016-12-29 12:59:48.638970", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "deleted_name", + "deleted_doctype", + "column_break_3", + "restored", + "new_name", + "section_break_6", + "data" + ], + "fields": [ + { + "fieldname": "deleted_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Deleted Name", + "read_only": 1 + }, + { + "fieldname": "deleted_doctype", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Deleted DocType", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "restored", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Restored", + "read_only": 1 + }, + { + "fieldname": "new_name", + "fieldtype": "Read Only", + "label": "New Name" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "data", + "fieldtype": "Code", + "label": "Data", + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2022-06-13 05:50:58.314908", + "modified_by": "Administrator", + "module": "Core", + "name": "Deleted Document", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "export": 1, + "read": 1, + "role": "System Manager" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "deleted_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/deleted_document/deleted_document.py b/xhiveframework/core/doctype/deleted_document/deleted_document.py new file mode 100644 index 0000000..0e839ea --- /dev/null +++ b/xhiveframework/core/doctype/deleted_document/deleted_document.py @@ -0,0 +1,92 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.doctype.bulk_update.bulk_update import show_progress +from xhiveframework.model.document import Document +from xhiveframework.model.workflow import get_workflow_name + + +class DeletedDocument(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data: DF.Code | None + deleted_doctype: DF.Data | None + deleted_name: DF.Data | None + new_name: DF.ReadOnly | None + restored: DF.Check + # end: auto-generated types + pass + + @staticmethod + def clear_old_logs(days=180): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Deleted Document") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + +@xhiveframework.whitelist() +def restore(name, alert=True): + deleted = xhiveframework.get_doc("Deleted Document", name) + + if deleted.restored: + xhiveframework.throw(_("Document {0} Already Restored").format(name), exc=xhiveframework.DocumentAlreadyRestored) + + doc = xhiveframework.get_doc(json.loads(deleted.data)) + + try: + doc.insert() + except xhiveframework.DocstatusTransitionError: + xhiveframework.msgprint(_("Cancelled Document restored as Draft")) + doc.docstatus = 0 + active_workflow = get_workflow_name(doc.doctype) + if active_workflow: + workflow_state_fieldname = xhiveframework.get_value("Workflow", active_workflow, "workflow_state_field") + if doc.get(workflow_state_fieldname): + doc.set(workflow_state_fieldname, None) + doc.insert() + + doc.add_comment("Edit", _("restored {0} as {1}").format(deleted.deleted_name, doc.name)) + + deleted.new_name = doc.name + deleted.restored = 1 + deleted.db_update() + + if alert: + xhiveframework.msgprint(_("Document Restored")) + + +@xhiveframework.whitelist() +def bulk_restore(docnames): + docnames = xhiveframework.parse_json(docnames) + message = _("Restoring Deleted Document") + restored, invalid, failed = [], [], [] + + for i, d in enumerate(docnames): + try: + show_progress(docnames, message, i + 1, d) + restore(d, alert=False) + xhiveframework.db.commit() + restored.append(d) + + except xhiveframework.DocumentAlreadyRestored: + xhiveframework.clear_last_message() + invalid.append(d) + + except Exception: + xhiveframework.clear_last_message() + failed.append(d) + xhiveframework.db.rollback() + + return {"restored": restored, "invalid": invalid, "failed": failed} diff --git a/xhiveframework/core/doctype/deleted_document/deleted_document_list.js b/xhiveframework/core/doctype/deleted_document/deleted_document_list.js new file mode 100644 index 0000000..df926d2 --- /dev/null +++ b/xhiveframework/core/doctype/deleted_document/deleted_document_list.js @@ -0,0 +1,50 @@ +xhiveframework.listview_settings["Deleted Document"] = { + onload: function (doclist) { + const action = () => { + const selected_docs = doclist.get_checked_items(); + if (selected_docs.length > 0) { + let docnames = selected_docs.map((doc) => doc.name); + xhiveframework.call({ + method: "xhiveframework.core.doctype.deleted_document.deleted_document.bulk_restore", + args: { docnames }, + callback: function (r) { + if (r.message) { + let body = (docnames) => { + const html = docnames.map((docname) => { + return `
  5. ${docname}
  6. `; + }); + return "
      " + html.join(""); + }; + + let message = (title, docnames) => { + return docnames.length > 0 ? title + body(docnames) + "
    " : ""; + }; + + const { restored, invalid, failed } = r.message; + const restored_summary = message( + __("Documents restored successfully"), + restored + ); + const invalid_summary = message( + __("Documents that were already restored"), + invalid + ); + const failed_summary = message( + __("Documents that failed to restore"), + failed + ); + const summary = restored_summary + invalid_summary + failed_summary; + + xhiveframework.msgprint(summary, __("Document Restoration Summary"), true); + + if (restored.length > 0) { + doclist.refresh(); + } + } + }, + }); + } + }; + doclist.page.add_actions_menu_item(__("Restore"), action, false); + }, +}; diff --git a/xhiveframework/core/doctype/deleted_document/test_deleted_document.py b/xhiveframework/core/doctype/deleted_document/test_deleted_document.py new file mode 100644 index 0000000..e672035 --- /dev/null +++ b/xhiveframework/core/doctype/deleted_document/test_deleted_document.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Deleted Document') + + +class TestDeletedDocument(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/docfield/README.md b/xhiveframework/core/doctype/docfield/README.md new file mode 100644 index 0000000..8c9caca --- /dev/null +++ b/xhiveframework/core/doctype/docfield/README.md @@ -0,0 +1,3 @@ +Represents a field of a DocType analogous to a table column in the database. DocFields represent the properties both the model and the view and hence may or may not have database columns associated (for example, Section Break does not have any column associated.) + +See Standard Field Types \ No newline at end of file diff --git a/xhiveframework/core/doctype/docfield/__init__.py b/xhiveframework/core/doctype/docfield/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/docfield/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/docfield/docfield.json b/xhiveframework/core/doctype/docfield/docfield.json new file mode 100644 index 0000000..f94a51a --- /dev/null +++ b/xhiveframework/core/doctype/docfield/docfield.json @@ -0,0 +1,580 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:33", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label_and_type", + "label", + "fieldtype", + "fieldname", + "precision", + "length", + "non_negative", + "hide_days", + "hide_seconds", + "reqd", + "is_virtual", + "search_index", + "column_break_18", + "options", + "sort_options", + "show_dashboard", + "defaults_section", + "default", + "column_break_6", + "fetch_from", + "fetch_if_empty", + "visibility_section", + "hidden", + "bold", + "allow_in_quick_entry", + "translatable", + "print_hide", + "print_hide_if_no_value", + "report_hide", + "column_break_28", + "depends_on", + "collapsible", + "collapsible_depends_on", + "hide_border", + "list__search_settings_section", + "in_list_view", + "in_standard_filter", + "in_preview", + "column_break_35", + "in_filter", + "in_global_search", + "permissions", + "read_only", + "allow_on_submit", + "ignore_user_permissions", + "allow_bulk_edit", + "column_break_13", + "permlevel", + "ignore_xss_filter", + "constraints_section", + "unique", + "no_copy", + "set_only_once", + "remember_last_selected_value", + "column_break_38", + "mandatory_depends_on", + "read_only_depends_on", + "display", + "print_width", + "width", + "max_height", + "columns", + "column_break_22", + "description", + "documentation_url", + "oldfieldname", + "oldfieldtype" + ], + "fields": [ + { + "fieldname": "label_and_type", + "fieldtype": "Section Break" + }, + { + "bold": 1, + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "oldfieldname": "label", + "oldfieldtype": "Data", + "print_width": "163", + "search_index": 1, + "width": "163" + }, + { + "bold": 1, + "default": "Data", + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nJSON\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nPhone\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime", + "reqd": 1, + "search_index": 1, + "sort_options": 1 + }, + { + "bold": 1, + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "search_index": 1 + }, + { + "default": "0", + "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", + "fieldname": "reqd", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Mandatory", + "oldfieldname": "reqd", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", + "description": "Set non-standard precision for a Float or Currency field", + "fieldname": "precision", + "fieldtype": "Select", + "label": "Precision", + "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9", + "print_hide": 1 + }, + { + "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", + "fieldname": "length", + "fieldtype": "Int", + "label": "Length" + }, + { + "default": "0", + "fieldname": "search_index", + "fieldtype": "Check", + "label": "Index", + "oldfieldname": "search_index", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "depends_on": "eval:!doc.is_virtual", + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View", + "print_width": "70px", + "width": "70px" + }, + { + "default": "0", + "fieldname": "in_standard_filter", + "fieldtype": "Check", + "label": "In List Filter" + }, + { + "default": "0", + "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", + "fieldname": "in_global_search", + "fieldtype": "Check", + "label": "In Global Search" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", + "fieldname": "in_preview", + "fieldtype": "Check", + "label": "In Preview" + }, + { + "default": "0", + "fieldname": "allow_in_quick_entry", + "fieldtype": "Check", + "label": "Allow in Quick Entry" + }, + { + "default": "0", + "fieldname": "bold", + "fieldtype": "Check", + "label": "Bold" + }, + { + "default": "0", + "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", + "fieldname": "translatable", + "fieldtype": "Check", + "label": "Translatable" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype===\"Section Break\"", + "fieldname": "collapsible", + "fieldtype": "Check", + "label": "Collapsible", + "length": 255 + }, + { + "depends_on": "eval:doc.fieldtype==\"Section Break\" && doc.collapsible", + "fieldname": "collapsible_depends_on", + "fieldtype": "Code", + "label": "Collapsible Depends On (JS)", + "max_height": "3rem", + "options": "JS" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", + "fieldname": "options", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text" + }, + { + "fieldname": "default", + "fieldtype": "Small Text", + "label": "Default", + "max_height": "3rem", + "oldfieldname": "default", + "oldfieldtype": "Text" + }, + { + "fieldname": "fetch_from", + "fieldtype": "Small Text", + "label": "Fetch From" + }, + { + "default": "0", + "description": "If unchecked, the value will always be re-fetched on save.", + "fieldname": "fetch_if_empty", + "fieldtype": "Check", + "label": "Fetch on Save if Empty" + }, + { + "fieldname": "permissions", + "fieldtype": "Section Break", + "label": "Permissions" + }, + { + "fieldname": "depends_on", + "fieldtype": "Code", + "label": "Display Depends On (JS)", + "length": 255, + "max_height": "3rem", + "oldfieldname": "depends_on", + "oldfieldtype": "Data", + "options": "JS" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden", + "oldfieldname": "hidden", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "unique", + "fieldtype": "Check", + "label": "Unique" + }, + { + "default": "0", + "fieldname": "set_only_once", + "fieldtype": "Check", + "label": "Set only once" + }, + { + "default": "0", + "depends_on": "eval: doc.fieldtype == \"Table\"", + "fieldname": "allow_bulk_edit", + "fieldtype": "Check", + "label": "Allow Bulk Edit" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Section Break', 'Column Break', 'Tab Break'], doc.fieldtype)", + "fieldname": "permlevel", + "fieldtype": "Int", + "label": "Perm Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "ignore_user_permissions", + "fieldtype": "Check", + "label": "Ignore User Permissions" + }, + { + "default": "0", + "depends_on": "eval: parent.is_submittable", + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "depends_on": "eval:(doc.fieldtype == 'Link')", + "fieldname": "remember_last_selected_value", + "fieldtype": "Check", + "label": "Remember Last Selected Value" + }, + { + "default": "0", + "description": "Don't encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", + "fieldname": "ignore_xss_filter", + "fieldtype": "Check", + "label": "Ignore XSS Filter" + }, + { + "fieldname": "display", + "fieldtype": "Section Break", + "label": "Display" + }, + { + "default": "0", + "fieldname": "in_filter", + "fieldtype": "Check", + "label": "In Filter", + "oldfieldname": "in_filter", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy", + "oldfieldname": "no_copy", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "print_hide", + "fieldtype": "Check", + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", + "fieldname": "print_hide_if_no_value", + "fieldtype": "Check", + "label": "Print Hide If No Value" + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "label": "Print Width", + "length": 10 + }, + { + "fieldname": "width", + "fieldtype": "Data", + "label": "Width", + "length": 10, + "oldfieldname": "width", + "oldfieldtype": "Data", + "print_width": "50px", + "width": "50px" + }, + { + "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", + "fieldname": "columns", + "fieldtype": "Int", + "label": "Columns" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "fieldname": "oldfieldname", + "fieldtype": "Data", + "hidden": 1, + "oldfieldname": "oldfieldname", + "oldfieldtype": "Data" + }, + { + "fieldname": "oldfieldtype", + "fieldtype": "Data", + "hidden": 1, + "oldfieldname": "oldfieldtype", + "oldfieldtype": "Data" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On (JS)", + "max_height": "3rem", + "options": "JS" + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On (JS)", + "max_height": "3rem", + "options": "JS" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_days", + "fieldtype": "Check", + "label": "Hide Days" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_seconds", + "fieldtype": "Check", + "label": "Hide Seconds" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Section Break'", + "fieldname": "hide_border", + "fieldtype": "Check", + "label": "Hide Border" + }, + { + "default": "0", + "depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)", + "fieldname": "non_negative", + "fieldtype": "Check", + "label": "Non Negative" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "defaults_section", + "fieldtype": "Section Break", + "label": "Defaults", + "max_height": "2rem" + }, + { + "fieldname": "visibility_section", + "fieldtype": "Section Break", + "label": "Visibility" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "constraints_section", + "fieldtype": "Section Break", + "label": "Constraints" + }, + { + "fieldname": "max_height", + "fieldtype": "Data", + "label": "Max Height", + "length": 10 + }, + { + "fieldname": "list__search_settings_section", + "fieldtype": "Section Break", + "label": "List / Search Settings" + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype===\"Tab Break\"", + "fieldname": "show_dashboard", + "fieldtype": "Check", + "label": "Show Dashboard" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Virtual" + }, + { + "depends_on": "eval:!in_list([\"Tab Break\", \"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", + "fieldname": "documentation_url", + "fieldtype": "Data", + "label": "Documentation URL", + "options": "URL" + }, + { + "default": "0", + "depends_on": "eval: doc.fieldtype === 'Select'", + "fieldname": "sort_options", + "fieldtype": "Check", + "label": "Sort Options" + } + ], + "idx": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-02-01 15:55:44.007917", + "modified_by": "Administrator", + "module": "Core", + "name": "DocField", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/docfield/docfield.py b/xhiveframework/core/doctype/docfield/docfield.py new file mode 100644 index 0000000..cdbc45d --- /dev/null +++ b/xhiveframework/core/doctype/docfield/docfield.py @@ -0,0 +1,153 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class DocField(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow_bulk_edit: DF.Check + allow_in_quick_entry: DF.Check + allow_on_submit: DF.Check + bold: DF.Check + collapsible: DF.Check + collapsible_depends_on: DF.Code | None + columns: DF.Int + default: DF.SmallText | None + depends_on: DF.Code | None + description: DF.SmallText | None + documentation_url: DF.Data | None + fetch_from: DF.SmallText | None + fetch_if_empty: DF.Check + fieldname: DF.Data | None + fieldtype: DF.Literal[ + "Autocomplete", + "Attach", + "Attach Image", + "Barcode", + "Button", + "Check", + "Code", + "Color", + "Column Break", + "Currency", + "Data", + "Date", + "Datetime", + "Duration", + "Dynamic Link", + "Float", + "Fold", + "Geolocation", + "Heading", + "HTML", + "HTML Editor", + "Icon", + "Image", + "Int", + "JSON", + "Link", + "Long Text", + "Markdown Editor", + "Password", + "Percent", + "Phone", + "Read Only", + "Rating", + "Section Break", + "Select", + "Signature", + "Small Text", + "Tab Break", + "Table", + "Table MultiSelect", + "Text", + "Text Editor", + "Time", + ] + hidden: DF.Check + hide_border: DF.Check + hide_days: DF.Check + hide_seconds: DF.Check + ignore_user_permissions: DF.Check + ignore_xss_filter: DF.Check + in_filter: DF.Check + in_global_search: DF.Check + in_list_view: DF.Check + in_preview: DF.Check + in_standard_filter: DF.Check + is_virtual: DF.Check + label: DF.Data | None + length: DF.Int + mandatory_depends_on: DF.Code | None + max_height: DF.Data | None + no_copy: DF.Check + non_negative: DF.Check + oldfieldname: DF.Data | None + oldfieldtype: DF.Data | None + options: DF.SmallText | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + permlevel: DF.Int + precision: DF.Literal["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + print_hide: DF.Check + print_hide_if_no_value: DF.Check + print_width: DF.Data | None + read_only: DF.Check + read_only_depends_on: DF.Code | None + remember_last_selected_value: DF.Check + report_hide: DF.Check + reqd: DF.Check + search_index: DF.Check + set_only_once: DF.Check + show_dashboard: DF.Check + sort_options: DF.Check + translatable: DF.Check + unique: DF.Check + width: DF.Data | None + # end: auto-generated types + + def get_link_doctype(self): + """Returns the Link doctype for the docfield (if applicable) + if fieldtype is Link: Returns "options" + if fieldtype is Table MultiSelect: Returns "options" of the Link field in the Child Table + """ + if self.fieldtype == "Link": + return self.options + + if self.fieldtype == "Table MultiSelect": + table_doctype = self.options + + return xhiveframework.db.get_value( + "DocField", + { + "fieldtype": "Link", + "parenttype": "DocType", + "parent": table_doctype, + "in_list_view": 1, + }, + "options", + ) + + def get_select_options(self): + if self.fieldtype == "Select": + options = self.options or "" + return [d for d in options.split("\n") if d] + + def __repr__(self): + unsaved = "unsaved" if not self.name else "" + doctype = self.__class__.__name__ + + docstatus = f" docstatus={self.docstatus}" if self.docstatus else "" + parent = f" parent={self.parent}" if getattr(self, "parent", None) else "" + + return f"<{self.fieldtype}{doctype}: {self.fieldname}{docstatus}{parent}{unsaved}>" diff --git a/xhiveframework/core/doctype/docperm/__init__.py b/xhiveframework/core/doctype/docperm/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/docperm/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/docperm/docperm.json b/xhiveframework/core/doctype/docperm/docperm.json new file mode 100644 index 0000000..3ce49c4 --- /dev/null +++ b/xhiveframework/core/doctype/docperm/docperm.json @@ -0,0 +1,221 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:33", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "role_and_level", + "role", + "if_owner", + "column_break_2", + "permlevel", + "section_break_4", + "select", + "read", + "write", + "create", + "delete", + "column_break_8", + "submit", + "cancel", + "amend", + "additional_permissions", + "report", + "export", + "import", + "column_break_19", + "share", + "print", + "email" + ], + "fields": [ + { + "fieldname": "role_and_level", + "fieldtype": "Section Break", + "label": "Role and Level" + }, + { + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "default": "0", + "description": "Apply this rule if the User is the Owner", + "fieldname": "if_owner", + "fieldtype": "Check", + "label": "If user is the owner" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int", + "print_width": "40px", + "width": "40px" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Permissions" + }, + { + "default": "1", + "fieldname": "read", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Read", + "oldfieldname": "read", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "1", + "fieldname": "write", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Write", + "oldfieldname": "write", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "1", + "fieldname": "create", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Create", + "oldfieldname": "create", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "1", + "fieldname": "delete", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Delete" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "submit", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Submit", + "oldfieldname": "submit", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "cancel", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Cancel", + "oldfieldname": "cancel", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "default": "0", + "fieldname": "amend", + "fieldtype": "Check", + "label": "Amend", + "oldfieldname": "amend", + "oldfieldtype": "Check", + "print_width": "32px", + "width": "32px" + }, + { + "fieldname": "additional_permissions", + "fieldtype": "Section Break", + "label": "Additional Permissions" + }, + { + "default": "1", + "fieldname": "report", + "fieldtype": "Check", + "label": "Report", + "print_width": "32px", + "width": "32px" + }, + { + "default": "1", + "fieldname": "export", + "fieldtype": "Check", + "label": "Export" + }, + { + "default": "0", + "fieldname": "import", + "fieldtype": "Check", + "label": "Import" + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "share", + "fieldtype": "Check", + "label": "Share" + }, + { + "default": "1", + "fieldname": "print", + "fieldtype": "Check", + "label": "Print" + }, + { + "default": "1", + "fieldname": "email", + "fieldtype": "Check", + "label": "Email" + }, + { + "default": "0", + "fieldname": "select", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Select" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2023-02-20 13:21:45.071310", + "modified_by": "Administrator", + "module": "Core", + "name": "DocPerm", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/docperm/docperm.py b/xhiveframework/core/doctype/docperm/docperm.py new file mode 100644 index 0000000..87f42dd --- /dev/null +++ b/xhiveframework/core/doctype/docperm/docperm.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class DocPerm(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + amend: DF.Check + cancel: DF.Check + create: DF.Check + delete: DF.Check + email: DF.Check + export: DF.Check + if_owner: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + permlevel: DF.Int + print: DF.Check + read: DF.Check + report: DF.Check + role: DF.Link + select: DF.Check + share: DF.Check + submit: DF.Check + write: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/docshare/__init__.py b/xhiveframework/core/doctype/docshare/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/docshare/docshare.js b/xhiveframework/core/doctype/docshare/docshare.js new file mode 100644 index 0000000..4735204 --- /dev/null +++ b/xhiveframework/core/doctype/docshare/docshare.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("DocShare", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/docshare/docshare.json b/xhiveframework/core/doctype/docshare/docshare.json new file mode 100644 index 0000000..e3581f5 --- /dev/null +++ b/xhiveframework/core/doctype/docshare/docshare.json @@ -0,0 +1,113 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "hash", + "creation": "2015-02-04 04:33:36.330477", + "description": "Internal record of document shares", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "user", + "share_doctype", + "share_name", + "read", + "write", + "share", + "submit", + "everyone", + "notify_by_email" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "search_index": 1 + }, + { + "fieldname": "share_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "share_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Document Name", + "options": "share_doctype", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "read", + "fieldtype": "Check", + "label": "Read" + }, + { + "default": "0", + "fieldname": "write", + "fieldtype": "Check", + "label": "Write" + }, + { + "default": "0", + "fieldname": "share", + "fieldtype": "Check", + "label": "Share" + }, + { + "default": "0", + "fieldname": "everyone", + "fieldtype": "Check", + "label": "Everyone", + "search_index": 1 + }, + { + "default": "1", + "fieldname": "notify_by_email", + "fieldtype": "Check", + "label": "Notify by email", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "submit", + "fieldtype": "Check", + "label": "Submit" + } + ], + "in_create": 1, + "links": [], + "modified": "2023-06-15 18:02:51.877533", + "modified_by": "Administrator", + "module": "Core", + "name": "DocShare", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "export": 1, + "import": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/docshare/docshare.py b/xhiveframework/core/doctype/docshare/docshare.py new file mode 100644 index 0000000..0b1434a --- /dev/null +++ b/xhiveframework/core/doctype/docshare/docshare.py @@ -0,0 +1,97 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, get_fullname + +exclude_from_linked_with = True + + +class DocShare(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + everyone: DF.Check + notify_by_email: DF.Check + read: DF.Check + share: DF.Check + share_doctype: DF.Link + share_name: DF.DynamicLink + submit: DF.Check + user: DF.Link | None + write: DF.Check + # end: auto-generated types + no_feed_on_delete = True + + def validate(self): + self.validate_user() + self.check_share_permission() + self.check_is_submittable() + self.cascade_permissions_downwards() + self.get_doc().run_method("validate_share", self) + + def cascade_permissions_downwards(self): + if self.share or self.write or self.submit: + self.read = 1 + if self.submit: + self.write = 1 + + def get_doc(self): + if not getattr(self, "_doc", None): + self._doc = xhiveframework.get_doc(self.share_doctype, self.share_name) + return self._doc + + def validate_user(self): + if self.everyone: + self.user = None + elif not self.user: + xhiveframework.throw(_("User is mandatory for Share"), xhiveframework.MandatoryError) + + def check_share_permission(self): + if not self.flags.ignore_share_permission and not xhiveframework.has_permission( + self.share_doctype, "share", self.get_doc() + ): + xhiveframework.throw(_('You need to have "Share" permission'), xhiveframework.PermissionError) + + def check_is_submittable(self): + if self.submit and not cint(xhiveframework.db.get_value("DocType", self.share_doctype, "is_submittable")): + xhiveframework.throw( + _("Cannot share {0} with submit permission as the doctype {1} is not submittable").format( + xhiveframework.bold(self.share_name), xhiveframework.bold(self.share_doctype) + ) + ) + + def after_insert(self): + doc = self.get_doc() + owner = get_fullname(self.owner) + + if self.everyone: + doc.add_comment("Shared", _("{0} shared this document with everyone").format(owner)) + else: + doc.add_comment( + "Shared", _("{0} shared this document with {1}").format(owner, get_fullname(self.user)) + ) + + def on_trash(self): + if not self.flags.ignore_share_permission: + self.check_share_permission() + + self.get_doc().add_comment( + "Unshared", + _("{0} un-shared this document with {1}").format( + get_fullname(self.owner), get_fullname(self.user) + ), + ) + + +def on_doctype_update(): + """Add index in `tabDocShare` for `(user, share_doctype)`""" + xhiveframework.db.add_index("DocShare", ["user", "share_doctype"]) + xhiveframework.db.add_index("DocShare", ["share_doctype", "share_name"]) diff --git a/xhiveframework/core/doctype/docshare/test_docshare.py b/xhiveframework/core/doctype/docshare/test_docshare.py new file mode 100644 index 0000000..9db1678 --- /dev/null +++ b/xhiveframework/core/doctype/docshare/test_docshare.py @@ -0,0 +1,226 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +import xhiveframework.share +from xhiveframework.automation.doctype.auto_repeat.test_auto_repeat import create_submittable_doctype +from xhiveframework.tests.utils import XhiveFrameworkTestCase, change_settings + +test_dependencies = ["User"] + + +class TestDocShare(XhiveFrameworkTestCase): + def setUp(self): + self.user = "test@example.com" + self.event = xhiveframework.get_doc( + { + "doctype": "Event", + "subject": "test share event", + "starts_on": "2015-01-01 10:00:00", + "event_type": "Private", + } + ).insert() + + def tearDown(self): + xhiveframework.set_user("Administrator") + self.event.delete() + + def test_add(self): + # user not shared + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", self.user)) + xhiveframework.share.add("Event", self.event.name, self.user) + self.assertTrue(self.event.name in xhiveframework.share.get_shared("Event", self.user)) + + def test_doc_permission(self): + xhiveframework.set_user(self.user) + + self.assertFalse(self.event.has_permission()) + + xhiveframework.set_user("Administrator") + xhiveframework.share.add("Event", self.event.name, self.user) + + xhiveframework.set_user(self.user) + # PERF: All share permission check should happen with maximum 1 query. + with self.assertRowsRead(1): + self.assertTrue(self.event.has_permission()) + + second_event = xhiveframework.get_doc( + { + "doctype": "Event", + "subject": "test share event 2", + "starts_on": "2015-01-01 10:00:00", + "event_type": "Private", + } + ).insert() + xhiveframework.share.add("Event", second_event.name, self.user) + with self.assertRowsRead(1): + self.assertTrue(self.event.has_permission()) + + def test_list_permission(self): + xhiveframework.set_user(self.user) + with self.assertRaises(xhiveframework.PermissionError): + xhiveframework.get_list("Web Page") + + xhiveframework.set_user("Administrator") + doc = xhiveframework.new_doc("Web Page") + doc.update({"title": "test document for docshare permissions"}) + doc.insert() + xhiveframework.share.add("Web Page", doc.name, self.user) + + xhiveframework.set_user(self.user) + self.assertEqual(len(xhiveframework.get_list("Web Page")), 1) + + doc.delete(ignore_permissions=True) + with self.assertRaises(xhiveframework.PermissionError): + xhiveframework.get_list("Web Page") + + def test_share_permission(self): + xhiveframework.share.add("Event", self.event.name, self.user, write=1, share=1) + + xhiveframework.set_user(self.user) + self.assertTrue(self.event.has_permission("share")) + + # test cascade + self.assertTrue(self.event.has_permission("read")) + self.assertTrue(self.event.has_permission("write")) + + def test_set_permission(self): + xhiveframework.share.add("Event", self.event.name, self.user) + + xhiveframework.set_user(self.user) + self.assertFalse(self.event.has_permission("share")) + + xhiveframework.set_user("Administrator") + xhiveframework.share.set_permission("Event", self.event.name, self.user, "share") + + xhiveframework.set_user(self.user) + self.assertTrue(self.event.has_permission("share")) + + def test_permission_to_share(self): + xhiveframework.set_user(self.user) + self.assertRaises(xhiveframework.PermissionError, xhiveframework.share.add, "Event", self.event.name, self.user) + + xhiveframework.set_user("Administrator") + xhiveframework.share.add("Event", self.event.name, self.user, write=1, share=1) + + # test not raises + xhiveframework.set_user(self.user) + xhiveframework.share.add("Event", self.event.name, "test1@example.com", write=1, share=1) + + def test_remove_share(self): + xhiveframework.share.add("Event", self.event.name, self.user, write=1, share=1) + + xhiveframework.set_user(self.user) + self.assertTrue(self.event.has_permission("share")) + + xhiveframework.set_user("Administrator") + xhiveframework.share.remove("Event", self.event.name, self.user) + + xhiveframework.set_user(self.user) + self.assertFalse(self.event.has_permission("share")) + + def test_share_with_everyone(self): + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", self.user)) + + xhiveframework.share.set_permission("Event", self.event.name, None, "read", everyone=1) + self.assertTrue(self.event.name in xhiveframework.share.get_shared("Event", self.user)) + self.assertTrue(self.event.name in xhiveframework.share.get_shared("Event", "test1@example.com")) + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", "Guest")) + + xhiveframework.share.set_permission("Event", self.event.name, None, "read", value=0, everyone=1) + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", self.user)) + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", "test1@example.com")) + self.assertTrue(self.event.name not in xhiveframework.share.get_shared("Event", "Guest")) + + def test_share_with_submit_perm(self): + doctype = "Test DocShare with Submit" + create_submittable_doctype(doctype, submit_perms=0) + + submittable_doc = xhiveframework.get_doc(dict(doctype=doctype, test="test docshare with submit")).insert() + + xhiveframework.set_user(self.user) + self.assertFalse(xhiveframework.has_permission(doctype, "submit", user=self.user)) + + xhiveframework.set_user("Administrator") + xhiveframework.share.add(doctype, submittable_doc.name, self.user, submit=1) + + xhiveframework.set_user(self.user) + self.assertTrue(xhiveframework.has_permission(doctype, "submit", doc=submittable_doc.name, user=self.user)) + + # test cascade + self.assertTrue(xhiveframework.has_permission(doctype, "read", doc=submittable_doc.name, user=self.user)) + self.assertTrue(xhiveframework.has_permission(doctype, "write", doc=submittable_doc.name, user=self.user)) + + xhiveframework.share.remove(doctype, submittable_doc.name, self.user) + + def test_share_int_pk(self): + test_doc = xhiveframework.new_doc("Console Log") + + test_doc.insert() + xhiveframework.share.add("Console Log", test_doc.name, self.user) + + xhiveframework.set_user(self.user) + self.assertIn( + str(test_doc.name), [str(name) for name in xhiveframework.get_list("Console Log", pluck="name")] + ) + + test_doc.reload() + self.assertTrue(test_doc.has_permission("read")) + + @change_settings("System Settings", {"disable_document_sharing": 1}) + def test_share_disabled_add(self): + "Test if user loses share access on disabling share globally." + xhiveframework.share.add("Event", self.event.name, self.user, share=1) # Share as admin + xhiveframework.set_user(self.user) + + # User does not have share access although given to them + self.assertFalse(self.event.has_permission("share")) + self.assertRaises( + xhiveframework.PermissionError, xhiveframework.share.add, "Event", self.event.name, "test1@example.com" + ) + + @change_settings("System Settings", {"disable_document_sharing": 1}) + def test_share_disabled_add_with_ignore_permissions(self): + xhiveframework.share.add("Event", self.event.name, self.user, share=1) + xhiveframework.set_user(self.user) + + # User does not have share access although given to them + self.assertFalse(self.event.has_permission("share")) + + # Test if behaviour is consistent for developer overrides + xhiveframework.share.add_docshare( + "Event", self.event.name, "test1@example.com", flags={"ignore_share_permission": True} + ) + + @change_settings("System Settings", {"disable_document_sharing": 1}) + def test_share_disabled_set_permission(self): + xhiveframework.share.add("Event", self.event.name, self.user, share=1) + xhiveframework.set_user(self.user) + + # User does not have share access although given to them + self.assertFalse(self.event.has_permission("share")) + self.assertRaises( + xhiveframework.PermissionError, + xhiveframework.share.set_permission, + "Event", + self.event.name, + "test1@example.com", + "read", + ) + + @change_settings("System Settings", {"disable_document_sharing": 1}) + def test_share_disabled_assign_to(self): + """ + Assigning a document to a user without access must not share the document, + if sharing disabled. + """ + from xhiveframework.desk.form.assign_to import add + + xhiveframework.share.add("Event", self.event.name, self.user, share=1) + xhiveframework.set_user(self.user) + + self.assertRaises( + xhiveframework.ValidationError, + add, + {"doctype": "Event", "name": self.event.name, "assign_to": ["test1@example.com"]}, + ) diff --git a/xhiveframework/core/doctype/docshare/test_records.json b/xhiveframework/core/doctype/docshare/test_records.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/xhiveframework/core/doctype/docshare/test_records.json @@ -0,0 +1 @@ +[] diff --git a/xhiveframework/core/doctype/doctype/README.md b/xhiveframework/core/doctype/doctype/README.md new file mode 100644 index 0000000..3922a7e --- /dev/null +++ b/xhiveframework/core/doctype/doctype/README.md @@ -0,0 +1,15 @@ +DocType is the basic building block of an application and encompasses all the three elements i.e. model, view and controller. It represents a: + +- Table in the database +- Form in the application +- Controller (class) to execute business logic + +#### Single Type + +DocTypes can be of "Single" type where they do not represent a table, and only one instance is maintained. This can be used where the DocType is required only for its view features or to store some configurations in one place. + +#### Child Tables + +DocTypes can be child tables of other DocTypes. In such cases, they must defined `parent`, `parenttype` and `parentfield` properties to uniquely identify its placement. + +In the parent DocType, the position of a child in the field sequence is defined by the `Table` field type. \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype/__init__.py b/xhiveframework/core/doctype/doctype/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/doctype/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/doctype/boilerplate/__init__.py b/xhiveframework/core/doctype/doctype/boilerplate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/doctype/boilerplate/controller._py b/xhiveframework/core/doctype/doctype/boilerplate/controller._py new file mode 100644 index 0000000..3e6508d --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/controller._py @@ -0,0 +1,9 @@ +# Copyright (c) {year}, {app_publisher} and contributors +# For license information, please see license.txt + +# import xhiveframework +{base_class_import} + + +class {classname}({base_class}): + {custom_controller} diff --git a/xhiveframework/core/doctype/doctype/boilerplate/controller.js b/xhiveframework/core/doctype/doctype/boilerplate/controller.js new file mode 100644 index 0000000..70261cf --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/controller.js @@ -0,0 +1,8 @@ +// Copyright (c) {year}, {app_publisher} and contributors +// For license information, please see license.txt + +// xhiveframework.ui.form.on("{doctype}", {{ +// refresh(frm) {{ + +// }}, +// }}); diff --git a/xhiveframework/core/doctype/doctype/boilerplate/controller_list.html b/xhiveframework/core/doctype/doctype/boilerplate/controller_list.html new file mode 100644 index 0000000..bc56ceb --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/controller_list.html @@ -0,0 +1,34 @@ +
    +
    +
    + {{%= list.get_avatar_and_id(doc) %}} + + + + {{%= doc.text %}} + + + {{% if(doc.check) {{ %}} + + + + {{% }} %}} + + + + {{%= doc.status %}} + +
    +
    + +
    + {{% var completed = doc.completed, title = __("Completed") %}} + {{% include "templates/form_grid/includes/progress.html" %}} +
    +
    diff --git a/xhiveframework/core/doctype/doctype/boilerplate/controller_list.js b/xhiveframework/core/doctype/doctype/boilerplate/controller_list.js new file mode 100644 index 0000000..936ebac --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/controller_list.js @@ -0,0 +1,5 @@ +/* eslint-disable */ +// xhiveframework.listview_settings["{doctype}"] = {{ +// add_fields: ["status"], +// filters: [["status","=", "Open"]], +// }}; diff --git a/xhiveframework/core/doctype/doctype/boilerplate/templates/controller.html b/xhiveframework/core/doctype/doctype/boilerplate/templates/controller.html new file mode 100644 index 0000000..412368d --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/templates/controller.html @@ -0,0 +1,7 @@ +{{% extends "templates/web.html" %}} + +{{% block page_content %}} +

    {{{{ title }}}}

    +{{% endblock %}} + + \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype/boilerplate/templates/controller_row.html b/xhiveframework/core/doctype/doctype/boilerplate/templates/controller_row.html new file mode 100644 index 0000000..66fe744 --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/templates/controller_row.html @@ -0,0 +1,4 @@ + + diff --git a/xhiveframework/core/doctype/doctype/boilerplate/test_controller._py b/xhiveframework/core/doctype/doctype/boilerplate/test_controller._py new file mode 100644 index 0000000..99f1d0f --- /dev/null +++ b/xhiveframework/core/doctype/doctype/boilerplate/test_controller._py @@ -0,0 +1,9 @@ +# Copyright (c) {year}, {app_publisher} and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class Test{classname}(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/doctype/doctype.js b/xhiveframework/core/doctype/doctype/doctype.js new file mode 100644 index 0000000..ded8a1b --- /dev/null +++ b/xhiveframework/core/doctype/doctype/doctype.js @@ -0,0 +1,186 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.ui.form.on("DocType", { + onload: function (frm) { + if (frm.is_new() && !frm.doc?.fields) { + xhiveframework.listview_settings["DocType"].new_doctype_dialog(); + } + }, + + before_save: function (frm) { + let form_builder = xhiveframework.form_builder; + if (form_builder?.store) { + let fields = form_builder.store.update_fields(); + + // if fields is a string, it means there is an error + if (typeof fields === "string") { + xhiveframework.throw(fields); + } + } + }, + + after_save: function (frm) { + if ( + xhiveframework.form_builder && + xhiveframework.form_builder.doctype === frm.doc.name && + xhiveframework.form_builder.store + ) { + xhiveframework.form_builder.store.fetch(); + } + }, + + refresh: function (frm) { + frm.set_query("role", "permissions", function (doc) { + if (doc.custom && xhiveframework.session.user != "Administrator") { + return { + query: "xhiveframework.core.doctype.role.role.role_query", + filters: [["Role", "name", "!=", "All"]], + }; + } + }); + + if (xhiveframework.session.user !== "Administrator" || !xhiveframework.boot.developer_mode) { + if (frm.is_new()) { + frm.set_value("custom", 1); + } + frm.toggle_enable("custom", 0); + frm.toggle_enable("is_virtual", 0); + frm.toggle_enable("beta", 0); + } + + if (!frm.is_new() && !frm.doc.istable) { + if (frm.doc.issingle) { + frm.add_custom_button(__("Go to {0}", [__(frm.doc.name)]), () => { + window.open(`/app/${xhiveframework.router.slug(frm.doc.name)}`); + }); + } else { + frm.add_custom_button(__("Go to {0} List", [__(frm.doc.name)]), () => { + window.open(`/app/${xhiveframework.router.slug(frm.doc.name)}`); + }); + } + } + + const customize_form_link = "Customize Form"; + if (!xhiveframework.boot.developer_mode && !frm.doc.custom) { + // make the document read-only + frm.set_read_only(); + frm.dashboard.clear_comment(); + frm.dashboard.add_comment( + __("DocTypes can not be modified, please use {0} instead", [customize_form_link]), + "blue", + true + ); + } else if (xhiveframework.boot.developer_mode) { + frm.dashboard.clear_comment(); + let msg = __( + "This site is running in developer mode. Any change made here will be updated in code." + ); + msg += "
    "; + msg += __("If you just want to customize for your site, use {0} instead.", [ + customize_form_link, + ]); + frm.dashboard.add_comment(msg, "yellow", true); + } + + if (frm.is_new()) { + frm.events.set_default_permission(frm); + frm.set_value("default_view", "List"); + } else { + frm.toggle_enable("engine", 0); + } + + // set label for "In List View" for child tables + frm.get_docfield("fields", "in_list_view").label = frm.doc.istable + ? __("In Grid View") + : __("In List View"); + + frm.cscript.autoname(frm); + frm.cscript.set_naming_rule_description(frm); + frm.trigger("setup_default_views"); + + render_form_builder(frm); + }, + + istable: (frm) => { + if (frm.doc.istable && frm.is_new()) { + frm.set_value("default_view", null); + } else if (!frm.doc.istable && !frm.is_new()) { + frm.events.set_default_permission(frm); + } + }, + + set_default_permission: (frm) => { + if (!(frm.doc.permissions && frm.doc.permissions.length)) { + frm.add_child("permissions", { role: "System Manager" }); + } + }, + + is_tree: (frm) => { + frm.trigger("setup_default_views"); + }, + + is_calendar_and_gantt: (frm) => { + frm.trigger("setup_default_views"); + }, + + setup_default_views: (frm) => { + xhiveframework.model.set_default_views_for_doctype(frm.doc.name, frm); + }, + + on_tab_change: (frm) => { + let current_tab = frm.get_active_tab().label; + + if (current_tab === "Form") { + frm.footer.wrapper.hide(); + frm.form_wrapper.find(".form-message").hide(); + frm.form_wrapper.addClass("mb-1"); + } else { + frm.footer.wrapper.show(); + frm.form_wrapper.find(".form-message").show(); + frm.form_wrapper.removeClass("mb-1"); + } + }, +}); + +xhiveframework.ui.form.on("DocField", { + form_render(frm, doctype, docname) { + frm.trigger("setup_fetch_from_fields", doctype, docname); + }, + + fieldtype: function (frm) { + frm.trigger("max_attachments"); + }, + + fields_add: (frm) => { + frm.trigger("setup_default_views"); + }, +}); + +function render_form_builder(frm) { + if (xhiveframework.form_builder && xhiveframework.form_builder.doctype === frm.doc.name) { + xhiveframework.form_builder.setup_page_actions(); + xhiveframework.form_builder.store.fetch(); + return; + } + + if (xhiveframework.form_builder) { + xhiveframework.form_builder.wrapper = $(frm.fields_dict["form_builder"].wrapper); + xhiveframework.form_builder.frm = frm; + xhiveframework.form_builder.doctype = frm.doc.name; + xhiveframework.form_builder.customize = false; + xhiveframework.form_builder.init(true); + xhiveframework.form_builder.store.fetch(); + } else { + xhiveframework.require("form_builder.bundle.js").then(() => { + xhiveframework.form_builder = new xhiveframework.ui.FormBuilder({ + wrapper: $(frm.fields_dict["form_builder"].wrapper), + frm: frm, + doctype: frm.doc.name, + customize: false, + }); + }); + } +} + +extend_cscript(cur_frm.cscript, new xhiveframework.model.DocTypeController({ frm: cur_frm })); diff --git a/xhiveframework/core/doctype/doctype/doctype.json b/xhiveframework/core/doctype/doctype/doctype.json new file mode 100644 index 0000000..25d4bd7 --- /dev/null +++ b/xhiveframework/core/doctype/doctype/doctype.json @@ -0,0 +1,790 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2013-02-18 13:36:19", + "description": "DocType is a Table / Form in the application.", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "form_builder_tab", + "form_builder", + "settings_tab", + "sb0", + "module", + "is_submittable", + "istable", + "issingle", + "is_tree", + "is_calendar_and_gantt", + "editable_grid", + "quick_entry", + "cb01", + "track_changes", + "track_seen", + "track_views", + "custom", + "beta", + "is_virtual", + "queue_in_background", + "sb1", + "naming_rule", + "autoname", + "allow_rename", + "column_break_15", + "description", + "documentation", + "form_settings_section", + "image_field", + "timeline_field", + "nsm_parent_field", + "max_attachments", + "column_break_23", + "hide_toolbar", + "allow_copy", + "allow_import", + "allow_events_in_timeline", + "allow_auto_repeat", + "make_attachments_public", + "view_settings", + "title_field", + "show_title_field_in_link", + "translated_doctype", + "search_fields", + "default_print_format", + "sort_field", + "sort_order", + "default_view", + "force_re_route_to_default_view", + "column_break_29", + "document_type", + "icon", + "color", + "show_preview_popup", + "show_name_in_global_search", + "email_settings_sb", + "default_email_template", + "column_break_51", + "email_append_to", + "sender_field", + "sender_name_field", + "subject_field", + "sb2", + "permissions", + "restrict_to_domain", + "read_only", + "in_create", + "actions_section", + "actions", + "links_section", + "links", + "document_states_section", + "states", + "web_view", + "has_web_view", + "allow_guest_to_view", + "index_web_pages_for_search", + "route", + "is_published_field", + "website_search_field", + "advanced", + "engine", + "migration_hash", + "fields_section", + "fields", + "connections_tab" + ], + "fields": [ + { + "fieldname": "sb0", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Module", + "oldfieldname": "module", + "oldfieldtype": "Link", + "options": "Module Def", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.", + "fieldname": "is_submittable", + "fieldtype": "Check", + "label": "Is Submittable" + }, + { + "default": "0", + "description": "Child Tables are shown as a Grid in other DocTypes", + "fieldname": "istable", + "fieldtype": "Check", + "in_standard_filter": 1, + "label": "Is Child Table", + "oldfieldname": "istable", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "Single Types have only one record no tables associated. Values are stored in tabSingles", + "fieldname": "issingle", + "fieldtype": "Check", + "in_standard_filter": 1, + "label": "Is Single", + "oldfieldname": "issingle", + "oldfieldtype": "Check", + "set_only_once": 1 + }, + { + "default": "1", + "depends_on": "istable", + "fieldname": "editable_grid", + "fieldtype": "Check", + "label": "Editable Grid" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable && !doc.issingle", + "description": "Open a dialog with mandatory fields to create a new record quickly", + "fieldname": "quick_entry", + "fieldtype": "Check", + "label": "Quick Entry" + }, + { + "fieldname": "cb01", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "If enabled, changes to the document are tracked and shown in timeline", + "fieldname": "track_changes", + "fieldtype": "Check", + "label": "Track Changes" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "If enabled, the document is marked as seen, the first time a user opens it", + "fieldname": "track_seen", + "fieldtype": "Check", + "label": "Track Seen" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "If enabled, document views are tracked, this can happen multiple times", + "fieldname": "track_views", + "fieldtype": "Check", + "label": "Track Views" + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "label": "Custom?" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "fieldname": "beta", + "fieldtype": "Check", + "label": "Beta" + }, + { + "fieldname": "fields", + "fieldtype": "Table", + "label": "Fields", + "oldfieldname": "fields", + "oldfieldtype": "Table", + "options": "DocField" + }, + { + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Naming" + }, + { + "description": "Naming Options:\n
    1. field:[fieldname] - By Field
    2. autoincrement - Uses Databases' Auto Increment feature
    3. naming_series: - By Naming Series (field called naming_series must be present)
    4. Prompt - Prompt user for a name
    5. [series] - Series by prefix (separated by a dot); for example PRE.#####
    6. \n
    7. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Replace all braced words (fieldnames, date words (DD, MM, YY), series) with their value. Outside braces, any characters can be used.
    ", + "fieldname": "autoname", + "fieldtype": "Data", + "label": "Auto Name", + "oldfieldname": "autoname", + "oldfieldtype": "Data" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text" + }, + { + "collapsible": 1, + "depends_on": "eval:!doc.istable", + "fieldname": "form_settings_section", + "fieldtype": "Section Break", + "label": "Form Settings" + }, + { + "description": "Must be of type \"Attach Image\"", + "fieldname": "image_field", + "fieldtype": "Data", + "label": "Image Field" + }, + { + "depends_on": "eval:!doc.istable", + "description": "Comments and Communications will be associated with this linked document", + "fieldname": "timeline_field", + "fieldtype": "Data", + "label": "Timeline Field" + }, + { + "fieldname": "max_attachments", + "fieldtype": "Int", + "label": "Max Attachments", + "oldfieldname": "max_attachments", + "oldfieldtype": "Int" + }, + { + "fieldname": "column_break_23", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "hide_toolbar", + "fieldtype": "Check", + "label": "Hide Sidebar, Menu, and Comments", + "oldfieldname": "hide_toolbar", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "allow_copy", + "fieldtype": "Check", + "label": "Hide Copy", + "oldfieldname": "allow_copy", + "oldfieldtype": "Check" + }, + { + "default": "1", + "depends_on": "eval:doc.naming_rule !== \"Autoincrement\"", + "fieldname": "allow_rename", + "fieldtype": "Check", + "label": "Allow Rename", + "oldfieldname": "allow_rename", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "allow_import", + "fieldtype": "Check", + "label": "Allow Import (via Data Import Tool)" + }, + { + "default": "0", + "fieldname": "allow_events_in_timeline", + "fieldtype": "Check", + "label": "Allow events in timeline" + }, + { + "default": "0", + "fieldname": "allow_auto_repeat", + "fieldtype": "Check", + "label": "Allow Auto Repeat" + }, + { + "collapsible": 1, + "depends_on": "eval:!doc.istable", + "fieldname": "view_settings", + "fieldtype": "Section Break", + "label": "View Settings" + }, + { + "depends_on": "eval:!doc.istable", + "fieldname": "title_field", + "fieldtype": "Data", + "label": "Title Field", + "mandatory_depends_on": "eval:doc.show_title_field_in_link" + }, + { + "depends_on": "eval:!doc.istable", + "fieldname": "search_fields", + "fieldtype": "Data", + "label": "Search Fields", + "oldfieldname": "search_fields", + "oldfieldtype": "Data" + }, + { + "fieldname": "default_print_format", + "fieldtype": "Data", + "label": "Default Print Format" + }, + { + "default": "modified", + "depends_on": "eval:!doc.istable", + "fieldname": "sort_field", + "fieldtype": "Data", + "label": "Default Sort Field" + }, + { + "default": "DESC", + "depends_on": "eval:!doc.istable", + "fieldname": "sort_order", + "fieldtype": "Select", + "label": "Default Sort Order", + "options": "ASC\nDESC" + }, + { + "fieldname": "column_break_29", + "fieldtype": "Column Break" + }, + { + "fieldname": "document_type", + "fieldtype": "Select", + "label": "Show in Module Section", + "oldfieldname": "document_type", + "oldfieldtype": "Select", + "options": "\nDocument\nSetup\nSystem\nOther" + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon" + }, + { + "fieldname": "color", + "fieldtype": "Data", + "label": "Color" + }, + { + "default": "0", + "fieldname": "show_preview_popup", + "fieldtype": "Check", + "label": "Show Preview Popup" + }, + { + "default": "0", + "fieldname": "show_name_in_global_search", + "fieldtype": "Check", + "label": "Make \"name\" searchable in Global Search" + }, + { + "depends_on": "eval:!doc.istable", + "fieldname": "sb2", + "fieldtype": "Section Break", + "label": "Permission Rules" + }, + { + "fieldname": "permissions", + "fieldtype": "Table", + "label": "Permissions", + "oldfieldname": "permissions", + "oldfieldtype": "Table", + "options": "DocPerm" + }, + { + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict To Domain", + "options": "Domain" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "User Cannot Search", + "oldfieldname": "read_only", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "in_create", + "fieldtype": "Check", + "label": "User Cannot Create", + "oldfieldname": "in_create", + "oldfieldtype": "Check" + }, + { + "depends_on": "eval:doc.custom===0 && !doc.istable", + "fieldname": "web_view", + "fieldtype": "Section Break", + "label": "Web View" + }, + { + "default": "0", + "fieldname": "has_web_view", + "fieldtype": "Check", + "label": "Has Web View" + }, + { + "default": "0", + "depends_on": "has_web_view", + "fieldname": "allow_guest_to_view", + "fieldtype": "Check", + "label": "Allow Guest to View" + }, + { + "depends_on": "eval:!doc.istable", + "fieldname": "route", + "fieldtype": "Data", + "label": "Route" + }, + { + "depends_on": "has_web_view", + "fieldname": "is_published_field", + "fieldtype": "Data", + "label": "Is Published Field" + }, + { + "collapsible": 1, + "fieldname": "advanced", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Advanced" + }, + { + "default": "InnoDB", + "depends_on": "eval:!doc.issingle", + "fieldname": "engine", + "fieldtype": "Select", + "label": "Database Engine", + "options": "InnoDB\nMyISAM" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "Tree structures are implemented using Nested Set", + "fieldname": "is_tree", + "fieldtype": "Check", + "label": "Is Tree" + }, + { + "depends_on": "is_tree", + "fieldname": "nsm_parent_field", + "fieldtype": "Data", + "label": "Parent Field (Tree)" + }, + { + "description": "URL for documentation or help", + "fieldname": "documentation", + "fieldtype": "Data", + "label": "Documentation Link" + }, + { + "collapsible": 1, + "collapsible_depends_on": "actions", + "depends_on": "eval:!doc.istable", + "fieldname": "actions_section", + "fieldtype": "Section Break", + "label": "Actions" + }, + { + "fieldname": "actions", + "fieldtype": "Table", + "label": "Actions", + "options": "DocType Action" + }, + { + "collapsible": 1, + "collapsible_depends_on": "links", + "depends_on": "eval:!doc.istable", + "fieldname": "links_section", + "fieldtype": "Section Break", + "label": "Linked Documents" + }, + { + "fieldname": "links", + "fieldtype": "Table", + "label": "Links", + "options": "DocType Link" + }, + { + "depends_on": "email_append_to", + "fieldname": "subject_field", + "fieldtype": "Data", + "label": "Subject Field" + }, + { + "depends_on": "email_append_to", + "fieldname": "sender_field", + "fieldtype": "Data", + "label": "Sender Email Field", + "mandatory_depends_on": "email_append_to" + }, + { + "default": "0", + "fieldname": "email_append_to", + "fieldtype": "Check", + "label": "Allow document creation via Email" + }, + { + "collapsible": 1, + "depends_on": "eval:!doc.istable", + "fieldname": "email_settings_sb", + "fieldtype": "Section Break", + "label": "Email Settings" + }, + { + "default": "1", + "fieldname": "index_web_pages_for_search", + "fieldtype": "Check", + "label": "Index Web Pages for Search" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" + }, + { + "fieldname": "default_email_template", + "fieldtype": "Link", + "label": "Default Email Template", + "options": "Email Template" + }, + { + "fieldname": "column_break_51", + "fieldtype": "Column Break" + }, + { + "depends_on": "has_web_view", + "fieldname": "website_search_field", + "fieldtype": "Data", + "label": "Website Search Field" + }, + { + "fieldname": "naming_rule", + "fieldtype": "Select", + "label": "Naming Rule", + "length": 40, + "options": "\nSet by user\nAutoincrement\nBy fieldname\nBy \"Naming Series\" field\nExpression\nExpression (old style)\nRandom\nBy script" + }, + { + "fieldname": "migration_hash", + "fieldtype": "Data", + "hidden": 1 + }, + { + "fieldname": "states", + "fieldtype": "Table", + "label": "States", + "options": "DocType State" + }, + { + "collapsible": 1, + "depends_on": "eval:!doc.istable", + "fieldname": "document_states_section", + "fieldtype": "Section Break", + "label": "Document States" + }, + { + "default": "0", + "fieldname": "show_title_field_in_link", + "fieldtype": "Check", + "label": "Show Title in Link Fields" + }, + { + "default": "0", + "fieldname": "translated_doctype", + "fieldtype": "Check", + "label": "Translate Link Fields" + }, + { + "default": "0", + "fieldname": "make_attachments_public", + "fieldtype": "Check", + "label": "Make Attachments Public by Default" + }, + { + "default": "0", + "depends_on": "eval: doc.is_submittable", + "description": "Enabling this will submit documents in background", + "fieldname": "queue_in_background", + "fieldtype": "Check", + "label": "Queue in Background (BETA)" + }, + { + "fieldname": "default_view", + "fieldtype": "Select", + "label": "Default View" + }, + { + "default": "0", + "fieldname": "force_re_route_to_default_view", + "fieldtype": "Check", + "label": "Force Re-route to Default View" + }, + { + "default": "0", + "depends_on": "eval:!doc.istable", + "description": "Enables Calendar and Gantt views.", + "fieldname": "is_calendar_and_gantt", + "fieldtype": "Check", + "label": "Is Calendar and Gantt" + }, + { + "fieldname": "settings_tab", + "fieldtype": "Tab Break", + "label": "Settings" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "form_builder_tab", + "fieldtype": "Tab Break", + "label": "Form" + }, + { + "fieldname": "form_builder", + "fieldtype": "HTML", + "label": "Form Builder" + }, + { + "collapsible": 1, + "fieldname": "fields_section", + "fieldtype": "Section Break", + "label": "Fields" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "depends_on": "email_append_to", + "fieldname": "sender_name_field", + "fieldtype": "Data", + "label": "Sender Name Field" + } + ], + "icon": "fa fa-bolt", + "idx": 6, + "index_web_pages_for_search": 1, + "links": [ + { + "group": "Views", + "link_doctype": "Report", + "link_fieldname": "ref_doctype" + }, + { + "group": "Workflow", + "link_doctype": "Workflow", + "link_fieldname": "document_type" + }, + { + "group": "Workflow", + "link_doctype": "Notification", + "link_fieldname": "document_type" + }, + { + "group": "Customization", + "link_doctype": "Custom Field", + "link_fieldname": "dt" + }, + { + "group": "Customization", + "link_doctype": "Client Script", + "link_fieldname": "dt" + }, + { + "group": "Customization", + "link_doctype": "Server Script", + "link_fieldname": "reference_doctype" + }, + { + "group": "Workflow", + "link_doctype": "Webhook", + "link_fieldname": "webhook_doctype" + }, + { + "group": "Views", + "link_doctype": "Print Format", + "link_fieldname": "doc_type" + }, + { + "group": "Views", + "link_doctype": "Web Form", + "link_fieldname": "doc_type" + }, + { + "group": "Views", + "link_doctype": "Calendar View", + "link_fieldname": "reference_doctype" + }, + { + "group": "Views", + "link_doctype": "Kanban Board", + "link_fieldname": "reference_doctype" + }, + { + "group": "Workflow", + "link_doctype": "Onboarding Step", + "link_fieldname": "reference_document" + }, + { + "group": "Rules", + "link_doctype": "Auto Repeat", + "link_fieldname": "reference_doctype" + }, + { + "group": "Rules", + "link_doctype": "Assignment Rule", + "link_fieldname": "document_type" + }, + { + "group": "Rules", + "link_doctype": "Energy Point Rule", + "link_fieldname": "reference_doctype" + } + ], + "modified": "2023-12-01 18:37:16.799471", + "modified_by": "Administrator", + "module": "Core", + "name": "DocType", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "route": "doctype", + "search_fields": "module", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype/doctype.py b/xhiveframework/core/doctype/doctype/doctype.py new file mode 100644 index 0000000..3550380 --- /dev/null +++ b/xhiveframework/core/doctype/doctype/doctype.py @@ -0,0 +1,1895 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import copy +import json +import os +import re +import shutil +from typing import TYPE_CHECKING, Union + +import xhiveframework +from xhiveframework import _ +from xhiveframework.cache_manager import clear_controller_cache, clear_user_cache +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_field +from xhiveframework.custom.doctype.property_setter.property_setter import make_property_setter +from xhiveframework.database import savepoint +from xhiveframework.database.schema import validate_column_length, validate_column_name +from xhiveframework.desk.notifications import delete_notification_count_for, get_filters_for +from xhiveframework.desk.utils import validate_route_conflict +from xhiveframework.model import ( + child_table_fields, + data_field_options, + default_fields, + no_value_fields, + table_fields, +) +from xhiveframework.model.base_document import get_controller +from xhiveframework.model.docfield import supports_translation +from xhiveframework.model.document import Document +from xhiveframework.model.meta import Meta +from xhiveframework.modules import get_doc_path, make_boilerplate +from xhiveframework.modules.import_file import get_file_path +from xhiveframework.permissions import ALL_USER_ROLE, AUTOMATIC_ROLES, SYSTEM_USER_ROLE +from xhiveframework.query_builder.functions import Concat +from xhiveframework.utils import cint, flt, is_a_property, random_string +from xhiveframework.website.utils import clear_cache + +if TYPE_CHECKING: + from xhiveframework.custom.doctype.customize_form.customize_form import CustomizeForm + +DEPENDS_ON_PATTERN = re.compile(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+') +ILLEGAL_FIELDNAME_PATTERN = re.compile("""['",./%@()<>{}]""") +WHITESPACE_PADDING_PATTERN = re.compile(r"^[ \t\n\r]+|[ \t\n\r]+$", flags=re.ASCII) +START_WITH_LETTERS_PATTERN = re.compile(r"^(?![\W])[^\d_\s][\w -]+$", flags=re.ASCII) +FIELD_PATTERN = re.compile("{(.*?)}", flags=re.UNICODE) + + +class InvalidFieldNameError(xhiveframework.ValidationError): + pass + + +class UniqueFieldnameError(xhiveframework.ValidationError): + pass + + +class IllegalMandatoryError(xhiveframework.ValidationError): + pass + + +class DoctypeLinkError(xhiveframework.ValidationError): + pass + + +class WrongOptionsDoctypeLinkError(xhiveframework.ValidationError): + pass + + +class HiddenAndMandatoryWithoutDefaultError(xhiveframework.ValidationError): + pass + + +class NonUniqueError(xhiveframework.ValidationError): + pass + + +class CannotIndexedError(xhiveframework.ValidationError): + pass + + +class CannotCreateStandardDoctypeError(xhiveframework.ValidationError): + pass + + +form_grid_templates = {"fields": "templates/form_grid/fields.html"} + + +class DocType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.docfield.docfield import DocField + from xhiveframework.core.doctype.docperm.docperm import DocPerm + from xhiveframework.core.doctype.doctype_action.doctype_action import DocTypeAction + from xhiveframework.core.doctype.doctype_link.doctype_link import DocTypeLink + from xhiveframework.core.doctype.doctype_state.doctype_state import DocTypeState + from xhiveframework.types import DF + + actions: DF.Table[DocTypeAction] + allow_auto_repeat: DF.Check + allow_copy: DF.Check + allow_events_in_timeline: DF.Check + allow_guest_to_view: DF.Check + allow_import: DF.Check + allow_rename: DF.Check + autoname: DF.Data | None + beta: DF.Check + color: DF.Data | None + custom: DF.Check + default_email_template: DF.Link | None + default_print_format: DF.Data | None + default_view: DF.Literal[None] + description: DF.SmallText | None + document_type: DF.Literal["", "Document", "Setup", "System", "Other"] + documentation: DF.Data | None + editable_grid: DF.Check + email_append_to: DF.Check + engine: DF.Literal["InnoDB", "MyISAM"] + fields: DF.Table[DocField] + force_re_route_to_default_view: DF.Check + has_web_view: DF.Check + hide_toolbar: DF.Check + icon: DF.Data | None + image_field: DF.Data | None + in_create: DF.Check + index_web_pages_for_search: DF.Check + is_calendar_and_gantt: DF.Check + is_published_field: DF.Data | None + is_submittable: DF.Check + is_tree: DF.Check + is_virtual: DF.Check + issingle: DF.Check + istable: DF.Check + links: DF.Table[DocTypeLink] + make_attachments_public: DF.Check + max_attachments: DF.Int + migration_hash: DF.Data | None + module: DF.Link + naming_rule: DF.Literal[ + "", + "Set by user", + "Autoincrement", + "By fieldname", + 'By "Naming Series" field', + "Expression", + "Expression (old style)", + "Random", + "By script", + ] + nsm_parent_field: DF.Data | None + permissions: DF.Table[DocPerm] + queue_in_background: DF.Check + quick_entry: DF.Check + read_only: DF.Check + restrict_to_domain: DF.Link | None + route: DF.Data | None + search_fields: DF.Data | None + sender_field: DF.Data | None + sender_name_field: DF.Data | None + show_name_in_global_search: DF.Check + show_preview_popup: DF.Check + show_title_field_in_link: DF.Check + sort_field: DF.Data | None + sort_order: DF.Literal["ASC", "DESC"] + states: DF.Table[DocTypeState] + subject_field: DF.Data | None + timeline_field: DF.Data | None + title_field: DF.Data | None + track_changes: DF.Check + track_seen: DF.Check + track_views: DF.Check + translated_doctype: DF.Check + website_search_field: DF.Data | None + # end: auto-generated types + + def validate(self): + """Validate DocType before saving. + + - Check if developer mode is set. + - Validate series + - Check fieldnames (duplication etc) + - Clear permission table for child tables + - Add `amended_from` and `amended_by` if Amendable + - Add custom field `auto_repeat` if Repeatable + - Check if links point to valid fieldnames""" + + self.check_developer_mode() + + self.validate_name() + + self.set_defaults_for_single_and_table() + self.set_defaults_for_autoincremented() + self.scrub_field_names() + self.set_default_in_list_view() + self.set_default_translatable() + validate_series(self) + self.set("can_change_name_type", validate_autoincrement_autoname(self)) + self.validate_document_type() + validate_fields(self) + self.check_indexing_for_dashboard_links() + if not self.istable: + validate_permissions(self) + + self.make_amendable() + self.make_repeatable() + self.validate_nestedset() + self.validate_child_table() + self.validate_website() + self.validate_virtual_doctype_methods() + self.ensure_minimum_max_attachment_limit() + validate_links_table_fieldnames(self) + + if not self.is_new(): + self.before_update = xhiveframework.get_doc("DocType", self.name) + self.setup_fields_to_fetch() + self.validate_field_name_conflicts() + + check_email_append_to(self) + + if self.default_print_format and not self.custom: + xhiveframework.throw(_("Standard DocType cannot have default print format, use Customize Form")) + + def validate_field_name_conflicts(self): + """Check if field names dont conflict with controller properties and methods""" + core_doctypes = [ + "Custom DocPerm", + "DocPerm", + "Custom Field", + "Customize Form Field", + "Web Form Field", + "DocField", + ] + + if self.name in core_doctypes: + return + + try: + controller = get_controller(self.name) + except ImportError: + controller = Document + + available_objects = {x for x in dir(controller) if isinstance(x, str)} + property_set = {x for x in available_objects if is_a_property(getattr(controller, x, None))} + method_set = { + x for x in available_objects if x not in property_set and callable(getattr(controller, x, None)) + } + + for docfield in self.get("fields") or []: + if docfield.fieldtype in no_value_fields: + continue + + conflict_type = None + field = docfield.fieldname + field_label = docfield.label or docfield.fieldname + + if docfield.fieldname in method_set: + conflict_type = "controller method" + if docfield.fieldname in property_set and not docfield.is_virtual: + conflict_type = "class property" + + if conflict_type: + xhiveframework.throw( + _("Fieldname '{0}' conflicting with a {1} of the name {2} in {3}").format( + field_label, conflict_type, field, self.name + ) + ) + + def set_defaults_for_single_and_table(self): + if self.issingle: + self.allow_import = 0 + self.is_submittable = 0 + self.istable = 0 + + elif self.istable: + self.allow_import = 0 + self.permissions = [] + + def set_defaults_for_autoincremented(self): + if self.autoname and self.autoname == "autoincrement": + self.allow_rename = 0 + + def set_default_in_list_view(self): + """Set default in-list-view for first 4 mandatory fields""" + not_allowed_in_list_view = get_fields_not_allowed_in_list_view(self.meta) + + if not [d.fieldname for d in self.fields if d.in_list_view]: + cnt = 0 + for d in self.fields: + if d.reqd and not d.hidden and d.fieldtype not in not_allowed_in_list_view: + d.in_list_view = 1 + cnt += 1 + if cnt == 4: + break + + def set_default_translatable(self): + """Ensure that non-translatable never will be translatable""" + for d in self.fields: + if d.translatable and not supports_translation(d.fieldtype): + d.translatable = 0 + + def check_indexing_for_dashboard_links(self): + """Enable indexing for outgoing links used in dashboard""" + for d in self.fields: + if d.fieldtype == "Link" and not (d.unique or d.search_index): + referred_as_link = xhiveframework.db.exists( + "DocType Link", + {"parent": d.options, "link_doctype": self.name, "link_fieldname": d.fieldname}, + ) + if not referred_as_link: + continue + + xhiveframework.msgprint( + _("{0} should be indexed because it's referred in dashboard connections").format( + _(d.label, context=d.parent) + ), + alert=True, + indicator="orange", + ) + + def check_developer_mode(self): + """Throw exception if not developer mode or via patch""" + if xhiveframework.flags.in_patch or xhiveframework.flags.in_test: + return + + if not xhiveframework.conf.get("developer_mode") and not self.custom: + xhiveframework.throw( + _("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), + CannotCreateStandardDoctypeError, + ) + + if self.is_virtual and self.custom: + xhiveframework.throw(_("Not allowed to create custom Virtual DocType."), CannotCreateStandardDoctypeError) + + if xhiveframework.conf.get("developer_mode"): + self.owner = "Administrator" + self.modified_by = "Administrator" + + def setup_fields_to_fetch(self): + """Setup query to update values for newly set fetch values""" + try: + old_meta = xhiveframework.get_meta(xhiveframework.get_doc("DocType", self.name), cached=False) + old_fields_to_fetch = [df.fieldname for df in old_meta.get_fields_to_fetch()] + except xhiveframework.DoesNotExistError: + old_fields_to_fetch = [] + + new_meta = xhiveframework.get_meta(self, cached=False) + + self.flags.update_fields_to_fetch_queries = [] + + new_fields_to_fetch = [df for df in new_meta.get_fields_to_fetch()] + + if set(old_fields_to_fetch) != {df.fieldname for df in new_fields_to_fetch}: + for df in new_fields_to_fetch: + if df.fieldname not in old_fields_to_fetch: + link_fieldname, source_fieldname = df.fetch_from.split(".", 1) + if not source_fieldname: + continue # Invalid expression + link_df = new_meta.get_field(link_fieldname) + + if xhiveframework.db.db_type == "postgres": + update_query = """ + UPDATE `tab{doctype}` + SET `{fieldname}` = source.`{source_fieldname}` + FROM `tab{link_doctype}` as source + WHERE `{link_fieldname}` = source.name + AND ifnull(`{fieldname}`, '')='' + """ + else: + update_query = """ + UPDATE `tab{doctype}` as target + INNER JOIN `tab{link_doctype}` as source + ON `target`.`{link_fieldname}` = `source`.`name` + SET `target`.`{fieldname}` = `source`.`{source_fieldname}` + WHERE ifnull(`target`.`{fieldname}`, '')="" + """ + + self.flags.update_fields_to_fetch_queries.append( + update_query.format( + link_doctype=link_df.options, + source_fieldname=source_fieldname, + doctype=self.name, + fieldname=df.fieldname, + link_fieldname=link_fieldname, + ) + ) + + def update_fields_to_fetch(self): + """Update fetch values based on queries setup""" + if self.flags.update_fields_to_fetch_queries and not self.issingle: + for query in self.flags.update_fields_to_fetch_queries: + xhiveframework.db.sql(query) + + def validate_document_type(self): + if self.document_type == "Transaction": + self.document_type = "Document" + if self.document_type == "Master": + self.document_type = "Setup" + + def validate_website(self): + """Ensure that website generator has field 'route'""" + if self.route: + self.route = self.route.strip("/") + + if self.has_web_view: + # route field must be present + if "route" not in [d.fieldname for d in self.fields]: + xhiveframework.throw(_('Field "route" is mandatory for Web Views'), title="Missing Field") + + # clear website cache + clear_cache() + + def validate_virtual_doctype_methods(self): + if not self.get("is_virtual") or self.is_new(): + return + + from xhiveframework.model.virtual_doctype import validate_controller + + validate_controller(self.name) + + def ensure_minimum_max_attachment_limit(self): + """Ensure that max_attachments is *at least* bigger than number of attach fields.""" + from xhiveframework.model import attachment_fieldtypes + + if not self.max_attachments: + return + + total_attach_fields = len([d for d in self.fields if d.fieldtype in attachment_fieldtypes]) + if total_attach_fields > self.max_attachments: + self.max_attachments = total_attach_fields + field_label = xhiveframework.bold(self.meta.get_field("max_attachments").label) + xhiveframework.msgprint( + _("Number of attachment fields are more than {}, limit updated to {}.").format( + field_label, total_attach_fields + ), + title=_("Insufficient attachment limit"), + alert=True, + ) + + def change_modified_of_parent(self): + """Change the timestamp of parent DocType if the current one is a child to clear caches.""" + if xhiveframework.flags.in_import: + return + parent_list = xhiveframework.get_all( + "DocField", "parent", dict(fieldtype=["in", xhiveframework.model.table_fields], options=self.name) + ) + for p in parent_list: + xhiveframework.db.set_value("DocType", p.parent, {}) + + def scrub_field_names(self): + """Sluggify fieldnames if not set from Label.""" + restricted = ( + "name", + "parent", + "creation", + "owner", + "modified", + "modified_by", + "parentfield", + "parenttype", + "file_list", + "flags", + "docstatus", + ) + for d in self.get("fields"): + if d.fieldtype: + if not getattr(d, "fieldname", None): + if d.label: + d.fieldname = d.label.strip().lower().replace(" ", "_").strip("?") + if d.fieldname in restricted: + d.fieldname = d.fieldname + "1" + if d.fieldtype == "Section Break": + d.fieldname = d.fieldname + "_section" + elif d.fieldtype == "Column Break": + d.fieldname = d.fieldname + "_column" + elif d.fieldtype == "Tab Break": + d.fieldname = d.fieldname + "_tab" + elif d.fieldtype in ("Section Break", "Column Break", "Tab Break"): + d.fieldname = d.fieldtype.lower().replace(" ", "_") + "_" + str(random_string(4)) + else: + xhiveframework.throw( + _("Row #{}: Fieldname is required").format(d.idx), title="Missing Fieldname" + ) + else: + if d.fieldname in restricted: + xhiveframework.throw( + _("Fieldname {0} is restricted").format(d.fieldname), InvalidFieldNameError + ) + d.fieldname = ILLEGAL_FIELDNAME_PATTERN.sub("", d.fieldname) + + # fieldnames should be lowercase + d.fieldname = d.fieldname.lower() + + # unique is automatically an index + if d.unique: + d.search_index = 0 + + def on_update(self): + """Update database schema, make controller templates if `custom` is not set and clear cache.""" + + if self.get("can_change_name_type"): + self.setup_autoincrement_and_sequence() + + try: + xhiveframework.db.updatedb(self.name, Meta(self)) + except Exception as e: + print(f"\n\nThere was an issue while migrating the DocType: {self.name}\n") + raise e + + self.change_modified_of_parent() + make_module_and_roles(self) + + self.update_fields_to_fetch() + + allow_doctype_export = ( + not self.custom + and not xhiveframework.flags.in_import + and (xhiveframework.conf.developer_mode or xhiveframework.flags.allow_doctype_export) + ) + if allow_doctype_export: + self.export_doc() + self.make_controller_template() + self.set_base_class_for_controller() + self.export_types_to_controller() + + # update index + if not self.custom: + self.run_module_method("on_doctype_update") + if self.flags.in_insert: + self.run_module_method("after_doctype_insert") + + self.sync_doctype_layouts() + delete_notification_count_for(doctype=self.name) + + xhiveframework.clear_cache(doctype=self.name) + + # clear user cache so that on the next reload this doctype is included in boot + clear_user_cache(xhiveframework.session.user) + + if not xhiveframework.flags.in_install and hasattr(self, "before_update"): + self.sync_global_search() + + clear_linked_doctype_cache() + + @savepoint(catch=Exception) + def sync_doctype_layouts(self): + """Sync Doctype Layout""" + doctype_layouts = xhiveframework.get_all( + "DocType Layout", filters={"document_type": self.name}, pluck="name", ignore_ddl=True + ) + for layout in doctype_layouts: + layout_doc = xhiveframework.get_doc("DocType Layout", layout) + layout_doc.sync_fields() + layout_doc.save() + + def setup_autoincrement_and_sequence(self): + """Changes name type and makes sequence on change (if required)""" + + name_type = f"varchar({xhiveframework.db.VARCHAR_LEN})" + + if self.autoname == "autoincrement": + name_type = "bigint" + xhiveframework.db.create_sequence(self.name, check_not_exists=True) + + change_name_column_type(self.name, name_type) + + def sync_global_search(self): + """If global search settings are changed, rebuild search properties for this table""" + global_search_fields_before_update = [ + d.fieldname for d in self.before_update.fields if d.in_global_search + ] + if self.before_update.show_name_in_global_search: + global_search_fields_before_update.append("name") + + global_search_fields_after_update = [d.fieldname for d in self.fields if d.in_global_search] + if self.show_name_in_global_search: + global_search_fields_after_update.append("name") + + if set(global_search_fields_before_update) != set(global_search_fields_after_update): + now = (not xhiveframework.request) or xhiveframework.flags.in_test or xhiveframework.flags.in_install + xhiveframework.enqueue("xhiveframework.utils.global_search.rebuild_for_doctype", now=now, doctype=self.name) + + def set_base_class_for_controller(self): + """If DocType.has_web_view has been changed, updates the controller class and import + from `WebsiteGenertor` to `Document` or viceversa""" + + if not self.has_value_changed("has_web_view"): + return + + despaced_name = self.name.replace(" ", "_") + scrubbed_name = xhiveframework.scrub(self.name) + scrubbed_module = xhiveframework.scrub(self.module) + controller_path = xhiveframework.get_module_path( + scrubbed_module, "doctype", scrubbed_name, f"{scrubbed_name}.py" + ) + + document_cls_tag = f"class {despaced_name}(Document)" + document_import_tag = "from xhiveframework.model.document import Document" + website_generator_cls_tag = f"class {despaced_name}(WebsiteGenerator)" + website_generator_import_tag = "from xhiveframework.website.website_generator import WebsiteGenerator" + + with open(controller_path) as f: + code = f.read() + updated_code = code + + is_website_generator_class = all( + [website_generator_cls_tag in code, website_generator_import_tag in code] + ) + + if self.has_web_view and not is_website_generator_class: + updated_code = updated_code.replace(document_import_tag, website_generator_import_tag).replace( + document_cls_tag, website_generator_cls_tag + ) + elif not self.has_web_view and is_website_generator_class: + updated_code = updated_code.replace(website_generator_import_tag, document_import_tag).replace( + website_generator_cls_tag, document_cls_tag + ) + + if updated_code != code: + with open(controller_path, "w") as f: + f.write(updated_code) + + def run_module_method(self, method): + from xhiveframework.modules import load_doctype_module + + module = load_doctype_module(self.name, self.module) + if hasattr(module, method): + getattr(module, method)() + + def before_rename(self, old, new, merge=False): + """Throw exception if merge. DocTypes cannot be merged.""" + if not self.custom and xhiveframework.session.user != "Administrator": + xhiveframework.throw(_("DocType can only be renamed by Administrator")) + + self.check_developer_mode() + self.validate_name(new) + + if merge: + xhiveframework.throw(_("DocType can not be merged")) + + def after_rename(self, old, new, merge=False): + """Change table name using `RENAME TABLE` if table exists. Or update + `doctype` property for Single type.""" + + if self.issingle: + xhiveframework.db.sql("""update tabSingles set doctype=%s where doctype=%s""", (new, old)) + xhiveframework.db.sql( + """update tabSingles set value=%s + where doctype=%s and field='name' and value = %s""", + (new, new, old), + ) + else: + xhiveframework.db.rename_table(old, new) + xhiveframework.db.commit() + + # Do not rename and move files and folders for custom doctype + if not self.custom: + if not xhiveframework.flags.in_patch: + self.rename_files_and_folders(old, new) + + clear_controller_cache(old) + + def after_delete(self): + if not self.custom: + clear_controller_cache(self.name) + + def rename_files_and_folders(self, old, new): + # move files + new_path = get_doc_path(self.module, "doctype", new) + old_path = get_doc_path(self.module, "doctype", old) + shutil.move(old_path, new_path) + + # rename files + for fname in os.listdir(new_path): + if xhiveframework.scrub(old) in fname: + old_file_name = os.path.join(new_path, fname) + new_file_name = os.path.join(new_path, fname.replace(xhiveframework.scrub(old), xhiveframework.scrub(new))) + shutil.move(old_file_name, new_file_name) + + self.rename_inside_controller(new, old, new_path) + xhiveframework.msgprint(_("Renamed files and replaced code in controllers, please check!")) + + def rename_inside_controller(self, new, old, new_path): + for fname in ("{}.js", "{}.py", "{}_list.js", "{}_calendar.js", "test_{}.py", "test_{}.js"): + fname = os.path.join(new_path, fname.format(xhiveframework.scrub(new))) + if os.path.exists(fname): + with open(fname) as f: + code = f.read() + with open(fname, "w") as f: + if fname.endswith(".js"): + file_content = code.replace(old, new) # replace str with full str (js controllers) + + elif fname.endswith(".py"): + file_content = code.replace( + xhiveframework.scrub(old), xhiveframework.scrub(new) + ) # replace str with _ (py imports) + file_content = file_content.replace( + old.replace(" ", ""), new.replace(" ", "") + ) # replace str (py controllers) + + f.write(file_content) + + # updating json file with new name + doctype_json_path = os.path.join(new_path, f"{xhiveframework.scrub(new)}.json") + current_data = xhiveframework.get_file_json(doctype_json_path) + current_data["name"] = new + + with open(doctype_json_path, "w") as f: + json.dump(current_data, f, indent=1) + + def before_reload(self): + """Preserve naming series changes in Property Setter.""" + if not (self.issingle and self.istable): + self.preserve_naming_series_options_in_property_setter() + + def preserve_naming_series_options_in_property_setter(self): + """Preserve naming_series as property setter if it does not exist""" + naming_series = self.get("fields", {"fieldname": "naming_series"}) + + if not naming_series: + return + + # check if atleast 1 record exists + if not ( + xhiveframework.db.table_exists(self.name) + and xhiveframework.get_all(self.name, fields=["name"], limit=1, as_list=True) + ): + return + + existing_property_setter = xhiveframework.db.get_value( + "Property Setter", {"doc_type": self.name, "property": "options", "field_name": "naming_series"} + ) + + if not existing_property_setter: + make_property_setter( + self.name, + "naming_series", + "options", + naming_series[0].options, + "Text", + validate_fields_for_doctype=False, + ) + if naming_series[0].default: + make_property_setter( + self.name, + "naming_series", + "default", + naming_series[0].default, + "Text", + validate_fields_for_doctype=False, + ) + + def before_export(self, docdict): + # remove null and empty fields + def remove_null_fields(o): + to_remove = [] + for attr, value in o.items(): + if isinstance(value, list): + for v in value: + remove_null_fields(v) + elif not value: + to_remove.append(attr) + + for attr in to_remove: + del o[attr] + + remove_null_fields(docdict) + + # retain order of 'fields' table and change order in 'field_order' + docdict["field_order"] = [f.fieldname for f in self.fields] + + if self.custom: + return + + path = get_file_path(self.module, "DocType", self.name) + if os.path.exists(path): + try: + with open(path) as txtfile: + olddoc = json.loads(txtfile.read()) + + old_field_names = [f["fieldname"] for f in olddoc.get("fields", [])] + if old_field_names: + new_field_dicts = [] + remaining_field_names = [f.fieldname for f in self.fields] + + for fieldname in old_field_names: + field_dict = [f for f in docdict["fields"] if f["fieldname"] == fieldname] + if field_dict: + new_field_dicts.append(field_dict[0]) + if fieldname in remaining_field_names: + remaining_field_names.remove(fieldname) + + for fieldname in remaining_field_names: + field_dict = [f for f in docdict["fields"] if f["fieldname"] == fieldname] + new_field_dicts.append(field_dict[0]) + + docdict["fields"] = new_field_dicts + except ValueError: + pass + + @staticmethod + def prepare_for_import(docdict): + # set order of fields from field_order + if docdict.get("field_order"): + new_field_dicts = [] + remaining_field_names = [f["fieldname"] for f in docdict.get("fields", [])] + + for fieldname in docdict.get("field_order"): + field_dict = [f for f in docdict.get("fields", []) if f["fieldname"] == fieldname] + if field_dict: + new_field_dicts.append(field_dict[0]) + if fieldname in remaining_field_names: + remaining_field_names.remove(fieldname) + + for fieldname in remaining_field_names: + field_dict = [f for f in docdict.get("fields", []) if f["fieldname"] == fieldname] + new_field_dicts.append(field_dict[0]) + + docdict["fields"] = new_field_dicts + + if "field_order" in docdict: + del docdict["field_order"] + + def export_doc(self): + """Export to standard folder `[module]/doctype/[name]/[name].json`.""" + from xhiveframework.modules.export_file import export_to_files + + export_to_files(record_list=[["DocType", self.name]], create_init=True) + + def make_controller_template(self): + """Make boilerplate controller template.""" + make_boilerplate("controller._py", self) + + if not self.istable: + make_boilerplate("test_controller._py", self.as_dict()) + make_boilerplate("controller.js", self.as_dict()) + # make_boilerplate("controller_list.js", self.as_dict()) + + if self.has_web_view: + templates_path = xhiveframework.get_module_path( + xhiveframework.scrub(self.module), "doctype", xhiveframework.scrub(self.name), "templates" + ) + if not os.path.exists(templates_path): + os.makedirs(templates_path) + make_boilerplate("templates/controller.html", self.as_dict()) + make_boilerplate("templates/controller_row.html", self.as_dict()) + + def export_types_to_controller(self): + from xhiveframework.modules.utils import get_module_app + from xhiveframework.types.exporter import TypeExporter + + try: + app = get_module_app(self.module) + except xhiveframework.DoesNotExistError: + return + + if any(xhiveframework.get_hooks("export_python_type_annotations", app_name=app)): + TypeExporter(self).export_types() + + def make_amendable(self): + """If is_submittable is set, add amended_from docfields.""" + if self.is_submittable: + docfield_exists = xhiveframework.get_all( + "DocField", filters={"fieldname": "amended_from", "parent": self.name}, pluck="name", limit=1 + ) + if not docfield_exists: + self.append( + "fields", + { + "label": "Amended From", + "fieldtype": "Link", + "fieldname": "amended_from", + "options": self.name, + "read_only": 1, + "print_hide": 1, + "no_copy": 1, + "search_index": 1, + }, + ) + + def make_repeatable(self): + """If allow_auto_repeat is set, add auto_repeat custom field.""" + if self.allow_auto_repeat: + if not xhiveframework.db.exists( + "Custom Field", {"fieldname": "auto_repeat", "dt": self.name} + ) and not xhiveframework.db.exists("DocField", {"fieldname": "auto_repeat", "parent": self.name}): + insert_after = self.fields[len(self.fields) - 1].fieldname + df = dict( + fieldname="auto_repeat", + label="Auto Repeat", + fieldtype="Link", + options="Auto Repeat", + insert_after=insert_after, + read_only=1, + no_copy=1, + print_hide=1, + ) + create_custom_field(self.name, df) + + def validate_nestedset(self): + if not self.get("is_tree"): + return + self.add_nestedset_fields() + + if not self.nsm_parent_field: + field_label = xhiveframework.bold(_("Parent Field (Tree)")) + xhiveframework.throw(_("{0} is a mandatory field").format(field_label), xhiveframework.MandatoryError) + + # check if field is valid + fieldnames = [df.fieldname for df in self.fields] + if self.nsm_parent_field and self.nsm_parent_field not in fieldnames: + xhiveframework.throw(_("Parent Field must be a valid fieldname"), InvalidFieldNameError) + + def add_nestedset_fields(self): + """If is_tree is set, add parent_field, lft, rgt, is_group fields.""" + fieldnames = [df.fieldname for df in self.fields] + if "lft" in fieldnames: + return + + self.append( + "fields", + { + "label": "Left", + "fieldtype": "Int", + "fieldname": "lft", + "read_only": 1, + "hidden": 1, + "no_copy": 1, + }, + ) + + self.append( + "fields", + { + "label": "Right", + "fieldtype": "Int", + "fieldname": "rgt", + "read_only": 1, + "hidden": 1, + "no_copy": 1, + }, + ) + + self.append("fields", {"label": "Is Group", "fieldtype": "Check", "fieldname": "is_group"}) + self.append( + "fields", + {"label": "Old Parent", "fieldtype": "Link", "options": self.name, "fieldname": "old_parent"}, + ) + + parent_field_label = f"Parent {self.name}" + parent_field_name = xhiveframework.scrub(parent_field_label) + self.append( + "fields", + { + "label": parent_field_label, + "fieldtype": "Link", + "options": self.name, + "fieldname": parent_field_name, + "ignore_user_permissions": 1, + }, + ) + self.nsm_parent_field = parent_field_name + + def validate_child_table(self): + if not self.get("istable") or self.is_new() or self.get("is_virtual"): + # if the doctype is not a child table then return + # if the doctype is a new doctype and also a child table then + # don't move forward as it will be handled via schema + return + + self.add_child_table_fields() + + def add_child_table_fields(self): + from xhiveframework.database.schema import add_column + + add_column(self.name, "parent", "Data") + add_column(self.name, "parenttype", "Data") + add_column(self.name, "parentfield", "Data") + + def get_max_idx(self): + """Returns the highest `idx`""" + max_idx = xhiveframework.db.sql("""select max(idx) from `tabDocField` where parent = %s""", self.name) + return max_idx and max_idx[0][0] or 0 + + def validate_name(self, name=None): + if not name: + name = self.name + + # a Doctype name is the tablename created in database + # `tab` the length of tablename is limited to 64 characters + max_length = xhiveframework.db.MAX_COLUMN_LENGTH - 3 + if len(name) > max_length: + # length(tab + ) should be equal to 64 characters hence doctype should be 61 characters + xhiveframework.throw( + _("Doctype name is limited to {0} characters ({1})").format(max_length, name), + xhiveframework.NameError, + ) + + # a DocType name should not start or end with an empty space + if WHITESPACE_PADDING_PATTERN.search(name): + xhiveframework.throw(_("DocType's name should not start or end with whitespace"), xhiveframework.NameError) + + # a DocType's name should not start with a number or underscore + # and should only contain letters, numbers, underscore, and hyphen + if not START_WITH_LETTERS_PATTERN.match(name): + xhiveframework.throw( + _( + "A DocType's name should start with a letter and can only " + "consist of letters, numbers, spaces, underscores and hyphens" + ), + xhiveframework.NameError, + title="Invalid Name", + ) + + validate_route_conflict(self.doctype, self.name) + + +def validate_series(dt, autoname=None, name=None): + """Validate if `autoname` property is correctly set.""" + if not autoname: + autoname = dt.autoname + if not name: + name = dt.name + + if not autoname and dt.get("fields", {"fieldname": "naming_series"}): + dt.autoname = "naming_series:" + elif dt.autoname and dt.autoname.startswith("naming_series:"): + fieldname = dt.autoname.split("naming_series:", 1)[0] or "naming_series" + if not dt.get("fields", {"fieldname": fieldname}): + xhiveframework.throw( + _("Fieldname called {0} must exist to enable autonaming").format(xhiveframework.bold(fieldname)), + title=_("Field Missing"), + ) + + # validate field name if autoname field:fieldname is used + # Create unique index on autoname field automatically. + if autoname and autoname.startswith("field:"): + field = autoname.split(":")[1] + if not field or field not in [df.fieldname for df in dt.fields]: + xhiveframework.throw(_("Invalid fieldname '{0}' in autoname").format(field)) + else: + for df in dt.fields: + if df.fieldname == field: + df.unique = 1 + break + + if ( + autoname + and (not autoname.startswith("field:")) + and (not autoname.startswith("eval:")) + and (autoname.lower() not in ("prompt", "hash")) + and (not autoname.startswith("naming_series:")) + and (not autoname.startswith("format:")) + ): + prefix = autoname.split(".", 1)[0] + doctype = xhiveframework.qb.DocType("DocType") + used_in = ( + xhiveframework.qb.from_(doctype) + .select(doctype.name) + .where(doctype.autoname.like(Concat(prefix, ".%"))) + .where(doctype.name != name) + ).run() + if used_in: + xhiveframework.throw(_("Series {0} already used in {1}").format(prefix, used_in[0][0])) + + +def validate_autoincrement_autoname(dt: Union[DocType, "CustomizeForm"]) -> bool: + """Checks if can doctype can change to/from autoincrement autoname""" + + def get_autoname_before_save(dt: Union[DocType, "CustomizeForm"]) -> str: + if dt.doctype == "Customize Form": + property_value = xhiveframework.db.get_value( + "Property Setter", {"doc_type": dt.doc_type, "property": "autoname"}, "value" + ) + # initially no property setter is set, + # hence getting autoname value from the doctype itself + if not property_value: + return xhiveframework.db.get_value("DocType", dt.doc_type, "autoname") or "" + + return property_value + + return getattr(dt.get_doc_before_save(), "autoname", "") + + if not dt.is_new(): + autoname_before_save = get_autoname_before_save(dt) + is_autoname_autoincrement = dt.autoname == "autoincrement" + + if ( + is_autoname_autoincrement + and autoname_before_save != "autoincrement" + or (not is_autoname_autoincrement and autoname_before_save == "autoincrement") + ): + if dt.doctype == "Customize Form": + xhiveframework.throw(_("Cannot change to/from autoincrement autoname in Customize Form")) + + if xhiveframework.get_meta(dt.name).issingle: + return False + + if not xhiveframework.get_all(dt.name, limit=1): + # allow changing the column type if there is no data + return True + + xhiveframework.throw( + _("Can only change to/from Autoincrement naming rule when there is no data in the doctype") + ) + + return False + + +def change_name_column_type(doctype_name: str, type: str) -> None: + """Changes name column type""" + + args = ( + (doctype_name, "name", type, False, True) + if (xhiveframework.db.db_type == "postgres") + else (doctype_name, "name", type, True) + ) + + xhiveframework.db.change_column_type(*args) + + +def validate_links_table_fieldnames(meta): + """Validate fieldnames in Links table""" + if not meta.links or xhiveframework.flags.in_patch or xhiveframework.flags.in_fixtures or xhiveframework.flags.in_migrate: + return + + fieldnames = tuple(field.fieldname for field in meta.fields) + for index, link in enumerate(meta.links, 1): + _test_connection_query(doctype=link.link_doctype, field=link.link_fieldname, idx=index) + + if not link.is_child_table: + continue + + if not link.parent_doctype: + message = _("Document Links Row #{0}: Parent DocType is mandatory for internal links").format( + index + ) + xhiveframework.throw(message, xhiveframework.ValidationError, _("Parent Missing")) + + if not link.table_fieldname: + message = _("Document Links Row #{0}: Table Fieldname is mandatory for internal links").format( + index + ) + xhiveframework.throw(message, xhiveframework.ValidationError, _("Table Fieldname Missing")) + + if meta.name == link.parent_doctype: + field_exists = link.table_fieldname in fieldnames + else: + field_exists = xhiveframework.get_meta(link.parent_doctype).has_field(link.table_fieldname) + + if not field_exists: + message = _("Document Links Row #{0}: Could not find field {1} in {2} DocType").format( + index, xhiveframework.bold(link.table_fieldname), xhiveframework.bold(meta.name) + ) + xhiveframework.throw(message, xhiveframework.ValidationError, _("Invalid Table Fieldname")) + + +def _test_connection_query(doctype, field, idx): + """Make sure that connection can be queried. + + This function executes query similar to one that would be executed for + finding count on dashboard and hence validates if fieldname/doctype are + correct. + """ + filters = get_filters_for(doctype) or {} + filters[field] = "" + + try: + xhiveframework.get_all(doctype, filters=filters, limit=1, distinct=True, ignore_ifnull=True) + except Exception as e: + xhiveframework.clear_last_message() + msg = _("Document Links Row #{0}: Invalid doctype or fieldname.").format(idx) + msg += "
    " + str(e) + xhiveframework.throw(msg, InvalidFieldNameError) + + +def validate_fields_for_doctype(doctype): + meta = xhiveframework.get_meta(doctype, cached=False) + validate_links_table_fieldnames(meta) + validate_fields(meta) + + +# this is separate because it is also called via custom field +def validate_fields(meta: Meta): + """Validate doctype fields. Checks + 1. There are no illegal characters in fieldnames + 2. If fieldnames are unique. + 3. Validate column length. + 4. Fields that do have database columns are not mandatory. + 5. `Link` and `Table` options are valid. + 6. **Hidden** and **Mandatory** are not set simultaneously. + 7. `Check` type field has default as 0 or 1. + 8. `Dynamic Links` are correctly defined. + 9. Precision is set in numeric fields and is between 1 & 6. + 10. Fold is not at the end (if set). + 11. `search_fields` are valid. + 12. `title_field` and title field pattern are valid. + 13. `unique` check is only valid for Data, Link and Read Only fieldtypes. + 14. `unique` cannot be checked if there exist non-unique values. + + :param meta: `xhiveframework.model.meta.Meta` object to check.""" + + def check_illegal_characters(fieldname): + validate_column_name(fieldname) + + def check_invalid_fieldnames(docname, fieldname): + if fieldname in Document._reserved_keywords: + xhiveframework.throw( + _("{0}: fieldname cannot be set to reserved keyword {1}").format( + xhiveframework.bold(docname), + xhiveframework.bold(fieldname), + ), + title=_("Invalid Fieldname"), + ) + + def check_unique_fieldname(docname, fieldname): + duplicates = list( + filter(None, map(lambda df: df.fieldname == fieldname and str(df.idx) or None, fields)) + ) + if len(duplicates) > 1: + xhiveframework.throw( + _("{0}: Fieldname {1} appears multiple times in rows {2}").format( + docname, fieldname, ", ".join(duplicates) + ), + UniqueFieldnameError, + ) + + def check_fieldname_length(fieldname): + validate_column_length(fieldname) + + def check_illegal_mandatory(docname, d): + if (d.fieldtype in no_value_fields) and d.fieldtype not in table_fields and d.reqd: + xhiveframework.throw( + _("{0}: Field {1} of type {2} cannot be mandatory").format(docname, d.label, d.fieldtype), + IllegalMandatoryError, + ) + + def check_link_table_options(docname, d): + if xhiveframework.flags.in_patch or xhiveframework.flags.in_fixtures: + return + + if d.fieldtype in ("Link", *table_fields): + if not d.options: + xhiveframework.throw( + _("{0}: Options required for Link or Table type field {1} in row {2}").format( + docname, d.label, d.idx + ), + DoctypeLinkError, + ) + if d.options == "[Select]" or d.options == d.parent: + return + if d.options != d.parent: + options = xhiveframework.db.get_value("DocType", d.options, "name") + if not options: + xhiveframework.throw( + _("{0}: Options must be a valid DocType for field {1} in row {2}").format( + docname, d.label, d.idx + ), + WrongOptionsDoctypeLinkError, + ) + elif not (options == d.options): + xhiveframework.throw( + _("{0}: Options {1} must be the same as doctype name {2} for the field {3}").format( + docname, d.options, options, d.label + ), + DoctypeLinkError, + ) + else: + # fix case + d.options = options + + def check_hidden_and_mandatory(docname, d): + if d.hidden and d.reqd and not d.default and not xhiveframework.flags.in_migrate: + xhiveframework.throw( + _("{0}: Field {1} in row {2} cannot be hidden and mandatory without default").format( + docname, d.label, d.idx + ), + HiddenAndMandatoryWithoutDefaultError, + ) + + def check_width(d): + if d.fieldtype == "Currency" and cint(d.width) < 100: + xhiveframework.throw(_("Max width for type Currency is 100px in row {0}").format(d.idx)) + + def check_in_list_view(is_table, d): + if d.in_list_view and (d.fieldtype in not_allowed_in_list_view): + property_label = "In Grid View" if is_table else "In List View" + xhiveframework.throw( + _("'{0}' not allowed for type {1} in row {2}").format(property_label, d.fieldtype, d.idx) + ) + + def check_in_global_search(d): + if d.in_global_search and d.fieldtype in no_value_fields: + xhiveframework.throw( + _("'In Global Search' not allowed for type {0} in row {1}").format(d.fieldtype, d.idx) + ) + + def check_dynamic_link_options(d): + if d.fieldtype == "Dynamic Link": + doctype_pointer = list(filter(lambda df: df.fieldname == d.options, fields)) + if ( + not doctype_pointer + or (doctype_pointer[0].fieldtype not in ("Link", "Select")) + or (doctype_pointer[0].fieldtype == "Link" and doctype_pointer[0].options != "DocType") + ): + xhiveframework.throw( + _( + "Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'" + ) + ) + + def check_illegal_default(d): + if d.fieldtype == "Check" and not d.default: + d.default = "0" + if d.fieldtype == "Check" and cint(d.default) not in (0, 1): + xhiveframework.throw( + _("Default for 'Check' type of field {0} must be either '0' or '1'").format( + xhiveframework.bold(d.fieldname) + ) + ) + if d.fieldtype == "Select" and d.default: + if not d.options: + xhiveframework.throw( + _("Options for {0} must be set before setting the default value.").format( + xhiveframework.bold(d.fieldname) + ) + ) + elif d.default not in d.options.split("\n"): + xhiveframework.throw( + _("Default value for {0} must be in the list of options.").format( + xhiveframework.bold(d.fieldname) + ) + ) + + def check_precision(d): + if ( + d.fieldtype in ("Currency", "Float", "Percent") + and d.precision is not None + and not (1 <= cint(d.precision) <= 6) + ): + xhiveframework.throw(_("Precision should be between 1 and 6")) + + def check_unique_and_text(docname, d): + if meta.is_virtual: + return + + if meta.issingle: + d.unique = 0 + d.search_index = 0 + + if getattr(d, "unique", False): + if d.fieldtype not in ("Data", "Link", "Read Only", "Int"): + xhiveframework.throw( + _("{0}: Fieldtype {1} for {2} cannot be unique").format(docname, d.fieldtype, d.label), + NonUniqueError, + ) + + if not d.get("__islocal") and xhiveframework.db.has_column(d.parent, d.fieldname): + has_non_unique_values = xhiveframework.db.sql( + f"""select `{d.fieldname}`, count(*) + from `tab{d.parent}` where ifnull(`{d.fieldname}`, '') != '' + group by `{d.fieldname}` having count(*) > 1 limit 1""" + ) + + if has_non_unique_values and has_non_unique_values[0][0]: + xhiveframework.throw( + _("{0}: Field '{1}' cannot be set as Unique as it has non-unique values").format( + docname, d.label + ), + NonUniqueError, + ) + + if d.search_index and d.fieldtype in ("Text", "Long Text", "Small Text", "Code", "Text Editor"): + xhiveframework.throw( + _("{0}:Fieldtype {1} for {2} cannot be indexed").format(docname, d.fieldtype, d.label), + CannotIndexedError, + ) + + def check_fold(fields): + fold_exists = False + for i, f in enumerate(fields): + if f.fieldtype == "Fold": + if fold_exists: + xhiveframework.throw(_("There can be only one Fold in a form")) + fold_exists = True + if i < len(fields) - 1: + nxt = fields[i + 1] + if nxt.fieldtype != "Section Break": + xhiveframework.throw(_("Fold must come before a Section Break")) + else: + xhiveframework.throw(_("Fold can not be at the end of the form")) + + def check_search_fields(meta, fields): + """Throw exception if `search_fields` don't contain valid fields.""" + if not meta.search_fields: + return + + # No value fields should not be included in search field + search_fields = [field.strip() for field in (meta.search_fields or "").split(",")] + fieldtype_mapper = { + field.fieldname: field.fieldtype + for field in filter(lambda field: field.fieldname in search_fields, fields) + } + + for fieldname in search_fields: + fieldname = fieldname.strip() + if (fieldtype_mapper.get(fieldname) in no_value_fields) or (fieldname not in fieldname_list): + xhiveframework.throw(_("Search field {0} is not valid").format(fieldname)) + + def check_title_field(meta): + """Throw exception if `title_field` isn't a valid fieldname.""" + if not meta.get("title_field"): + return + + if meta.title_field not in fieldname_list: + xhiveframework.throw(_("Title field must be a valid fieldname"), InvalidFieldNameError) + + def _validate_title_field_pattern(pattern): + if not pattern: + return + + for fieldname in FIELD_PATTERN.findall(pattern): + if fieldname.startswith("{"): + # edge case when double curlies are used for escape + continue + + if fieldname not in fieldname_list: + xhiveframework.throw( + _("{{{0}}} is not a valid fieldname pattern. It should be {{field_name}}.").format( + fieldname + ), + InvalidFieldNameError, + ) + + df = meta.get("fields", filters={"fieldname": meta.title_field})[0] + if df: + _validate_title_field_pattern(df.options) + _validate_title_field_pattern(df.default) + + def check_image_field(meta): + '''check image_field exists and is of type "Attach Image"''' + if not meta.image_field: + return + + df = meta.get("fields", {"fieldname": meta.image_field}) + if not df: + xhiveframework.throw(_("Image field must be a valid fieldname"), InvalidFieldNameError) + if df[0].fieldtype != "Attach Image": + xhiveframework.throw(_("Image field must be of type Attach Image"), InvalidFieldNameError) + + def check_is_published_field(meta): + if not meta.is_published_field: + return + + if meta.is_published_field not in fieldname_list: + xhiveframework.throw(_("Is Published Field must be a valid fieldname"), InvalidFieldNameError) + + def check_website_search_field(meta): + if not meta.get("website_search_field"): + return + + if meta.website_search_field not in fieldname_list: + xhiveframework.throw(_("Website Search Field must be a valid fieldname"), InvalidFieldNameError) + + if "title" not in fieldname_list: + xhiveframework.throw( + _('Field "title" is mandatory if "Website Search Field" is set.'), title=_("Missing Field") + ) + + def check_timeline_field(meta): + if not meta.timeline_field: + return + + if meta.timeline_field not in fieldname_list: + xhiveframework.throw(_("Timeline field must be a valid fieldname"), InvalidFieldNameError) + + df = meta.get("fields", {"fieldname": meta.timeline_field})[0] + if df.fieldtype not in ("Link", "Dynamic Link"): + xhiveframework.throw(_("Timeline field must be a Link or Dynamic Link"), InvalidFieldNameError) + + def check_sort_field(meta): + """Validate that sort_field(s) is a valid field""" + if meta.sort_field: + sort_fields = [meta.sort_field] + if "," in meta.sort_field: + sort_fields = [d.split(maxsplit=1)[0] for d in meta.sort_field.split(",")] + + for fieldname in sort_fields: + if fieldname not in (fieldname_list + list(default_fields) + list(child_table_fields)): + xhiveframework.throw( + _("Sort field {0} must be a valid fieldname").format(fieldname), InvalidFieldNameError + ) + + def check_illegal_depends_on_conditions(docfield): + """assignment operation should not be allowed in the depends on condition.""" + depends_on_fields = [ + "depends_on", + "collapsible_depends_on", + "mandatory_depends_on", + "read_only_depends_on", + ] + for field in depends_on_fields: + depends_on = docfield.get(field, None) + if depends_on and ("=" in depends_on) and DEPENDS_ON_PATTERN.match(depends_on): + xhiveframework.throw(_("Invalid {0} condition").format(xhiveframework.unscrub(field)), xhiveframework.ValidationError) + + def check_table_multiselect_option(docfield): + """check if the doctype provided in Option has atleast 1 Link field""" + if not docfield.fieldtype == "Table MultiSelect": + return + + doctype = docfield.options + meta = xhiveframework.get_meta(doctype) + link_field = [df for df in meta.fields if df.fieldtype == "Link"] + + if not link_field: + xhiveframework.throw( + _( + "DocType {0} provided for the field {1} must have atleast one Link field" + ).format(doctype, docfield.fieldname), + xhiveframework.ValidationError, + ) + + def scrub_options_in_select(field): + """Strip options for whitespaces""" + + if field.fieldtype == "Select" and field.options is not None: + options_list = [] + for i, option in enumerate(field.options.split("\n")): + _option = option.strip() + if i == 0 or _option: + options_list.append(_option) + field.options = "\n".join(options_list) + + def scrub_fetch_from(field): + if hasattr(field, "fetch_from") and field.fetch_from: + field.fetch_from = field.fetch_from.strip("\n").strip() + + def validate_data_field_type(docfield): + if docfield.get("is_virtual"): + return + + if docfield.fieldtype == "Data" and not (docfield.oldfieldtype and docfield.oldfieldtype != "Data"): + if docfield.options and (docfield.options not in data_field_options): + df_str = xhiveframework.bold(_(docfield.label, context=docfield.parent)) + text_str = ( + _("{0} is an invalid Data field.").format(df_str) + + "
    " * 2 + + _("Only Options allowed for Data field are:") + + "
    " + ) + df_options_str = "
    • " + "
    • ".join(_(x) for x in data_field_options) + "
    " + + xhiveframework.msgprint(text_str + df_options_str, title="Invalid Data Field", alert=True) + + def check_child_table_option(docfield): + if xhiveframework.flags.in_fixtures: + return + if docfield.fieldtype not in ["Table MultiSelect", "Table"]: + return + + doctype = docfield.options + child_doctype_meta = xhiveframework.get_meta(doctype) + + if not child_doctype_meta.istable: + xhiveframework.throw( + _("Option {0} for field {1} is not a child table").format( + xhiveframework.bold(doctype), xhiveframework.bold(docfield.fieldname) + ), + title=_("Invalid Option"), + ) + + if not (meta.is_virtual == child_doctype_meta.is_virtual): + error_msg = " should be virtual." if meta.is_virtual else " cannot be virtual." + xhiveframework.throw( + _("Child Table {0} for field {1}" + error_msg).format( + xhiveframework.bold(doctype), xhiveframework.bold(docfield.fieldname) + ), + title=_("Invalid Option"), + ) + + def check_max_height(docfield): + if getattr(docfield, "max_height", None) and (docfield.max_height[-2:] not in ("px", "em")): + xhiveframework.throw(f"Max for {xhiveframework.bold(docfield.fieldname)} height must be in px, em, rem") + + def check_no_of_ratings(docfield): + if docfield.fieldtype == "Rating": + if docfield.options and (int(docfield.options) > 10 or int(docfield.options) < 3): + xhiveframework.throw(_("Options for Rating field can range from 3 to 10")) + + fields = meta.get("fields") + fieldname_list = [d.fieldname for d in fields] + + not_allowed_in_list_view = get_fields_not_allowed_in_list_view(meta) + + for d in fields: + if not d.permlevel: + d.permlevel = 0 + if d.fieldtype not in table_fields: + d.allow_bulk_edit = 0 + if not d.fieldname: + d.fieldname = d.fieldname.lower().strip("?") + + check_illegal_characters(d.fieldname) + check_invalid_fieldnames(meta.get("name"), d.fieldname) + check_fieldname_length(d.fieldname) + check_hidden_and_mandatory(meta.get("name"), d) + check_unique_and_text(meta.get("name"), d) + check_table_multiselect_option(d) + scrub_options_in_select(d) + scrub_fetch_from(d) + validate_data_field_type(d) + + if not xhiveframework.flags.in_migrate: + check_unique_fieldname(meta.get("name"), d.fieldname) + check_link_table_options(meta.get("name"), d) + check_illegal_mandatory(meta.get("name"), d) + check_dynamic_link_options(d) + check_in_list_view(meta.get("istable"), d) + check_in_global_search(d) + check_illegal_depends_on_conditions(d) + check_illegal_default(d) + check_child_table_option(d) + check_max_height(d) + check_no_of_ratings(d) + + if not xhiveframework.flags.in_migrate: + check_fold(fields) + check_search_fields(meta, fields) + check_title_field(meta) + check_timeline_field(meta) + check_is_published_field(meta) + check_website_search_field(meta) + check_sort_field(meta) + check_image_field(meta) + + +def get_fields_not_allowed_in_list_view(meta) -> list[str]: + not_allowed_in_list_view = list(copy.copy(no_value_fields)) + not_allowed_in_list_view.append("Attach Image") + if meta.istable: + not_allowed_in_list_view.remove("Button") + not_allowed_in_list_view.remove("HTML") + return not_allowed_in_list_view + + +def validate_permissions_for_doctype(doctype, for_remove=False, alert=False): + """Validates if permissions are set correctly.""" + doctype = xhiveframework.get_doc("DocType", doctype) + validate_permissions(doctype, for_remove, alert=alert) + + # save permissions + for perm in doctype.get("permissions"): + perm.db_update() + + clear_permissions_cache(doctype.name) + + +def clear_permissions_cache(doctype): + from xhiveframework.cache_manager import clear_user_cache + + xhiveframework.clear_cache(doctype=doctype) + delete_notification_count_for(doctype) + + clear_user_cache() + + +def validate_permissions(doctype, for_remove=False, alert=False): + permissions = doctype.get("permissions") + # Some DocTypes may not have permissions by default, don't show alert for them + if not permissions and alert: + xhiveframework.msgprint(_("No Permissions Specified"), alert=True, indicator="orange") + issingle = issubmittable = isimportable = False + if doctype: + issingle = cint(doctype.issingle) + issubmittable = cint(doctype.is_submittable) + isimportable = cint(doctype.allow_import) + + def get_txt(d): + return _("For {0} at level {1} in {2} in row {3}").format(d.role, d.permlevel, d.parent, d.idx) + + def check_atleast_one_set(d): + if not d.select and not d.read and not d.write and not d.submit and not d.cancel and not d.create: + xhiveframework.throw(_("{0}: No basic permissions set").format(get_txt(d))) + + def check_double(d): + has_similar = False + similar_because_of = "" + for p in permissions: + if p.role == d.role and p.permlevel == d.permlevel and p != d: + if p.if_owner == d.if_owner: + similar_because_of = _("If Owner") + has_similar = True + break + + if has_similar: + xhiveframework.throw( + _("{0}: Only one rule allowed with the same Role, Level and {1}").format( + get_txt(d), similar_because_of + ) + ) + + def check_level_zero_is_set(d): + if cint(d.permlevel) > 0 and d.role not in (ALL_USER_ROLE, SYSTEM_USER_ROLE): + has_zero_perm = False + for p in permissions: + if p.role == d.role and (p.permlevel or 0) == 0 and p != d: + has_zero_perm = True + break + + if not has_zero_perm: + xhiveframework.throw( + _("{0}: Permission at level 0 must be set before higher levels are set").format( + get_txt(d) + ) + ) + + for invalid in ("create", "submit", "cancel", "amend"): + if d.get(invalid): + d.set(invalid, 0) + + def check_permission_dependency(d): + if d.cancel and not d.submit: + xhiveframework.throw(_("{0}: Cannot set Cancel without Submit").format(get_txt(d))) + + if (d.submit or d.cancel or d.amend) and not d.write: + xhiveframework.throw(_("{0}: Cannot set Submit, Cancel, Amend without Write").format(get_txt(d))) + if d.amend and not d.write: + xhiveframework.throw(_("{0}: Cannot set Amend without Cancel").format(get_txt(d))) + if d.get("import") and not d.create: + xhiveframework.throw(_("{0}: Cannot set Import without Create").format(get_txt(d))) + + def remove_rights_for_single(d): + if not issingle: + return + + if d.report: + xhiveframework.msgprint(_("Report cannot be set for Single types")) + d.report = 0 + d.set("import", 0) + d.set("export", 0) + + def check_if_submittable(d): + if d.submit and not issubmittable: + xhiveframework.throw(_("{0}: Cannot set Assign Submit if not Submittable").format(get_txt(d))) + elif d.amend and not issubmittable: + xhiveframework.throw(_("{0}: Cannot set Assign Amend if not Submittable").format(get_txt(d))) + + def check_if_importable(d): + if d.get("import") and not isimportable: + xhiveframework.throw(_("{0}: Cannot set import as {1} is not importable").format(get_txt(d), doctype)) + + def validate_permission_for_all_role(d): + if xhiveframework.session.user == "Administrator": + return + + if doctype.custom: + if d.role in AUTOMATIC_ROLES: + xhiveframework.throw( + _( + "Row # {0}: Non administrator user can not set the role {1} to the custom doctype" + ).format(d.idx, xhiveframework.bold(_(d.role))), + title=_("Permissions Error"), + ) + + roles = [row.name for row in xhiveframework.get_all("Role", filters={"is_custom": 1})] + + if d.role in roles: + xhiveframework.throw( + _( + "Row # {0}: Non administrator user can not set the role {1} to the custom doctype" + ).format(d.idx, xhiveframework.bold(_(d.role))), + title=_("Permissions Error"), + ) + + for d in permissions: + if not d.permlevel: + d.permlevel = 0 + check_atleast_one_set(d) + if not for_remove: + check_double(d) + check_permission_dependency(d) + check_if_submittable(d) + check_if_importable(d) + check_level_zero_is_set(d) + remove_rights_for_single(d) + validate_permission_for_all_role(d) + + +def make_module_and_roles(doc, perm_fieldname="permissions"): + """Make `Module Def` and `Role` records if already not made. Called while installing.""" + try: + if ( + hasattr(doc, "restrict_to_domain") + and doc.restrict_to_domain + and not xhiveframework.db.exists("Domain", doc.restrict_to_domain) + ): + xhiveframework.get_doc(dict(doctype="Domain", domain=doc.restrict_to_domain)).insert() + + if "tabModule Def" in xhiveframework.db.get_tables() and not xhiveframework.db.exists("Module Def", doc.module): + m = xhiveframework.get_doc({"doctype": "Module Def", "module_name": doc.module}) + if xhiveframework.scrub(doc.module) in xhiveframework.local.module_app: + m.app_name = xhiveframework.local.module_app[xhiveframework.scrub(doc.module)] + else: + m.app_name = "xhiveframework" + m.flags.ignore_mandatory = m.flags.ignore_permissions = True + if xhiveframework.flags.package: + m.package = xhiveframework.flags.package.name + m.custom = 1 + m.insert() + + roles = [p.role for p in doc.get("permissions") or []] + list(AUTOMATIC_ROLES) + + for role in list(set(roles)): + if xhiveframework.db.table_exists("Role", cached=False) and not xhiveframework.db.exists("Role", role): + r = xhiveframework.new_doc("Role") + r.role_name = role + r.desk_access = 1 + r.flags.ignore_mandatory = r.flags.ignore_permissions = True + r.insert() + except xhiveframework.DoesNotExistError: + pass + except xhiveframework.db.ProgrammingError as e: + if xhiveframework.db.is_table_missing(e): + pass + else: + raise + + +def check_fieldname_conflicts(docfield): + """Checks if fieldname conflicts with methods or properties""" + doc = xhiveframework.get_doc({"doctype": docfield.dt}) + available_objects = [x for x in dir(doc) if isinstance(x, str)] + property_list = [x for x in available_objects if is_a_property(getattr(type(doc), x, None))] + method_list = [x for x in available_objects if x not in property_list and callable(getattr(doc, x))] + msg = _("Fieldname {0} conflicting with meta object").format(docfield.fieldname) + + if docfield.fieldname in method_list + property_list: + xhiveframework.msgprint(msg, raise_exception=not docfield.is_virtual) + + +def clear_linked_doctype_cache(): + xhiveframework.cache.delete_value("linked_doctypes_without_ignore_user_permissions_enabled") + + +def check_email_append_to(doc): + if not hasattr(doc, "email_append_to") or not doc.email_append_to: + return + + # Subject Field + doc.subject_field = doc.subject_field.strip() if doc.subject_field else None + subject_field = get_field(doc, doc.subject_field) + + if doc.subject_field and not subject_field: + xhiveframework.throw(_("Select a valid Subject field for creating documents from Email")) + + if subject_field and subject_field.fieldtype not in [ + "Data", + "Text", + "Long Text", + "Small Text", + "Text Editor", + ]: + xhiveframework.throw(_("Subject Field type should be Data, Text, Long Text, Small Text, Text Editor")) + + # Sender Field is mandatory + doc.sender_field = doc.sender_field.strip() if doc.sender_field else None + sender_field = get_field(doc, doc.sender_field) + + if doc.sender_field and not sender_field: + xhiveframework.throw(_("Select a valid Sender Field for creating documents from Email")) + + if not sender_field.options == "Email": + xhiveframework.throw(_("Sender Field should have Email in options")) + + +def get_field(doc, fieldname): + if not (doc or fieldname): + return + + for field in doc.fields: + if field.fieldname == fieldname: + return field + + +@xhiveframework.whitelist() +def get_row_size_utilization(doctype: str) -> float: + """Get row size utilization in percentage""" + + xhiveframework.has_permission("DocType", throw=True) + try: + return flt(xhiveframework.db.get_row_size(doctype) / xhiveframework.db.MAX_ROW_SIZE_LIMIT * 100, 2) + except Exception: + return 0.0 diff --git a/xhiveframework/core/doctype/doctype/doctype_list.js b/xhiveframework/core/doctype/doctype/doctype_list.js new file mode 100644 index 0000000..5f0395f --- /dev/null +++ b/xhiveframework/core/doctype/doctype/doctype_list.js @@ -0,0 +1,123 @@ +xhiveframework.listview_settings["DocType"] = { + primary_action: function () { + this.new_doctype_dialog(); + }, + + new_doctype_dialog() { + let non_developer = xhiveframework.session.user !== "Administrator" || !xhiveframework.boot.developer_mode; + let fields = [ + { + label: __("DocType Name"), + fieldname: "name", + fieldtype: "Data", + reqd: 1, + }, + { fieldtype: "Column Break" }, + { + label: __("Module"), + fieldname: "module", + fieldtype: "Link", + options: "Module Def", + reqd: 1, + }, + { fieldtype: "Section Break" }, + { + label: __("Is Submittable"), + fieldname: "is_submittable", + fieldtype: "Check", + description: __( + "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended." + ), + depends_on: "eval:!doc.istable && !doc.issingle", + }, + { + label: __("Is Child Table"), + fieldname: "istable", + fieldtype: "Check", + description: __("Child Tables are shown as a Grid in other DocTypes"), + depends_on: "eval:!doc.is_submittable && !doc.issingle", + }, + { + label: __("Editable Grid"), + fieldname: "editable_grid", + fieldtype: "Check", + depends_on: "istable", + default: 1, + }, + { + label: __("Is Single"), + fieldname: "issingle", + fieldtype: "Check", + description: __( + "Single Types have only one record no tables associated. Values are stored in tabSingles" + ), + depends_on: "eval:!doc.istable && !doc.is_submittable", + }, + { + label: "Is Tree", + fieldname: "is_tree", + fieldtype: "Check", + default: "0", + depends_on: "eval:!doc.istable", + description: "Tree structures are implemented using Nested Set", + }, + { + label: __("Custom?"), + fieldname: "custom", + fieldtype: "Check", + default: non_developer, + read_only: non_developer, + }, + ]; + + if (!non_developer) { + fields.push({ + label: "Is Virtual", + fieldname: "is_virtual", + fieldtype: "Check", + default: "0", + }); + } + + let new_d = new xhiveframework.ui.Dialog({ + title: __("Create New DocType"), + fields: fields, + primary_action_label: __("Create & Continue"), + primary_action(values) { + if (!values.istable) values.editable_grid = 0; + xhiveframework.db + .insert({ + doctype: "DocType", + ...values, + permissions: [ + { + create: 1, + delete: 1, + email: 1, + export: 1, + print: 1, + read: 1, + report: 1, + role: "System Manager", + share: 1, + write: 1, + submit: values.is_submittable ? 1 : 0, + }, + ], + fields: [{ fieldtype: "Section Break" }], + }) + .then((doc) => { + xhiveframework.set_route("Form", "DocType", doc.name); + }); + }, + secondary_action_label: __("Cancel"), + secondary_action() { + new_d.hide(); + if (xhiveframework.get_route()[0] === "Form") { + xhiveframework.set_route("List", "DocType"); + } + }, + }); + new_d.show(); + }, +}; diff --git a/xhiveframework/core/doctype/doctype/patches/set_route.py b/xhiveframework/core/doctype/doctype/patches/set_route.py new file mode 100644 index 0000000..bdbb6cc --- /dev/null +++ b/xhiveframework/core/doctype/doctype/patches/set_route.py @@ -0,0 +1,8 @@ +import xhiveframework +from xhiveframework.desk.utils import slug + + +def execute(): + for doctype in xhiveframework.get_all("DocType", ["name", "route"], dict(istable=0)): + if not doctype.route: + xhiveframework.db.set_value("DocType", doctype.name, "route", slug(doctype.name), update_modified=False) diff --git a/xhiveframework/core/doctype/doctype/test_doctype.py b/xhiveframework/core/doctype/doctype/test_doctype.py new file mode 100644 index 0000000..86cee16 --- /dev/null +++ b/xhiveframework/core/doctype/doctype/test_doctype.py @@ -0,0 +1,819 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import os +import random +import string +import unittest +from unittest.mock import patch + +import xhiveframework +from xhiveframework.cache_manager import clear_doctype_cache +from xhiveframework.core.doctype.doctype.doctype import ( + CannotIndexedError, + DoctypeLinkError, + HiddenAndMandatoryWithoutDefaultError, + IllegalMandatoryError, + InvalidFieldNameError, + UniqueFieldnameError, + WrongOptionsDoctypeLinkError, + validate_links_table_fieldnames, +) +from xhiveframework.core.doctype.rq_job.test_rq_job import wait_for_completion +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_fields +from xhiveframework.desk.form.load import getdoc +from xhiveframework.model.delete_doc import delete_controllers +from xhiveframework.model.sync import remove_orphan_doctypes +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocType(XhiveFrameworkTestCase): + def tearDown(self): + xhiveframework.db.rollback() + + def test_validate_name(self): + self.assertRaises(xhiveframework.NameError, new_doctype("_Some DocType").insert) + self.assertRaises(xhiveframework.NameError, new_doctype("8Some DocType").insert) + self.assertRaises(xhiveframework.NameError, new_doctype("Some (DocType)").insert) + self.assertRaises( + xhiveframework.NameError, + new_doctype("Some Doctype with a name whose length is more than 61 characters").insert, + ) + for name in ("Some DocType", "Some_DocType", "Some-DocType"): + if xhiveframework.db.exists("DocType", name): + xhiveframework.delete_doc("DocType", name) + + doc = new_doctype(name).insert() + doc.delete() + + def test_making_sequence_on_change(self): + xhiveframework.delete_doc_if_exists("DocType", self._testMethodName) + dt = new_doctype(self._testMethodName).insert(ignore_permissions=True) + autoname = dt.autoname + + # change autoname + dt.autoname = "autoincrement" + dt.save() + + # check if name type has been changed + self.assertEqual( + xhiveframework.db.sql( + f"""select data_type FROM information_schema.columns + where column_name = 'name' and table_name = 'tab{self._testMethodName}'""" + )[0][0], + "bigint", + ) + + if xhiveframework.db.db_type == "mariadb": + table_name = "information_schema.tables" + conditions = f"table_type = 'sequence' and table_name = '{self._testMethodName}_id_seq'" + else: + table_name = "information_schema.sequences" + conditions = f"sequence_name = '{self._testMethodName}_id_seq'" + + # check if sequence table is created + self.assertTrue( + xhiveframework.db.sql( + f"""select * from {table_name} + where {conditions}""" + ) + ) + + # change the autoname/naming rule back to original + dt.autoname = autoname + dt.save() + + # check if name type has changed + self.assertEqual( + xhiveframework.db.sql( + f"""select data_type FROM information_schema.columns + where column_name = 'name' and table_name = 'tab{self._testMethodName}'""" + )[0][0], + "varchar" if xhiveframework.db.db_type == "mariadb" else "character varying", + ) + + def test_doctype_unique_constraint_dropped(self): + if xhiveframework.db.exists("DocType", "With_Unique"): + xhiveframework.delete_doc("DocType", "With_Unique") + + dt = new_doctype("With_Unique", unique=1) + dt.insert() + + doc1 = xhiveframework.new_doc("With_Unique") + doc2 = xhiveframework.new_doc("With_Unique") + doc1.some_fieldname = "Something" + doc1.name = "one" + doc2.some_fieldname = "Something" + doc2.name = "two" + + doc1.insert() + self.assertRaises(xhiveframework.UniqueValidationError, doc2.insert) + xhiveframework.db.rollback() + + dt.fields[0].unique = 0 + dt.save() + + doc2.insert() + doc1.delete() + doc2.delete() + + def test_validate_search_fields(self): + doc = new_doctype("Test Search Fields") + doc.search_fields = "some_fieldname" + doc.insert() + self.assertEqual(doc.name, "Test Search Fields") + + # check if invalid fieldname is allowed or not + doc.search_fields = "some_fieldname_1" + self.assertRaises(xhiveframework.ValidationError, doc.save) + + # check if no value fields are allowed in search fields + field = doc.append("fields", {}) + field.fieldname = "some_html_field" + field.fieldtype = "HTML" + field.label = "Some HTML Field" + doc.search_fields = "some_fieldname,some_html_field" + self.assertRaises(xhiveframework.ValidationError, doc.save) + + def test_depends_on_fields(self): + doc = new_doctype("Test Depends On", depends_on="eval:doc.__islocal == 0") + doc.insert() + + # check if the assignment operation is allowed in depends_on + field = doc.fields[0] + field.depends_on = "eval:doc.__islocal = 0" + self.assertRaises(xhiveframework.ValidationError, doc.save) + + def test_all_depends_on_fields_conditions(self): + import re + + docfields = xhiveframework.get_all( + "DocField", + or_filters={ + "ifnull(depends_on, '')": ("!=", ""), + "ifnull(collapsible_depends_on, '')": ("!=", ""), + "ifnull(mandatory_depends_on, '')": ("!=", ""), + "ifnull(read_only_depends_on, '')": ("!=", ""), + }, + fields=[ + "parent", + "depends_on", + "collapsible_depends_on", + "mandatory_depends_on", + "read_only_depends_on", + "fieldname", + "fieldtype", + ], + ) + + pattern = r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+' + for field in docfields: + for depends_on in [ + "depends_on", + "collapsible_depends_on", + "mandatory_depends_on", + "read_only_depends_on", + ]: + condition = field.get(depends_on) + if condition: + self.assertFalse(re.match(pattern, condition)) + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + def test_sync_field_order(self): + import os + + from xhiveframework.modules.import_file import get_file_path + + # create test doctype + test_doctype = xhiveframework.get_doc( + { + "doctype": "DocType", + "module": "Core", + "fields": [ + {"label": "Field 1", "fieldname": "field_1", "fieldtype": "Data"}, + {"label": "Field 2", "fieldname": "field_2", "fieldtype": "Data"}, + {"label": "Field 3", "fieldname": "field_3", "fieldtype": "Data"}, + {"label": "Field 4", "fieldname": "field_4", "fieldtype": "Data"}, + ], + "permissions": [{"role": "System Manager", "read": 1}], + "name": "Test Field Order DocType", + "__islocal": 1, + } + ) + + path = get_file_path(test_doctype.module, test_doctype.doctype, test_doctype.name) + initial_fields_order = ["field_1", "field_2", "field_3", "field_4"] + + xhiveframework.delete_doc_if_exists("DocType", "Test Field Order DocType") + if os.path.isfile(path): + os.remove(path) + + try: + xhiveframework.flags.allow_doctype_export = 1 + test_doctype.save() + + # assert that field_order list is being created with the default order + test_doctype_json = xhiveframework.get_file_json(path) + self.assertTrue(test_doctype_json.get("field_order")) + self.assertEqual(len(test_doctype_json["fields"]), len(test_doctype_json["field_order"])) + self.assertListEqual( + [f["fieldname"] for f in test_doctype_json["fields"]], test_doctype_json["field_order"] + ) + self.assertListEqual([f["fieldname"] for f in test_doctype_json["fields"]], initial_fields_order) + self.assertListEqual(test_doctype_json["field_order"], initial_fields_order) + + # remove field_order to test reload_doc/sync/migrate is backwards compatible without field_order + del test_doctype_json["field_order"] + with open(path, "w+") as txtfile: + txtfile.write(xhiveframework.as_json(test_doctype_json)) + + # assert that field_order is actually removed from the json file + test_doctype_json = xhiveframework.get_file_json(path) + self.assertFalse(test_doctype_json.get("field_order")) + + # make sure that migrate/sync is backwards compatible without field_order + xhiveframework.reload_doctype(test_doctype.name, force=True) + test_doctype.reload() + + # assert that field_order list is being created with the default order again + test_doctype.save() + test_doctype_json = xhiveframework.get_file_json(path) + self.assertTrue(test_doctype_json.get("field_order")) + self.assertEqual(len(test_doctype_json["fields"]), len(test_doctype_json["field_order"])) + self.assertListEqual( + [f["fieldname"] for f in test_doctype_json["fields"]], test_doctype_json["field_order"] + ) + self.assertListEqual([f["fieldname"] for f in test_doctype_json["fields"]], initial_fields_order) + self.assertListEqual(test_doctype_json["field_order"], initial_fields_order) + + # reorder fields: swap row 1 and 3 + test_doctype.fields[0], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[0] + for i, f in enumerate(test_doctype.fields): + f.idx = i + 1 + + # assert that reordering fields only affects `field_order` rather than `fields` attr + test_doctype.save() + test_doctype_json = xhiveframework.get_file_json(path) + self.assertListEqual([f["fieldname"] for f in test_doctype_json["fields"]], initial_fields_order) + self.assertListEqual( + test_doctype_json["field_order"], ["field_3", "field_2", "field_1", "field_4"] + ) + + # reorder `field_order` in the json file: swap row 2 and 4 + test_doctype_json["field_order"][1], test_doctype_json["field_order"][3] = ( + test_doctype_json["field_order"][3], + test_doctype_json["field_order"][1], + ) + with open(path, "w+") as txtfile: + txtfile.write(xhiveframework.as_json(test_doctype_json)) + + # assert that reordering `field_order` from json file is reflected in DocType upon migrate/sync + xhiveframework.reload_doctype(test_doctype.name, force=True) + test_doctype.reload() + self.assertListEqual( + [f.fieldname for f in test_doctype.fields], ["field_3", "field_4", "field_1", "field_2"] + ) + + # insert row in the middle and remove first row (field 3) + test_doctype.append("fields", {"label": "Field 5", "fieldname": "field_5", "fieldtype": "Data"}) + test_doctype.fields[4], test_doctype.fields[3] = test_doctype.fields[3], test_doctype.fields[4] + test_doctype.fields[3], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[3] + test_doctype.remove(test_doctype.fields[0]) + for i, f in enumerate(test_doctype.fields): + f.idx = i + 1 + + test_doctype.save() + test_doctype_json = xhiveframework.get_file_json(path) + self.assertListEqual( + [f["fieldname"] for f in test_doctype_json["fields"]], + ["field_1", "field_2", "field_4", "field_5"], + ) + self.assertListEqual( + test_doctype_json["field_order"], ["field_4", "field_5", "field_1", "field_2"] + ) + except Exception: + raise + finally: + xhiveframework.flags.allow_doctype_export = 0 + + def test_unique_field_name_for_two_fields(self): + doc = new_doctype("Test Unique Field") + field_1 = doc.append("fields", {}) + field_1.fieldname = "some_fieldname_1" + field_1.fieldtype = "Data" + + field_2 = doc.append("fields", {}) + field_2.fieldname = "some_fieldname_1" + field_2.fieldtype = "Data" + + self.assertRaises(UniqueFieldnameError, doc.insert) + + def test_fieldname_is_not_name(self): + doc = new_doctype("Test Name Field") + field_1 = doc.append("fields", {}) + field_1.label = "Name" + field_1.fieldtype = "Data" + doc.insert() + self.assertEqual(doc.fields[1].fieldname, "name1") + doc.fields[1].fieldname = "name" + self.assertRaises(InvalidFieldNameError, doc.save) + + def test_illegal_mandatory_validation(self): + doc = new_doctype("Test Illegal mandatory") + field_1 = doc.append("fields", {}) + field_1.fieldname = "some_fieldname_1" + field_1.fieldtype = "Section Break" + field_1.reqd = 1 + + self.assertRaises(IllegalMandatoryError, doc.insert) + + def test_link_with_wrong_and_no_options(self): + doc = new_doctype("Test link") + field_1 = doc.append("fields", {}) + field_1.fieldname = "some_fieldname_1" + field_1.fieldtype = "Link" + + self.assertRaises(DoctypeLinkError, doc.insert) + + field_1.options = "wrongdoctype" + + self.assertRaises(WrongOptionsDoctypeLinkError, doc.insert) + + def test_hidden_and_mandatory_without_default(self): + doc = new_doctype("Test hidden and mandatory") + field_1 = doc.append("fields", {}) + field_1.fieldname = "some_fieldname_1" + field_1.fieldtype = "Data" + field_1.reqd = 1 + field_1.hidden = 1 + + self.assertRaises(HiddenAndMandatoryWithoutDefaultError, doc.insert) + + def test_field_can_not_be_indexed_validation(self): + doc = new_doctype("Test index") + field_1 = doc.append("fields", {}) + field_1.fieldname = "some_fieldname_1" + field_1.fieldtype = "Long Text" + field_1.search_index = 1 + + self.assertRaises(CannotIndexedError, doc.insert) + + def test_cancel_link_doctype(self): + import json + + from xhiveframework.desk.form.linked_with import cancel_all_linked_docs, get_submitted_linked_docs + + # create doctype + link_doc = new_doctype("Test Linked Doctype") + link_doc.is_submittable = 1 + for data in link_doc.get("permissions"): + data.submit = 1 + data.cancel = 1 + link_doc.insert() + + doc = new_doctype("Test Doctype") + doc.is_submittable = 1 + field_2 = doc.append("fields", {}) + field_2.label = "Test Linked Doctype" + field_2.fieldname = "test_linked_doctype" + field_2.fieldtype = "Link" + field_2.options = "Test Linked Doctype" + for data in link_doc.get("permissions"): + data.submit = 1 + data.cancel = 1 + doc.insert() + + # create doctype data + data_link_doc = xhiveframework.new_doc("Test Linked Doctype") + data_link_doc.some_fieldname = "Data1" + data_link_doc.insert() + data_link_doc.save() + data_link_doc.submit() + + data_doc = xhiveframework.new_doc("Test Doctype") + data_doc.some_fieldname = "Data1" + data_doc.test_linked_doctype = data_link_doc.name + data_doc.insert() + data_doc.save() + data_doc.submit() + + docs = get_submitted_linked_docs(link_doc.name, data_link_doc.name) + dump_docs = json.dumps(docs.get("docs")) + cancel_all_linked_docs(dump_docs) + data_link_doc.cancel() + data_doc.load_from_db() + self.assertEqual(data_link_doc.docstatus, 2) + self.assertEqual(data_doc.docstatus, 2) + + # delete doctype record + data_doc.delete() + data_link_doc.delete() + + # delete doctype + link_doc.delete() + doc.delete() + xhiveframework.db.commit() + + def test_ignore_cancelation_of_linked_doctype_during_cancel(self): + import json + + from xhiveframework.desk.form.linked_with import cancel_all_linked_docs, get_submitted_linked_docs + + # create linked doctype + link_doc = new_doctype("Test Linked Doctype 1") + link_doc.is_submittable = 1 + for data in link_doc.get("permissions"): + data.submit = 1 + data.cancel = 1 + link_doc.insert() + + # create first parent doctype + test_doc_1 = new_doctype("Test Doctype 1") + test_doc_1.is_submittable = 1 + + field_2 = test_doc_1.append("fields", {}) + field_2.label = "Test Linked Doctype 1" + field_2.fieldname = "test_linked_doctype_a" + field_2.fieldtype = "Link" + field_2.options = "Test Linked Doctype 1" + + for data in test_doc_1.get("permissions"): + data.submit = 1 + data.cancel = 1 + test_doc_1.insert() + + # crete second parent doctype + doc = new_doctype("Test Doctype 2") + doc.is_submittable = 1 + + field_2 = doc.append("fields", {}) + field_2.label = "Test Linked Doctype 1" + field_2.fieldname = "test_linked_doctype_a" + field_2.fieldtype = "Link" + field_2.options = "Test Linked Doctype 1" + + for data in link_doc.get("permissions"): + data.submit = 1 + data.cancel = 1 + doc.insert() + + # create doctype data + data_link_doc_1 = xhiveframework.new_doc("Test Linked Doctype 1") + data_link_doc_1.some_fieldname = "Data1" + data_link_doc_1.insert() + data_link_doc_1.save() + data_link_doc_1.submit() + + data_doc_2 = xhiveframework.new_doc("Test Doctype 1") + data_doc_2.some_fieldname = "Data1" + data_doc_2.test_linked_doctype_a = data_link_doc_1.name + data_doc_2.insert() + data_doc_2.save() + data_doc_2.submit() + + data_doc = xhiveframework.new_doc("Test Doctype 2") + data_doc.some_fieldname = "Data1" + data_doc.test_linked_doctype_a = data_link_doc_1.name + data_doc.insert() + data_doc.save() + data_doc.submit() + + docs = get_submitted_linked_docs(link_doc.name, data_link_doc_1.name) + dump_docs = json.dumps(docs.get("docs")) + + cancel_all_linked_docs(dump_docs, ignore_doctypes_on_cancel_all=["Test Doctype 2"]) + + # checking that doc for Test Doctype 2 is not canceled + self.assertRaises(xhiveframework.LinkExistsError, data_link_doc_1.cancel) + + data_doc.load_from_db() + data_doc_2.load_from_db() + self.assertEqual(data_link_doc_1.docstatus, 2) + + # linked doc is canceled + self.assertEqual(data_doc_2.docstatus, 2) + + # ignored doctype 2 during cancel + self.assertEqual(data_doc.docstatus, 1) + + # delete doctype record + data_doc.cancel() + data_doc.delete() + data_doc_2.delete() + data_link_doc_1.delete() + + # delete doctype + link_doc.delete() + doc.delete() + test_doc_1.delete() + xhiveframework.db.commit() + + def test_links_table_fieldname_validation(self): + doc = new_doctype("Test Links Table Validation") + + # check valid data + doc.append("links", {"link_doctype": "User", "link_fieldname": "first_name"}) + validate_links_table_fieldnames(doc) # no error + doc.links = [] # reset links table + + # check invalid doctype + doc.append("links", {"link_doctype": "User2", "link_fieldname": "first_name"}) + self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc) + doc.links = [] # reset links table + + # check invalid fieldname + doc.append("links", {"link_doctype": "User", "link_fieldname": "a_field_that_does_not_exists"}) + + self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc) + + def test_create_virtual_doctype(self): + """Test virtual DocType.""" + virtual_doc = new_doctype("Test Virtual Doctype") + virtual_doc.is_virtual = 1 + virtual_doc.insert(ignore_if_duplicate=True) + virtual_doc.reload() + doc = xhiveframework.get_doc("DocType", "Test Virtual Doctype") + + self.assertDictEqual(doc.as_dict(), virtual_doc.as_dict()) + self.assertEqual(doc.is_virtual, 1) + self.assertFalse(xhiveframework.db.table_exists("Test Virtual Doctype")) + + def test_create_virtual_doctype_as_child_table(self): + """Test virtual DocType as Child Table below a normal DocType.""" + xhiveframework.delete_doc_if_exists("DocType", "Test Parent Virtual DocType", force=1) + xhiveframework.delete_doc_if_exists("DocType", "Test Virtual DocType as Child Table", force=1) + + virtual_doc = new_doctype("Test Virtual DocType as Child Table") + virtual_doc.is_virtual = 1 + virtual_doc.istable = 1 + virtual_doc.insert(ignore_permissions=True) + + doc = xhiveframework.get_doc("DocType", "Test Virtual DocType as Child Table") + + self.assertEqual(doc.is_virtual, 1) + self.assertEqual(doc.istable, 1) + self.assertFalse(xhiveframework.db.table_exists("Test Virtual DocType as Child Table")) + + parent_doc = new_doctype("Test Parent Virtual DocType") + parent_doc.append( + "fields", + { + "fieldname": "virtual_child_table", + "fieldtype": "Table", + "options": "Test Virtual DocType as Child Table", + }, + ) + self.assertRaises(xhiveframework.exceptions.ValidationError, parent_doc.insert) + parent_doc.is_virtual = 1 + parent_doc.insert(ignore_permissions=True) + self.assertFalse(xhiveframework.db.table_exists("Test Parent Virtual DocType")) + + def test_default_fieldname(self): + fields = [ + {"label": "title", "fieldname": "title", "fieldtype": "Data", "default": "{some_fieldname}"} + ] + dt = new_doctype("DT with default field", fields=fields) + dt.insert() + + dt.delete() + + def test_autoincremented_doctype_transition(self): + xhiveframework.delete_doc_if_exists("DocType", "testy_autoinc_dt") + dt = new_doctype("testy_autoinc_dt", autoname="autoincrement").insert(ignore_permissions=True) + dt.autoname = "hash" + + dt.save(ignore_permissions=True) + + dt_data = xhiveframework.get_doc({"doctype": dt.name, "some_fieldname": "test data"}).insert( + ignore_permissions=True + ) + + dt.autoname = "autoincrement" + + try: + dt.save(ignore_permissions=True) + except xhiveframework.ValidationError as e: + self.assertEqual( + e.args[0], + "Can only change to/from Autoincrement naming rule when there is no data in the doctype", + ) + else: + self.fail( + """Shouldn't be possible to transition to/from autoincremented doctype + when data is present in doctype""" + ) + finally: + # cleanup + dt_data.delete(ignore_permissions=True) + dt.delete(ignore_permissions=True) + + def test_json_field(self): + """Test json field.""" + import json + + json_doc = new_doctype( + "Test Json Doctype", + fields=[{"label": "json field", "fieldname": "test_json_field", "fieldtype": "JSON"}], + ) + json_doc.insert() + json_doc.save() + doc = xhiveframework.get_doc("DocType", "Test Json Doctype") + for field in doc.fields: + if field.fieldname == "test_json_field": + self.assertEqual(field.fieldtype, "JSON") + break + + doc = xhiveframework.get_doc( + {"doctype": "Test Json Doctype", "test_json_field": json.dumps({"hello": "world"})} + ) + doc.insert() + doc.save() + + test_json = xhiveframework.get_doc("Test Json Doctype", doc.name) + + if isinstance(test_json.test_json_field, str): + test_json.test_json_field = json.loads(test_json.test_json_field) + + self.assertEqual(test_json.test_json_field["hello"], "world") + + def test_no_delete_doc(self): + self.assertRaises(xhiveframework.ValidationError, xhiveframework.delete_doc, "DocType", "Address") + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + @patch.dict(xhiveframework.conf, {"developer_mode": 1}) + def test_export_types(self): + """Export python types.""" + import ast + + from xhiveframework.types.exporter import TypeExporter + + def validate(code): + ast.parse(code) + + doctype = new_doctype(custom=0).insert() + + exporter = TypeExporter(doctype) + code = exporter.controller_path.read_text() + validate(code) + + # regenerate and verify and file is same word to word. + exporter.export_types() + new_code = exporter.controller_path.read_text() + validate(new_code) + + self.assertEqual(code, new_code) + + # Add fields and save + + fieldname = "test_type" + doctype.append("fields", {"fieldname": fieldname, "fieldtype": "Int"}) + doctype.save() + + new_field_code = exporter.controller_path.read_text() + validate(new_field_code) + + self.assertIn(fieldname, new_field_code) + self.assertIn("Int", new_field_code) + + doctype.delete() + xhiveframework.db.commit() + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + @patch.dict(xhiveframework.conf, {"developer_mode": 1}) + def test_custom_field_deletion(self): + """Custom child tables whose doctype doesn't exist should be auto deleted.""" + doctype = new_doctype(custom=0).insert().name + child = new_doctype(custom=0, istable=1).insert().name + + field = "abc" + create_custom_fields({doctype: [{"fieldname": field, "fieldtype": "Table", "options": child}]}) + + xhiveframework.delete_doc("DocType", child) + self.assertFalse(xhiveframework.get_meta(doctype).get_field(field)) + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + @patch.dict(xhiveframework.conf, {"developer_mode": 1}) + def test_delete_doctype_with_customization(self): + from xhiveframework.custom.doctype.property_setter.property_setter import make_property_setter + + custom_field = "customfield" + + doctype = new_doctype(custom=0).insert().name + + # Create property setter and custom field + field = "some_fieldname" + make_property_setter(doctype, field, "default", "DELETETHIS", "Data") + create_custom_fields({doctype: [{"fieldname": custom_field, "fieldtype": "Data"}]}) + + # Create 1 record + original_doc = xhiveframework.get_doc(doctype=doctype, custom_field_name="wat").insert() + self.assertEqual(original_doc.some_fieldname, "DELETETHIS") + + # delete doctype + xhiveframework.delete_doc("DocType", doctype) + clear_doctype_cache(doctype) + + # "restore" doctype by inserting doctype with same schema again + new_doctype(doctype, custom=0).insert() + + # Ensure basically same doctype getting "restored" + restored_doc = xhiveframework.get_last_doc(doctype) + verify_fields = ["doctype", field, custom_field] + for f in verify_fields: + self.assertEqual(original_doc.get(f), restored_doc.get(f)) + + # Check form load of restored doctype + getdoc(doctype, restored_doc.name) + + # ensure meta - property setter + self.assertEqual(xhiveframework.get_meta(doctype).get_field(field).default, "DELETETHIS") + xhiveframework.delete_doc("DocType", doctype) + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + @patch.dict(xhiveframework.conf, {"developer_mode": 1}) + def test_delete_orphaned_doctypes(self): + doctype = new_doctype(custom=0).insert() + xhiveframework.db.commit() + + delete_controllers(doctype.name, doctype.module) + job = xhiveframework.enqueue(remove_orphan_doctypes) + wait_for_completion(job) + + xhiveframework.db.rollback() + self.assertFalse(xhiveframework.db.exists("DocType", doctype.name)) + + def test_not_in_list_view_for_not_allowed_mandatory_field(self): + doctype = new_doctype( + fields=[ + { + "fieldname": "cover_image", + "fieldtype": "Attach Image", + "label": "Cover Image", + "reqd": 1, # mandatory + }, + { + "fieldname": "book_name", + "fieldtype": "Data", + "label": "Book Name", + "reqd": 1, # mandatory + }, + ], + ).insert() + + self.assertFalse(doctype.fields[0].in_list_view) + self.assertTrue(doctype.fields[1].in_list_view) + xhiveframework.delete_doc("DocType", doctype.name) + + +def new_doctype( + name: str | None = None, + unique: bool = False, + depends_on: str = "", + fields: list[dict] | None = None, + custom: bool = True, + default: str | None = None, + **kwargs, +): + if not name: + # Test prefix is required to avoid coverage + name = "Test " + "".join(random.sample(string.ascii_lowercase, 10)) + + doc = xhiveframework.get_doc( + { + "doctype": "DocType", + "module": "Core", + "custom": custom, + "fields": [ + { + "label": "Some Field", + "fieldname": "some_fieldname", + "fieldtype": "Data", + "unique": unique, + "default": default, + "depends_on": depends_on, + } + ], + "permissions": [ + { + "role": "System Manager", + "read": 1, + } + ], + "name": name, + **kwargs, + } + ) + + if fields and len(fields) > 0: + doc.set("fields", fields) + + return doc diff --git a/xhiveframework/core/doctype/doctype_action/__init__.py b/xhiveframework/core/doctype/doctype_action/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/doctype_action/doctype_action.json b/xhiveframework/core/doctype/doctype_action/doctype_action.json new file mode 100644 index 0000000..080755c --- /dev/null +++ b/xhiveframework/core/doctype/doctype_action/doctype_action.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "creation": "2019-09-23 16:28:13.953520", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label", + "action_type", + "action", + "group", + "hidden", + "custom" + ], + "fields": [ + { + "columns": 2, + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "fieldname": "group", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Group" + }, + { + "columns": 2, + "fieldname": "action_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Action Type", + "options": "Server Action\nRoute", + "reqd": 1 + }, + { + "columns": 4, + "fieldname": "action", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Action / Route", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "hidden": 1, + "label": "Custom" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-24 14:19:05.549835", + "modified_by": "Administrator", + "module": "Core", + "name": "DocType Action", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype_action/doctype_action.py b/xhiveframework/core/doctype/doctype_action/doctype_action.py new file mode 100644 index 0000000..58004a1 --- /dev/null +++ b/xhiveframework/core/doctype/doctype_action/doctype_action.py @@ -0,0 +1,27 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DocTypeAction(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action: DF.SmallText + action_type: DF.Literal["Server Action", "Route"] + custom: DF.Check + group: DF.Data | None + hidden: DF.Check + label: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/doctype_link/__init__.py b/xhiveframework/core/doctype/doctype_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/doctype_link/doctype_link.json b/xhiveframework/core/doctype/doctype_link/doctype_link.json new file mode 100644 index 0000000..4baec67 --- /dev/null +++ b/xhiveframework/core/doctype/doctype_link/doctype_link.json @@ -0,0 +1,87 @@ +{ + "actions": [], + "creation": "2019-09-24 11:41:25.291377", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "link_doctype", + "link_fieldname", + "parent_doctype", + "table_fieldname", + "group", + "hidden", + "is_child_table", + "custom" + ], + "fields": [ + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Link DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "link_fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Link Fieldname", + "reqd": 1 + }, + { + "fieldname": "group", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Group" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "hidden": 1, + "label": "Custom" + }, + { + "depends_on": "is_child_table", + "fieldname": "parent_doctype", + "fieldtype": "Link", + "label": "Parent DocType", + "mandatory_depends_on": "is_child_table", + "options": "DocType" + }, + { + "default": "0", + "fetch_from": "link_doctype.istable", + "fieldname": "is_child_table", + "fieldtype": "Check", + "label": "Is Child Table", + "read_only": 1 + }, + { + "fieldname": "table_fieldname", + "fieldtype": "Data", + "label": "Table Fieldname" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-31 15:23:12.237491", + "modified_by": "Administrator", + "module": "Core", + "name": "DocType Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype_link/doctype_link.py b/xhiveframework/core/doctype/doctype_link/doctype_link.py new file mode 100644 index 0000000..f6c3059 --- /dev/null +++ b/xhiveframework/core/doctype/doctype_link/doctype_link.py @@ -0,0 +1,29 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DocTypeLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + custom: DF.Check + group: DF.Data | None + hidden: DF.Check + is_child_table: DF.Check + link_doctype: DF.Link + link_fieldname: DF.Data + parent: DF.Data + parent_doctype: DF.Link | None + parentfield: DF.Data + parenttype: DF.Data + table_fieldname: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/doctype_state/__init__.py b/xhiveframework/core/doctype/doctype_state/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/doctype_state/doctype_state.json b/xhiveframework/core/doctype/doctype_state/doctype_state.json new file mode 100644 index 0000000..79797b4 --- /dev/null +++ b/xhiveframework/core/doctype/doctype_state/doctype_state.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "creation": "2021-08-23 17:21:28.345841", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "color", + "custom" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "default": "Blue", + "fieldname": "color", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Color", + "options": "Blue\nCyan\nGray\nGreen\nLight Blue\nOrange\nPink\nPurple\nRed\nYellow", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "hidden": 1, + "label": "Custom" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-12-14 14:14:55.716378", + "modified_by": "Administrator", + "module": "Core", + "name": "DocType State", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/doctype_state/doctype_state.py b/xhiveframework/core/doctype/doctype_state/doctype_state.py new file mode 100644 index 0000000..9be11cf --- /dev/null +++ b/xhiveframework/core/doctype/doctype_state/doctype_state.py @@ -0,0 +1,26 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DocTypeState(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + color: DF.Literal[ + "Blue", "Cyan", "Gray", "Green", "Light Blue", "Orange", "Pink", "Purple", "Red", "Yellow" + ] + custom: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + title: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/document_naming_rule/__init__.py b/xhiveframework/core/doctype/document_naming_rule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.js b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.js new file mode 100644 index 0000000..ebf3393 --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.js @@ -0,0 +1,46 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Document Naming Rule", { + refresh: function (frm) { + frm.trigger("document_type"); + frm.last_counter_value = frm.doc.counter; + frm.skip_before_save = false; + }, + before_save: function (frm) { + if (frm.is_new() || frm.skip_before_save || frm.last_counter_value === frm.doc.counter) + return; + + xhiveframework.validated = false; + xhiveframework.warn( + __("Are you sure?"), + __("Updating counter may lead to document name conflicts if not done properly"), + () => { + frm.skip_before_save = true; + frm.save(); + }, + __("Proceed"), + false + ); + }, + document_type: (frm) => { + // update the select field options with fieldnames + if (frm.doc.document_type) { + xhiveframework.model.with_doctype(frm.doc.document_type, () => { + let fieldnames = xhiveframework + .get_meta(frm.doc.document_type) + .fields.filter((d) => { + return xhiveframework.model.no_value_type.indexOf(d.fieldtype) === -1; + }) + .map((d) => { + return { label: `${d.label} (${d.fieldname})`, value: d.fieldname }; + }); + frm.fields_dict.conditions.grid.update_docfield_property( + "field", + "options", + fieldnames + ); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.json b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.json new file mode 100644 index 0000000..1e2247c --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.json @@ -0,0 +1,115 @@ +{ + "actions": [], + "creation": "2020-09-07 12:48:48.334318", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "disabled", + "priority", + "section_break_3", + "conditions", + "naming_section", + "prefix", + "counter", + "column_break_xfqa", + "prefix_digits" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "prefix", + "fieldtype": "Data", + "label": "Prefix", + "mandatory_depends_on": "eval:doc.naming_by===\"Numbered\"", + "reqd": 1 + }, + { + "default": "0", + "description": "Warning: Updating counter may lead to document name conflicts if not done properly", + "fieldname": "counter", + "fieldtype": "Int", + "label": "Counter", + "no_copy": 1 + }, + { + "default": "5", + "description": "Example: 00001", + "fieldname": "prefix_digits", + "fieldtype": "Int", + "label": "Digits", + "mandatory_depends_on": "eval:doc.naming_by===\"Numbered\"", + "reqd": 1 + }, + { + "fieldname": "naming_section", + "fieldtype": "Section Break", + "label": "Naming" + }, + { + "collapsible": 1, + "collapsible_depends_on": "conditions", + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Rule Conditions" + }, + { + "fieldname": "conditions", + "fieldtype": "Table", + "label": "Conditions", + "options": "Document Naming Rule Condition" + }, + { + "description": "Rules with higher priority number will be applied first.", + "fieldname": "priority", + "fieldtype": "Int", + "label": "Priority" + }, + { + "fieldname": "column_break_xfqa", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-04-24 15:14:32.054272", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Naming Rule", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "document_type", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.py b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.py new file mode 100644 index 0000000..43b9db0 --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule/document_naming_rule.py @@ -0,0 +1,69 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.model.naming import parse_naming_series +from xhiveframework.utils.data import evaluate_filters + + +class DocumentNamingRule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.document_naming_rule_condition.document_naming_rule_condition import ( + DocumentNamingRuleCondition, + ) + from xhiveframework.types import DF + + conditions: DF.Table[DocumentNamingRuleCondition] + counter: DF.Int + disabled: DF.Check + document_type: DF.Link + prefix: DF.Data + prefix_digits: DF.Int + priority: DF.Int + + # end: auto-generated types + def validate(self): + self.validate_fields_in_conditions() + + def clear_doctype_map(self): + xhiveframework.cache_manager.clear_doctype_map(self.doctype, self.document_type) + + def on_update(self): + self.clear_doctype_map() + + def on_trash(self): + self.clear_doctype_map() + + def validate_fields_in_conditions(self): + if self.has_value_changed("document_type"): + docfields = [x.fieldname for x in xhiveframework.get_meta(self.document_type).fields] + for condition in self.conditions: + if condition.field not in docfields: + xhiveframework.throw( + _("{0} is not a field of doctype {1}").format( + xhiveframework.bold(condition.field), xhiveframework.bold(self.document_type) + ) + ) + + def apply(self, doc): + """ + Apply naming rules for the given document. Will set `name` if the rule is matched. + """ + if self.conditions: + if not evaluate_filters( + doc, [(self.document_type, d.field, d.condition, d.value) for d in self.conditions] + ): + return + + counter = xhiveframework.db.get_value(self.doctype, self.name, "counter", for_update=True) or 0 + naming_series = parse_naming_series(self.prefix, doc=doc) + + doc.name = naming_series + ("%0" + str(self.prefix_digits) + "d") % (counter + 1) + xhiveframework.db.set_value(self.doctype, self.name, "counter", counter + 1) diff --git a/xhiveframework/core/doctype/document_naming_rule/test_document_naming_rule.py b/xhiveframework/core/doctype/document_naming_rule/test_document_naming_rule.py new file mode 100644 index 0000000..a3ee08d --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule/test_document_naming_rule.py @@ -0,0 +1,70 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocumentNamingRule(XhiveFrameworkTestCase): + def test_naming_rule_by_series(self): + naming_rule = xhiveframework.get_doc( + dict(doctype="Document Naming Rule", document_type="ToDo", prefix="test-todo-", prefix_digits=5) + ).insert() + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="Is this my name " + xhiveframework.generate_hash()) + ).insert() + + self.assertEqual(todo.name, "test-todo-00001") + + naming_rule.delete() + todo.delete() + + def test_naming_rule_by_condition(self): + naming_rule = xhiveframework.get_doc( + dict( + doctype="Document Naming Rule", + document_type="ToDo", + prefix="test-high-", + prefix_digits=5, + priority=10, + conditions=[dict(field="priority", condition="=", value="High")], + ) + ).insert() + + # another rule + naming_rule_1 = xhiveframework.copy_doc(naming_rule) + naming_rule_1.prefix = "test-medium-" + naming_rule_1.conditions[0].value = "Medium" + naming_rule_1.insert() + + # default rule with low priority - should not get applied for rules + # with higher priority + naming_rule_2 = xhiveframework.copy_doc(naming_rule) + naming_rule_2.prefix = "test-low-" + naming_rule_2.priority = 0 + naming_rule_2.conditions = [] + naming_rule_2.insert() + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", priority="High", description="Is this my name " + xhiveframework.generate_hash()) + ).insert() + + todo_1 = xhiveframework.get_doc( + dict(doctype="ToDo", priority="Medium", description="Is this my name " + xhiveframework.generate_hash()) + ).insert() + + todo_2 = xhiveframework.get_doc( + dict(doctype="ToDo", priority="Low", description="Is this my name " + xhiveframework.generate_hash()) + ).insert() + + try: + self.assertEqual(todo.name, "test-high-00001") + self.assertEqual(todo_1.name, "test-medium-00001") + self.assertEqual(todo_2.name, "test-low-00001") + finally: + naming_rule.delete() + naming_rule_1.delete() + naming_rule_2.delete() + todo.delete() + todo_1.delete() + todo_2.delete() diff --git a/xhiveframework/core/doctype/document_naming_rule_condition/__init__.py b/xhiveframework/core/doctype/document_naming_rule_condition/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.js b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.js new file mode 100644 index 0000000..30d794b --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Document Naming Rule Condition", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.json b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.json new file mode 100644 index 0000000..781566b --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.json @@ -0,0 +1,49 @@ +{ + "actions": [], + "creation": "2020-09-08 10:17:54.366279", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "field", + "condition", + "value" + ], + "fields": [ + { + "fieldname": "field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Field", + "reqd": 1 + }, + { + "fieldname": "condition", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Condition", + "options": "=\n!=\n>\n<\n>=\n<=", + "reqd": 1 + }, + { + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Value", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-08 10:19:56.192949", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Naming Rule Condition", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py new file mode 100644 index 0000000..57e756f --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule_condition/document_naming_rule_condition.py @@ -0,0 +1,24 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DocumentNamingRuleCondition(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + condition: DF.Literal["=", "!=", ">", "<", ">=", "<="] + field: DF.Literal[None] + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + value: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py b/xhiveframework/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py new file mode 100644 index 0000000..feac9d1 --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_rule_condition/test_document_naming_rule_condition.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocumentNamingRuleCondition(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/document_naming_settings/__init__.py b/xhiveframework/core/doctype/document_naming_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.js b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.js new file mode 100644 index 0000000..c9890ff --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.js @@ -0,0 +1,80 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Document Naming Settings", { + setup: function (frm) { + frm.set_query("document_type", "amend_naming_override", () => { + return { + filters: { + is_submittable: 1, + }, + }; + }); + }, + + refresh: function (frm) { + frm.trigger("setup_transaction_autocomplete"); + frm.disable_save(); + }, + + setup_transaction_autocomplete: function (frm) { + xhiveframework.call({ + method: "get_transactions_and_prefixes", + doc: frm.doc, + callback: function (r) { + frm.fields_dict.transaction_type.set_data(r.message.transactions); + frm.fields_dict.prefix.set_data(r.message.prefixes); + }, + }); + }, + + transaction_type: function (frm) { + frm.set_value("user_must_always_select", 0); + xhiveframework.call({ + method: "get_options", + doc: frm.doc, + callback: function (r) { + frm.set_value("naming_series_options", r.message); + if (r.message && r.message.split("\n")[0] == "") + frm.set_value("user_must_always_select", 1); + }, + }); + }, + + prefix: function (frm) { + xhiveframework.call({ + method: "get_current", + doc: frm.doc, + callback: function (r) { + frm.refresh_field("current_value"); + }, + }); + }, + + update: function (frm) { + xhiveframework.call({ + method: "update_series", + doc: frm.doc, + freeze: true, + freeze_msg: __("Updating naming series options"), + callback: function (r) { + frm.trigger("setup_transaction_autocomplete"); + frm.trigger("transaction_type"); + }, + }); + }, + + try_naming_series(frm) { + xhiveframework.call({ + method: "preview_series", + doc: frm.doc, + callback: function (r) { + if (!r.exc) { + frm.set_value("series_preview", r.message); + } else { + frm.set_value("series_preview", __("Failed to generate preview of series")); + } + }, + }); + }, +}); diff --git a/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.json b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.json new file mode 100644 index 0000000..5a1991c --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.json @@ -0,0 +1,165 @@ +{ + "actions": [], + "creation": "2022-05-30 07:24:07.736646", + "description": "Configure various aspects of how document naming works like naming series, current counter.", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "naming_series_tab", + "setup_series", + "transaction_type", + "naming_series_options", + "user_must_always_select", + "update", + "column_break_9", + "try_naming_series", + "series_preview", + "help_html", + "update_series", + "prefix", + "current_value", + "update_series_start", + "amended_documents_section", + "default_amend_naming", + "amend_naming_override", + "update_amendment_naming" + ], + "fields": [ + { + "collapsible": 1, + "description": "Set Naming Series options on your transactions.", + "fieldname": "setup_series", + "fieldtype": "Section Break", + "label": "Setup Series for transactions" + }, + { + "depends_on": "transaction_type", + "fieldname": "help_html", + "fieldtype": "HTML", + "label": "Help HTML", + "options": "
    \n Edit list of Series in the box. Rules:\n
      \n
    • Each Series Prefix on a new line.
    • \n
    • Allowed special characters are \"/\" and \"-\"
    • \n
    • \n Optionally, set the number of digits in the series using dot (.)\n followed by hashes (#). For example, \".####\" means that the series\n will have four digits. Default is five digits.\n
    • \n
    • \n You can also use variables in the series name by putting them\n between (.) dots\n
      \n Supported Variables:\n
        \n
      • .YYYY. - Year in 4 digits
      • \n
      • .YY. - Year in 2 digits
      • \n
      • .MM. - Month
      • \n
      • .DD. - Day of month
      • \n
      • .WW. - Week of the year
      • \n
      • .FY. - Fiscal Year
      • \n
      • \n .{fieldname}. - fieldname on the document e.g.\n branch\n
      • \n
      \n
    • \n
    \n Examples:\n
      \n
    • INV-
    • \n
    • INV-10-
    • \n
    • INVK-
    • \n
    • INV-.YYYY.-.{branch}.-.MM.-.####
    • \n
    \n
    \n
    \n" + }, + { + "default": "0", + "depends_on": "transaction_type", + "description": "Check this if you want to force the user to select a series before saving. There will be no default if you check this.", + "fieldname": "user_must_always_select", + "fieldtype": "Check", + "label": "User must always select" + }, + { + "depends_on": "transaction_type", + "fieldname": "update", + "fieldtype": "Button", + "label": "Update" + }, + { + "collapsible": 1, + "description": "Change the starting / current sequence number of an existing series.
    \n\nWarning: Incorrectly updating counters can prevent documents from getting created. ", + "fieldname": "update_series", + "fieldtype": "Section Break", + "label": "Update Series Counter" + }, + { + "fieldname": "prefix", + "fieldtype": "Autocomplete", + "label": "Prefix" + }, + { + "description": "This is the number of the last created transaction with this prefix", + "fieldname": "current_value", + "fieldtype": "Int", + "label": "Current Value" + }, + { + "fieldname": "update_series_start", + "fieldtype": "Button", + "label": "Update Series Number", + "options": "update_series_start" + }, + { + "depends_on": "transaction_type", + "fieldname": "naming_series_options", + "fieldtype": "Text", + "label": "Series List for this Transaction" + }, + { + "depends_on": "transaction_type", + "description": "Get a preview of generated names with a series.", + "fieldname": "try_naming_series", + "fieldtype": "Data", + "label": "Try a Naming Series" + }, + { + "fieldname": "transaction_type", + "fieldtype": "Autocomplete", + "label": "Select Transaction" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "naming_series_tab", + "fieldtype": "Tab Break", + "label": "Naming Series" + }, + { + "fieldname": "series_preview", + "fieldtype": "Text", + "label": "Preview of generated names", + "read_only": 1 + }, + { + "collapsible": 1, + "description": "Configure how amended documents will be named.
    \n\nDefault behaviour is to follow an amend counter which adds a number to the end of the original name indicating the amended version.
    \n\nDefault Naming will make the amended document to behave same as new documents.", + "fieldname": "amended_documents_section", + "fieldtype": "Section Break", + "label": "Amended Documents" + }, + { + "default": "Amend Counter", + "fieldname": "default_amend_naming", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Default Amendment Naming", + "options": "Amend Counter\nDefault Naming", + "reqd": 1 + }, + { + "fieldname": "amend_naming_override", + "fieldtype": "Table", + "label": "Amendment Naming Override", + "options": "Amended Document Naming Settings" + }, + { + "fieldname": "update_amendment_naming", + "fieldtype": "Button", + "label": "Update Amendment Naming", + "options": "update_amendment_rule" + } + ], + "hide_toolbar": 1, + "icon": "fa fa-sort-by-order", + "issingle": 1, + "links": [], + "modified": "2023-06-20 17:47:52.204139", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Naming Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.py b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.py new file mode 100644 index 0000000..8527fd0 --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_settings/document_naming_settings.py @@ -0,0 +1,256 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.doctype.doctype.doctype import validate_series +from xhiveframework.model.document import Document +from xhiveframework.model.naming import NamingSeries +from xhiveframework.permissions import get_doctypes_with_read + + +class NamingSeriesNotSetError(xhiveframework.ValidationError): + pass + + +class DocumentNamingSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.amended_document_naming_settings.amended_document_naming_settings import ( + AmendedDocumentNamingSettings, + ) + from xhiveframework.types import DF + + amend_naming_override: DF.Table[AmendedDocumentNamingSettings] + current_value: DF.Int + default_amend_naming: DF.Literal["Amend Counter", "Default Naming"] + naming_series_options: DF.Text | None + prefix: DF.Autocomplete | None + series_preview: DF.Text | None + transaction_type: DF.Autocomplete | None + try_naming_series: DF.Data | None + user_must_always_select: DF.Check + + # end: auto-generated types + @xhiveframework.whitelist() + def get_transactions_and_prefixes(self): + transactions = self._get_transactions() + prefixes = self._get_prefixes(transactions) + + return {"transactions": transactions, "prefixes": prefixes} + + def _get_transactions(self) -> list[str]: + readable_doctypes = set(get_doctypes_with_read()) + + standard = xhiveframework.get_all("DocField", {"fieldname": "naming_series"}, "parent", pluck="parent") + custom = xhiveframework.get_all("Custom Field", {"fieldname": "naming_series"}, "dt", pluck="dt") + + return sorted(readable_doctypes.intersection(standard + custom)) + + def _get_prefixes(self, doctypes) -> list[str]: + """Get all prefixes for naming series. + + - For all templates prefix is evaluated considering today's date + - All existing prefix in DB are shared as is. + """ + series_templates = set() + for d in doctypes: + try: + options = xhiveframework.get_meta(d).get_naming_series_options() + series_templates.update(options) + except xhiveframework.DoesNotExistError: + xhiveframework.msgprint(_("Unable to find DocType {0}").format(d)) + continue + + custom_templates = xhiveframework.get_all( + "DocType", + fields=["autoname"], + filters={ + "name": ("not in", doctypes), + "autoname": ("like", "%.#%"), + "module": ("not in", ["Core"]), + }, + ) + if custom_templates: + series_templates.update([d.autoname.rsplit(".", 1)[0] for d in custom_templates]) + + return self._evaluate_and_clean_templates(series_templates) + + def _evaluate_and_clean_templates(self, series_templates: set[str]) -> list[str]: + evalauted_prefix = set() + + series = xhiveframework.qb.DocType("Series") + prefixes_from_db = xhiveframework.qb.from_(series).select(series.name).run(pluck=True) + evalauted_prefix.update(prefixes_from_db) + + for series_template in series_templates: + try: + prefix = NamingSeries(series_template).get_prefix() + if "{" in prefix: + # fieldnames can't be evalauted, rely on data in DB instead + continue + evalauted_prefix.add(prefix) + except Exception: + xhiveframework.clear_last_message() + xhiveframework.log_error(f"Invalid naming series {series_template}") + + return sorted(evalauted_prefix) + + def get_options_list(self, options: str) -> list[str]: + return [op.strip() for op in options.split("\n") if op.strip()] + + @xhiveframework.whitelist() + def update_series(self): + """update series list""" + self.validate_set_series() + self.check_duplicate() + self.set_series_options_in_meta(self.transaction_type, self.naming_series_options) + + xhiveframework.msgprint( + _("Series Updated for {}").format(self.transaction_type), alert=True, indicator="green" + ) + + def validate_set_series(self): + if self.transaction_type and not self.naming_series_options: + xhiveframework.throw(_("Please set the series to be used.")) + + def set_series_options_in_meta(self, doctype: str, options: str) -> None: + options = self.get_options_list(options) + + # validate names + for series in options: + self.validate_series_name(series) + + if options and self.user_must_always_select: + options = ["", *options] + + default = options[0] if options else "" + + option_string = "\n".join(options) + + # Erase default first, it might not be in new options. + self.update_naming_series_property_setter(doctype, "default", "") + self.update_naming_series_property_setter(doctype, "options", option_string) + self.update_naming_series_property_setter(doctype, "default", default) + + self.naming_series_options = option_string + + xhiveframework.clear_cache(doctype=doctype) + + def update_naming_series_property_setter(self, doctype, property, value): + from xhiveframework.custom.doctype.property_setter.property_setter import make_property_setter + + make_property_setter(doctype, "naming_series", property, value, "Text") + + def check_duplicate(self): + def stripped_series(s: str) -> str: + return s.strip().rstrip("#") + + standard = xhiveframework.get_all("DocField", {"fieldname": "naming_series"}, "parent", pluck="parent") + custom = xhiveframework.get_all("Custom Field", {"fieldname": "naming_series"}, "dt", pluck="dt") + + all_doctypes_with_naming_series = set(standard + custom) + all_doctypes_with_naming_series.remove(self.transaction_type) + + existing_series = {} + for doctype in all_doctypes_with_naming_series: + for series in xhiveframework.get_meta(doctype).get_naming_series_options(): + existing_series[stripped_series(series)] = doctype + + dt = xhiveframework.get_doc("DocType", self.transaction_type) + + options = self.get_options_list(self.naming_series_options) + for series in options: + if stripped_series(series) in existing_series: + xhiveframework.throw(_("Series {0} already used in {1}").format(series, existing_series[series])) + validate_series(dt, series) + + def validate_series_name(self, series): + NamingSeries(series).validate() + + @xhiveframework.whitelist() + def get_options(self, doctype=None): + doctype = doctype or self.transaction_type + if not doctype: + return + + if xhiveframework.get_meta(doctype or self.transaction_type).get_field("naming_series"): + return xhiveframework.get_meta(doctype or self.transaction_type).get_field("naming_series").options + + @xhiveframework.whitelist() + def get_current(self): + """get series current""" + if self.prefix is not None: + self.current_value = NamingSeries(self.prefix).get_current_value() + return self.current_value + + @xhiveframework.whitelist() + def update_amendment_rule(self): + self.db_set("default_amend_naming", self.default_amend_naming) + + existing_overrides = xhiveframework.db.get_all( + "Amended Document Naming Settings", + filters={"name": ["not in", [d.name for d in self.amend_naming_override]]}, + pluck="name", + ) + for override in existing_overrides: + xhiveframework.delete_doc("Amended Document Naming Settings", override) + + for row in self.amend_naming_override: + row.save() + + xhiveframework.msgprint(_("Amendment naming rules updated."), indicator="green", alert=True) + + @xhiveframework.whitelist() + def update_series_start(self): + xhiveframework.only_for("System Manager") + + if self.prefix is None: + xhiveframework.throw(_("Please select prefix first")) + + naming_series = NamingSeries(self.prefix) + previous_value = naming_series.get_current_value() + naming_series.update_counter(self.current_value) + + self.create_version_log_for_change(naming_series.get_prefix(), previous_value, self.current_value) + + xhiveframework.msgprint( + _("Series counter for {} updated to {} successfully").format(self.prefix, self.current_value), + alert=True, + indicator="green", + ) + + def create_version_log_for_change(self, series, old, new): + version = xhiveframework.new_doc("Version") + version.ref_doctype = "Series" + version.docname = series or ".#" + version.data = xhiveframework.as_json({"changed": [["current", old, new]]}) + version.flags.ignore_links = True # series is not a "real" doctype + version.flags.ignore_permissions = True + version.insert() + + @xhiveframework.whitelist() + def preview_series(self) -> str: + """Preview what the naming series will generate.""" + + series = self.try_naming_series + if not series: + return "" + try: + doc = self._fetch_last_doc_if_available() + return "\n".join(NamingSeries(series).get_preview(doc=doc)) + except Exception as e: + xhiveframework.clear_last_message() + return _("Failed to generate names from the series") + f"\n{e!s}" + + def _fetch_last_doc_if_available(self): + """Fetch last doc for evaluating naming series with fields.""" + try: + return xhiveframework.get_last_doc(self.transaction_type) + except Exception: + return None diff --git a/xhiveframework/core/doctype/document_naming_settings/test_document_naming_settings.py b/xhiveframework/core/doctype/document_naming_settings/test_document_naming_settings.py new file mode 100644 index 0000000..648101e --- /dev/null +++ b/xhiveframework/core/doctype/document_naming_settings/test_document_naming_settings.py @@ -0,0 +1,117 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# See license.txt + +import xhiveframework +from xhiveframework.core.doctype.doctype.test_doctype import new_doctype +from xhiveframework.core.doctype.document_naming_settings.document_naming_settings import ( + DocumentNamingSettings, +) +from xhiveframework.model.naming import NamingSeries, get_default_naming_series +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import cint + + +class TestNamingSeries(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.ns_doctype = ( + new_doctype( + fields=[ + { + "label": "Series", + "fieldname": "naming_series", + "fieldtype": "Select", + "options": f"\n{xhiveframework.generate_hash()}-.###", + } + ], + autoname="naming_series:", + is_submittable=1, + ) + .insert() + .name + ) + + def setUp(self): + self.dns: DocumentNamingSettings = xhiveframework.get_doc("Document Naming Settings") + + def tearDown(self): + xhiveframework.db.rollback() + + def get_valid_serieses(self): + VALID_SERIES = ["SINV-", "SI-.{field}.", "SI-#.###", ""] + exisiting_series = self.dns.get_transactions_and_prefixes()["prefixes"] + return VALID_SERIES + exisiting_series + + def test_naming_preview(self): + self.dns.transaction_type = self.ns_doctype + + self.dns.try_naming_series = "AXBZ.####" + serieses = self.dns.preview_series().split("\n") + self.assertEqual(["AXBZ0001", "AXBZ0002", "AXBZ0003"], serieses) + + self.dns.try_naming_series = "AXBZ-.{currency}.-" + serieses = self.dns.preview_series().split("\n") + + def test_get_transactions(self): + naming_info = self.dns.get_transactions_and_prefixes() + self.assertIn(self.ns_doctype, naming_info["transactions"]) + + existing_naming_series = xhiveframework.get_meta(self.ns_doctype).get_field("naming_series").options + + for series in existing_naming_series.split("\n"): + self.assertIn(NamingSeries(series).get_prefix(), naming_info["prefixes"]) + + def test_default_naming_series(self): + self.assertIsNone(get_default_naming_series("DocType")) + + def test_updates_naming_options(self): + self.dns.transaction_type = self.ns_doctype + test_series = "KOOHBEW.###" + self.dns.naming_series_options = self.dns.get_options() + "\n" + test_series + self.dns.update_series() + self.assertIn(test_series, xhiveframework.get_meta(self.ns_doctype).get_naming_series_options()) + + def test_update_series_counter(self): + for series in self.get_valid_serieses(): + if not series: + continue + self.dns.prefix = series + current_count = cint(self.dns.get_current()) + new_count = self.dns.current_value = current_count + 1 + self.dns.update_series_start() + + self.assertEqual(self.dns.get_current(), new_count, f"Incorrect update for {series}") + + def test_amended_naming(self): + self.dns.amend_naming_override = [] + self.dns.default_amend_naming = "Amend Counter" + self.dns.update_amendment_rule() + + submittable_doc = xhiveframework.get_doc( + dict(doctype=self.ns_doctype, some_fieldname="test doc with submit") + ).submit() + submittable_doc.cancel() + + amended_doc = xhiveframework.get_doc( + dict( + doctype=self.ns_doctype, + some_fieldname="test doc with submit", + amended_from=submittable_doc.name, + ) + ).insert() + + self.assertIn(submittable_doc.name, amended_doc.name) + amended_doc.delete() + + self.dns.default_amend_naming = "Default Naming" + self.dns.update_amendment_rule() + + new_amended_doc = xhiveframework.get_doc( + dict( + doctype=self.ns_doctype, + some_fieldname="test doc with submit", + amended_from=submittable_doc.name, + ) + ).insert() + self.assertNotIn(submittable_doc.name, new_amended_doc.name) diff --git a/xhiveframework/core/doctype/document_share_key/__init__.py b/xhiveframework/core/doctype/document_share_key/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/document_share_key/document_share_key.js b/xhiveframework/core/doctype/document_share_key/document_share_key.js new file mode 100644 index 0000000..cb64431 --- /dev/null +++ b/xhiveframework/core/doctype/document_share_key/document_share_key.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Document Share Key", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/document_share_key/document_share_key.json b/xhiveframework/core/doctype/document_share_key/document_share_key.json new file mode 100644 index 0000000..b96fe09 --- /dev/null +++ b/xhiveframework/core/doctype/document_share_key/document_share_key.json @@ -0,0 +1,73 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "hash", + "creation": "2022-01-14 13:40:49.487646", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "reference_docname", + "key", + "expires_on" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "label": "Reference Document Name", + "options": "reference_doctype", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "key", + "fieldtype": "Data", + "label": "Key", + "read_only": 1 + }, + { + "fieldname": "expires_on", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expires On", + "read_only": 1 + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-01-14 13:57:28.050678", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Share Key", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/document_share_key/document_share_key.py b/xhiveframework/core/doctype/document_share_key/document_share_key.py new file mode 100644 index 0000000..d995881 --- /dev/null +++ b/xhiveframework/core/doctype/document_share_key/document_share_key.py @@ -0,0 +1,34 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +from random import randrange + +import xhiveframework +from xhiveframework.model.document import Document + + +class DocumentShareKey(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + expires_on: DF.Date | None + key: DF.Data | None + reference_docname: DF.DynamicLink | None + reference_doctype: DF.Link | None + + # end: auto-generated types + def before_insert(self): + self.key = xhiveframework.generate_hash(length=randrange(25, 35)) + if not self.expires_on and not self.flags.no_expiry: + self.expires_on = xhiveframework.utils.add_days( + None, days=xhiveframework.get_system_settings("document_share_key_expiry") or 90 + ) + + +def is_expired(expires_on): + return expires_on and expires_on < xhiveframework.utils.getdate() diff --git a/xhiveframework/core/doctype/document_share_key/test_document_share_key.py b/xhiveframework/core/doctype/document_share_key/test_document_share_key.py new file mode 100644 index 0000000..bc23872 --- /dev/null +++ b/xhiveframework/core/doctype/document_share_key/test_document_share_key.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocumentShareKey(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/domain/__init__.py b/xhiveframework/core/doctype/domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/domain/domain.js b/xhiveframework/core/doctype/domain/domain.js new file mode 100644 index 0000000..d679c83 --- /dev/null +++ b/xhiveframework/core/doctype/domain/domain.js @@ -0,0 +1,6 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Domain", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/domain/domain.json b/xhiveframework/core/doctype/domain/domain.json new file mode 100644 index 0000000..a6c7397 --- /dev/null +++ b/xhiveframework/core/doctype/domain/domain.json @@ -0,0 +1,54 @@ +{ + "autoname": "field:domain", + "creation": "2017-05-03 15:07:39.752820", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "domain" + ], + "fields": [ + { + "fieldname": "domain", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Domain", + "reqd": 1, + "unique": 1 + } + ], + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", + "module": "Core", + "name": "Domain", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "domain", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "domain" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/domain/domain.py b/xhiveframework/core/doctype/domain/domain.py new file mode 100644 index 0000000..0127dbd --- /dev/null +++ b/xhiveframework/core/doctype/domain/domain.py @@ -0,0 +1,136 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_fields +from xhiveframework.model.document import Document + + +class Domain(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + domain: DF.Data + # end: auto-generated types + """Domain documents are created automatically when DocTypes + with "Restricted" domains are imported during + installation or migration""" + + def setup_domain(self): + """Setup domain icons, permissions, custom fields etc.""" + self.setup_data() + self.setup_roles() + self.setup_properties() + self.set_values() + + if not int(xhiveframework.defaults.get_defaults().setup_complete or 0): + # if setup not complete, setup desktop etc. + self.setup_sidebar_items() + self.set_default_portal_role() + + if self.data.custom_fields: + create_custom_fields(self.data.custom_fields) + + if self.data.on_setup: + # custom on_setup method + xhiveframework.get_attr(self.data.on_setup)() + + def remove_domain(self): + """Unset domain settings""" + self.setup_data() + + if self.data.restricted_roles: + for role_name in self.data.restricted_roles: + if xhiveframework.db.exists("Role", role_name): + role = xhiveframework.get_doc("Role", role_name) + role.disabled = 1 + role.save() + + self.remove_custom_field() + + def remove_custom_field(self): + """Remove custom_fields when disabling domain""" + if self.data.custom_fields: + for doctype in self.data.custom_fields: + custom_fields = self.data.custom_fields[doctype] + + # custom_fields can be a list or dict + if isinstance(custom_fields, dict): + custom_fields = [custom_fields] + + for custom_field_detail in custom_fields: + custom_field_name = xhiveframework.db.get_value( + "Custom Field", dict(dt=doctype, fieldname=custom_field_detail.get("fieldname")) + ) + if custom_field_name: + xhiveframework.delete_doc("Custom Field", custom_field_name) + + def setup_roles(self): + """Enable roles that are restricted to this domain""" + if self.data.restricted_roles: + user = xhiveframework.get_doc("User", xhiveframework.session.user) + for role_name in self.data.restricted_roles: + user.append("roles", {"role": role_name}) + if not xhiveframework.db.get_value("Role", role_name): + xhiveframework.get_doc(dict(doctype="Role", role_name=role_name)).insert() + continue + + role = xhiveframework.get_doc("Role", role_name) + role.disabled = 0 + role.save() + user.save() + + def setup_data(self, domain=None): + """Load domain info via hooks""" + self.data = xhiveframework.get_domain_data(self.name) + + def get_domain_data(self, module): + return xhiveframework.get_attr(xhiveframework.get_hooks("domains")[self.name] + ".data") + + def set_default_portal_role(self): + """Set default portal role based on domain""" + if self.data.get("default_portal_role"): + xhiveframework.db.set_single_value( + "Portal Settings", "default_role", self.data.get("default_portal_role") + ) + + def setup_properties(self): + if self.data.properties: + for args in self.data.properties: + xhiveframework.make_property_setter(args) + + def set_values(self): + """set values based on `data.set_value`""" + if self.data.set_value: + for args in self.data.set_value: + xhiveframework.reload_doctype(args[0]) + doc = xhiveframework.get_doc(args[0], args[1] or args[0]) + doc.set(args[2], args[3]) + doc.save() + + def setup_sidebar_items(self): + """Enable / disable sidebar items""" + if self.data.allow_sidebar_items: + # disable all + xhiveframework.db.sql("update `tabPortal Menu Item` set enabled=0") + + # enable + xhiveframework.db.sql( + """update `tabPortal Menu Item` set enabled=1 + where route in ({})""".format(", ".join(f'"{d}"' for d in self.data.allow_sidebar_items)) + ) + + if self.data.remove_sidebar_items: + # disable all + xhiveframework.db.sql("update `tabPortal Menu Item` set enabled=1") + + # enable + xhiveframework.db.sql( + """update `tabPortal Menu Item` set enabled=0 + where route in ({})""".format(", ".join(f'"{d}"' for d in self.data.remove_sidebar_items)) + ) diff --git a/xhiveframework/core/doctype/domain/test_domain.py b/xhiveframework/core/doctype/domain/test_domain.py new file mode 100644 index 0000000..87ce0fe --- /dev/null +++ b/xhiveframework/core/doctype/domain/test_domain.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDomain(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/domain_settings/__init__.py b/xhiveframework/core/doctype/domain_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/domain_settings/domain_settings.js b/xhiveframework/core/doctype/domain_settings/domain_settings.js new file mode 100644 index 0000000..35ab302 --- /dev/null +++ b/xhiveframework/core/doctype/domain_settings/domain_settings.js @@ -0,0 +1,69 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Domain Settings", { + before_load: function (frm) { + if (!frm.domains_multicheck) { + frm.domains_multicheck = xhiveframework.ui.form.make_control({ + parent: frm.fields_dict.domains_html.$wrapper, + df: { + fieldname: "domains_multicheck", + fieldtype: "MultiCheck", + get_data: () => { + let active_domains = (frm.doc.active_domains || []).map( + (row) => row.domain + ); + return xhiveframework.boot.all_domains.map((domain) => { + return { + label: domain, + value: domain, + checked: active_domains.includes(domain), + }; + }); + }, + on_change: () => { + frm.dirty(); + }, + }, + render_input: true, + }); + frm.domains_multicheck.refresh_input(); + } + }, + + validate: function (frm) { + frm.trigger("set_options_in_table"); + }, + + set_options_in_table: function (frm) { + let selected_options = frm.domains_multicheck.get_value(); + let unselected_options = frm.domains_multicheck.options + .map((option) => option.value) + .filter((value) => { + return !selected_options.includes(value); + }); + + let map = {}, + list = []; + (frm.doc.active_domains || []).map((row) => { + map[row.domain] = row.name; + list.push(row.domain); + }); + + unselected_options.map((option) => { + if (list.includes(option)) { + xhiveframework.model.clear_doc("Has Domain", map[option]); + } + }); + + selected_options.map((option) => { + if (!list.includes(option)) { + xhiveframework.model.clear_doc("Has Domain", map[option]); + let row = xhiveframework.model.add_child(frm.doc, "Has Domain", "active_domains"); + row.domain = option; + } + }); + + refresh_field("active_domains"); + }, +}); diff --git a/xhiveframework/core/doctype/domain_settings/domain_settings.json b/xhiveframework/core/doctype/domain_settings/domain_settings.json new file mode 100644 index 0000000..c363529 --- /dev/null +++ b/xhiveframework/core/doctype/domain_settings/domain_settings.json @@ -0,0 +1,56 @@ +{ + "actions": [], + "creation": "2017-05-03 16:28:11.295095", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "active_domains_sb", + "domains_html", + "active_domains" + ], + "fields": [ + { + "fieldname": "active_domains_sb", + "fieldtype": "Section Break", + "label": "Active Domains" + }, + { + "fieldname": "domains_html", + "fieldtype": "HTML", + "label": "Domains HTML" + }, + { + "fieldname": "active_domains", + "fieldtype": "Table", + "hidden": 1, + "label": "Active Domains", + "options": "Has Domain", + "read_only": 1 + } + ], + "issingle": 1, + "links": [], + "modified": "2022-08-03 12:20:53.256607", + "modified_by": "Administrator", + "module": "Core", + "name": "Domain Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/domain_settings/domain_settings.py b/xhiveframework/core/doctype/domain_settings/domain_settings.py new file mode 100644 index 0000000..b46e48f --- /dev/null +++ b/xhiveframework/core/doctype/domain_settings/domain_settings.py @@ -0,0 +1,102 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class DomainSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_domain.has_domain import HasDomain + from xhiveframework.types import DF + + active_domains: DF.Table[HasDomain] + + # end: auto-generated types + def set_active_domains(self, domains): + active_domains = [d.domain for d in self.active_domains] + added = False + for d in domains: + if d not in active_domains: + self.append("active_domains", dict(domain=d)) + added = True + + if added: + self.save() + + def on_update(self): + for i, d in enumerate(self.active_domains): + # set the flag to update the the desktop icons of all domains + if i >= 1: + xhiveframework.flags.keep_desktop_icons = True + domain = xhiveframework.get_doc("Domain", d.domain) + domain.setup_domain() + + self.restrict_roles_and_modules() + xhiveframework.clear_cache() + + def restrict_roles_and_modules(self): + """Disable all restricted roles and set `restrict_to_domain` property in Module Def""" + active_domains = xhiveframework.get_active_domains() + all_domains = list(xhiveframework.get_hooks("domains") or {}) + + def remove_role(role): + xhiveframework.db.delete("Has Role", {"role": role}) + xhiveframework.set_value("Role", role, "disabled", 1) + + for domain in all_domains: + data = xhiveframework.get_domain_data(domain) + if not xhiveframework.db.get_value("Domain", domain): + xhiveframework.get_doc(dict(doctype="Domain", domain=domain)).insert() + if "modules" in data: + for module in data.get("modules"): + xhiveframework.db.set_value("Module Def", module, "restrict_to_domain", domain) + + if "restricted_roles" in data: + for role in data["restricted_roles"]: + if not xhiveframework.db.get_value("Role", role): + xhiveframework.get_doc(dict(doctype="Role", role_name=role)).insert() + xhiveframework.db.set_value("Role", role, "restrict_to_domain", domain) + + if domain not in active_domains: + remove_role(role) + + if "custom_fields" in data: + if domain not in active_domains: + inactive_domain = xhiveframework.get_doc("Domain", domain) + inactive_domain.setup_data() + inactive_domain.remove_custom_field() + + +def get_active_domains(): + """get the domains set in the Domain Settings as active domain""" + + def _get_active_domains(): + domains = xhiveframework.get_all( + "Has Domain", filters={"parent": "Domain Settings"}, fields=["domain"], distinct=True + ) + + active_domains = [row.get("domain") for row in domains] + active_domains.append("") + return active_domains + + return xhiveframework.cache.get_value("active_domains", _get_active_domains) + + +def get_active_modules(): + """get the active modules from Module Def""" + + def _get_active_modules(): + active_modules = [] + active_domains = get_active_domains() + for m in xhiveframework.get_all("Module Def", fields=["name", "restrict_to_domain"]): + if (not m.restrict_to_domain) or (m.restrict_to_domain in active_domains): + active_modules.append(m.name) + return active_modules + + return xhiveframework.cache.get_value("active_modules", _get_active_modules) diff --git a/xhiveframework/core/doctype/dynamic_link/__init__.py b/xhiveframework/core/doctype/dynamic_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/dynamic_link/dynamic_link.json b/xhiveframework/core/doctype/dynamic_link/dynamic_link.json new file mode 100644 index 0000000..b99f77f --- /dev/null +++ b/xhiveframework/core/doctype/dynamic_link/dynamic_link.json @@ -0,0 +1,47 @@ +{ + "creation": "2017-01-13 04:55:18.835023", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "link_doctype", + "link_name", + "link_title" + ], + "fields": [ + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Link Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "link_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Link Name", + "options": "link_doctype", + "reqd": 1 + }, + { + "fieldname": "link_title", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Link Title", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-10-10 22:05:54.736093", + "modified_by": "Administrator", + "module": "Core", + "name": "Dynamic Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/dynamic_link/dynamic_link.py b/xhiveframework/core/doctype/dynamic_link/dynamic_link.py new file mode 100644 index 0000000..1cfabd7 --- /dev/null +++ b/xhiveframework/core/doctype/dynamic_link/dynamic_link.py @@ -0,0 +1,43 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class DynamicLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + link_doctype: DF.Link + link_name: DF.DynamicLink + link_title: DF.ReadOnly | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass + + +def on_doctype_update(): + xhiveframework.db.add_index("Dynamic Link", ["link_doctype", "link_name"]) + + +def deduplicate_dynamic_links(doc): + links, duplicate = [], False + for l in doc.links or []: + t = (l.link_doctype, l.link_name) + if t not in links: + links.append(t) + else: + duplicate = True + + if duplicate: + doc.links = [] + for l in links: + doc.append("links", dict(link_doctype=l[0], link_name=l[1])) diff --git a/xhiveframework/core/doctype/error_log/__init__.py b/xhiveframework/core/doctype/error_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/error_log/error_log.js b/xhiveframework/core/doctype/error_log/error_log.js new file mode 100644 index 0000000..ff74503 --- /dev/null +++ b/xhiveframework/core/doctype/error_log/error_log.js @@ -0,0 +1,17 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Error Log", { + refresh: function (frm) { + frm.disable_save(); + + if (frm.doc.reference_doctype && frm.doc.reference_name) { + frm.add_custom_button(__("Show Related Errors"), function () { + xhiveframework.set_route("List", "Error Log", { + reference_doctype: frm.doc.reference_doctype, + reference_name: frm.doc.reference_name, + }); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/error_log/error_log.json b/xhiveframework/core/doctype/error_log/error_log.json new file mode 100644 index 0000000..813fb5f --- /dev/null +++ b/xhiveframework/core/doctype/error_log/error_log.json @@ -0,0 +1,95 @@ +{ + "actions": [], + "creation": "2013-01-16 13:09:40", + "doctype": "DocType", + "document_type": "System", + "engine": "MyISAM", + "field_order": [ + "seen", + "reference_doctype", + "column_break_3", + "reference_name", + "section_break_5", + "method", + "error", + "trace_id" + ], + "fields": [ + { + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "hidden": 1, + "label": "Seen" + }, + { + "fieldname": "method", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "read_only": 1 + }, + { + "fieldname": "error", + "fieldtype": "Code", + "label": "Error", + "read_only": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference DocType", + "options": "DocType", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "trace_id", + "fieldtype": "Data", + "label": "Trace ID", + "read_only": 1 + } + ], + "icon": "fa fa-warning-sign", + "idx": 1, + "in_create": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Core", + "name": "Error Log", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "method" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/error_log/error_log.py b/xhiveframework/core/doctype/error_log/error_log.py new file mode 100644 index 0000000..5ec6a85 --- /dev/null +++ b/xhiveframework/core/doctype/error_log/error_log.py @@ -0,0 +1,42 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.query_builder import Interval +from xhiveframework.query_builder.functions import Now + + +class ErrorLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + error: DF.Code | None + method: DF.Data | None + reference_doctype: DF.Link | None + reference_name: DF.Data | None + seen: DF.Check + trace_id: DF.Data | None + + # end: auto-generated types + def onload(self): + if not self.seen and not xhiveframework.flags.read_only: + self.db_set("seen", 1, update_modified=0) + xhiveframework.db.commit() + + @staticmethod + def clear_old_logs(days=30): + table = xhiveframework.qb.DocType("Error Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + +@xhiveframework.whitelist() +def clear_error_logs(): + """Flush all Error Logs""" + xhiveframework.only_for("System Manager") + xhiveframework.db.truncate("Error Log") diff --git a/xhiveframework/core/doctype/error_log/error_log_list.js b/xhiveframework/core/doctype/error_log/error_log_list.js new file mode 100644 index 0000000..2bf975f --- /dev/null +++ b/xhiveframework/core/doctype/error_log/error_log_list.js @@ -0,0 +1,25 @@ +xhiveframework.listview_settings["Error Log"] = { + add_fields: ["seen"], + get_indicator: function (doc) { + if (cint(doc.seen)) { + return [__("Seen"), "green", "seen,=,1"]; + } else { + return [__("Not Seen"), "red", "seen,=,0"]; + } + }, + order_by: "seen asc, modified desc", + onload: function (listview) { + listview.page.add_menu_item(__("Clear Error Logs"), function () { + xhiveframework.call({ + method: "xhiveframework.core.doctype.error_log.error_log.clear_error_logs", + callback: function () { + listview.refresh(); + }, + }); + }); + + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(cur_list.doctype); + }); + }, +}; diff --git a/xhiveframework/core/doctype/error_log/test_error_log.py b/xhiveframework/core/doctype/error_log/test_error_log.py new file mode 100644 index 0000000..a68e625 --- /dev/null +++ b/xhiveframework/core/doctype/error_log/test_error_log.py @@ -0,0 +1,72 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from unittest.mock import patch + +from ldap3.core.exceptions import LDAPException, LDAPInappropriateAuthenticationResult + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils.error import _is_ldap_exception, guess_exception_source + +# test_records = xhiveframework.get_test_records('Error Log') + + +class TestErrorLog(XhiveFrameworkTestCase): + def test_error_log(self): + """let's do an error log on error log?""" + doc = xhiveframework.new_doc("Error Log") + error = doc.log_error("This is an error") + self.assertEqual(error.doctype, "Error Log") + + def test_ldap_exceptions(self): + exc = [LDAPException, LDAPInappropriateAuthenticationResult] + + for e in exc: + self.assertTrue(_is_ldap_exception(e())) + + +_RAW_EXC = """ + File "apps/xhiveframework/xhiveframework/model/document.py", line 1284, in runner + add_to_return_value(self, fn(self, *args, **kwargs)) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "apps/xhiveframework/xhiveframework/model/document.py", line 933, in fn + return method_object(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "apps/xhiveerp/xhiveerp/selling/doctype/sales_order/sales_order.py", line 58, in onload + raise Exception("what") + Exception: what +""" + +_THROW_EXC = """ + File "apps/xhiveframework/xhiveframework/model/document.py", line 933, in fn + return method_object(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "apps/xhiveerp/xhiveerp/selling/doctype/sales_order/sales_order.py", line 58, in onload + xhiveframework.throw("what") + File "apps/xhiveframework/xhiveframework/__init__.py", line 550, in throw + msgprint( + File "apps/xhiveframework/xhiveframework/__init__.py", line 518, in msgprint + _raise_exception() + File "apps/xhiveframework/xhiveframework/__init__.py", line 467, in _raise_exception + raise raise_exception(msg) + xhiveframework.exceptions.ValidationError: what +""" + +TEST_EXCEPTIONS = ( + ( + "xhiveerp (app)", + _RAW_EXC, + ), + ( + "xhiveerp (app)", + _THROW_EXC, + ), +) + + +class TestExceptionSourceGuessing(XhiveFrameworkTestCase): + @patch.object(xhiveframework, "get_installed_apps", return_value=["xhiveframework", "xhiveerp", "3pa"]) + def test_exc_source_guessing(self, _installed_apps): + for source, exc in TEST_EXCEPTIONS: + result = guess_exception_source(exc) + self.assertEqual(result, source) diff --git a/xhiveframework/core/doctype/file/__init__.py b/xhiveframework/core/doctype/file/__init__.py new file mode 100644 index 0000000..ad28c17 --- /dev/null +++ b/xhiveframework/core/doctype/file/__init__.py @@ -0,0 +1,2 @@ +from .exceptions import * +from .utils import * diff --git a/xhiveframework/core/doctype/file/exceptions.py b/xhiveframework/core/doctype/file/exceptions.py new file mode 100644 index 0000000..f892bb4 --- /dev/null +++ b/xhiveframework/core/doctype/file/exceptions.py @@ -0,0 +1,16 @@ +import xhiveframework + + +class MaxFileSizeReachedError(xhiveframework.ValidationError): + pass + + +class FolderNotEmpty(xhiveframework.ValidationError): + pass + + +class FileTypeNotAllowed(xhiveframework.ValidationError): + pass + + +from xhiveframework.exceptions import * diff --git a/xhiveframework/core/doctype/file/file.js b/xhiveframework/core/doctype/file/file.js new file mode 100644 index 0000000..5fd3868 --- /dev/null +++ b/xhiveframework/core/doctype/file/file.js @@ -0,0 +1,106 @@ +xhiveframework.ui.form.on("File", { + refresh: function (frm) { + if (!frm.doc.is_folder) { + // add download button + frm.add_custom_button(__("Download"), () => frm.trigger("download"), "fa fa-download"); + } + + if (!frm.doc.is_private) { + frm.dashboard.set_headline( + __("This file is public. It can be accessed without authentication."), + "orange" + ); + } + + frm.toggle_display("preview", false); + + // preview different file types + frm.trigger("preview_file"); + + let is_raster_image = /\.(gif|jpg|jpeg|tiff|png)$/i.test(frm.doc.file_url); + let is_optimizable = !frm.doc.is_folder && is_raster_image && frm.doc.file_size > 0; + + // add optimize button + is_optimizable && frm.add_custom_button(__("Optimize"), () => frm.trigger("optimize")); + + // add unzip button + if (frm.doc.file_name && frm.doc.file_name.split(".").splice(-1)[0] === "zip") { + frm.add_custom_button(__("Unzip"), () => frm.trigger("unzip")); + } + if (frm.doc.file_url) { + frm.add_web_link(frm.doc.file_url, __("View file")); + } + }, + + preview_file: function (frm) { + let $preview = ""; + let file_extension = frm.doc.file_type.toLowerCase(); + + if (xhiveframework.utils.is_image_file(frm.doc.file_url)) { + $preview = $(`
    + +
    `); + } else if (xhiveframework.utils.is_video_file(frm.doc.file_url)) { + $preview = $(`
    + +
    `); + } else if (file_extension === "pdf") { + $preview = $(`
    + + + +
    `); + } else if (file_extension === "mp3") { + $preview = $(`
    + +
    `); + } + + if ($preview) { + frm.toggle_display("preview", true); + frm.get_field("preview_html").$wrapper.html($preview); + } + }, + + download: function (frm) { + let file_url = frm.doc.file_url; + if (frm.doc.file_name) { + file_url = file_url.replace(/#/g, "%23"); + } + window.open(file_url); + }, + + optimize: function (frm) { + xhiveframework.show_alert(__("Optimizing image...")); + frm.call("optimize_file").then(() => { + xhiveframework.show_alert(__("Image optimized")); + }); + }, + + unzip: function (frm) { + xhiveframework.call({ + method: "xhiveframework.core.api.file.unzip_file", + args: { + name: frm.doc.name, + }, + callback: function () { + xhiveframework.set_route("List", "File"); + }, + }); + }, +}); diff --git a/xhiveframework/core/doctype/file/file.json b/xhiveframework/core/doctype/file/file.json new file mode 100644 index 0000000..215178d --- /dev/null +++ b/xhiveframework/core/doctype/file/file.json @@ -0,0 +1,224 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2012-12-12 11:19:22", + "default_view": "File", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "file_name", + "is_private", + "column_break_7jmm", + "file_type", + "preview", + "preview_html", + "section_break_5", + "is_home_folder", + "is_attachments_folder", + "file_size", + "column_break_5", + "file_url", + "thumbnail_url", + "folder", + "is_folder", + "section_break_8", + "attached_to_doctype", + "column_break_10", + "attached_to_name", + "attached_to_field", + "old_parent", + "content_hash", + "uploaded_to_dropbox", + "uploaded_to_google_drive" + ], + "fields": [ + { + "fieldname": "file_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "File Name", + "oldfieldname": "file_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:!doc.is_folder", + "fieldname": "is_private", + "fieldtype": "Check", + "label": "Is Private" + }, + { + "fieldname": "preview", + "fieldtype": "Section Break", + "label": "Preview" + }, + { + "fieldname": "preview_html", + "fieldtype": "HTML", + "label": "Preview HTML" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "is_home_folder", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Home Folder" + }, + { + "default": "0", + "fieldname": "is_attachments_folder", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Attachments Folder" + }, + { + "fieldname": "file_size", + "fieldtype": "Int", + "in_list_view": 1, + "label": "File Size", + "length": 20, + "options": "File Size", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.is_folder", + "fieldname": "file_url", + "fieldtype": "Code", + "label": "File URL", + "read_only": 1 + }, + { + "fieldname": "thumbnail_url", + "fieldtype": "Small Text", + "label": "Thumbnail URL", + "read_only": 1 + }, + { + "fieldname": "folder", + "fieldtype": "Link", + "hidden": 1, + "label": "Folder", + "options": "File", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_folder", + "fieldtype": "Check", + "label": "Is Folder", + "read_only": 1 + }, + { + "depends_on": "eval:!doc.is_folder", + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "attached_to_doctype", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Attached To DocType", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "attached_to_name", + "fieldtype": "Data", + "label": "Attached To Name", + "read_only": 1 + }, + { + "fieldname": "attached_to_field", + "fieldtype": "Data", + "label": "Attached To Field", + "read_only": 1 + }, + { + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "label": "old_parent" + }, + { + "fieldname": "content_hash", + "fieldtype": "Data", + "label": "Content Hash", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "uploaded_to_dropbox", + "fieldtype": "Check", + "label": "Uploaded To Dropbox", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "uploaded_to_google_drive", + "fieldtype": "Check", + "label": "Uploaded To Google Drive", + "read_only": 1 + }, + { + "fieldname": "column_break_7jmm", + "fieldtype": "Column Break" + }, + { + "fieldname": "file_type", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "File Type", + "read_only": 1 + } + ], + "force_re_route_to_default_view": 1, + "icon": "fa fa-file", + "idx": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Core", + "name": "File", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "role": "All", + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "file_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/file/file.py b/xhiveframework/core/doctype/file/file.py new file mode 100755 index 0000000..c2b77b1 --- /dev/null +++ b/xhiveframework/core/doctype/file/file.py @@ -0,0 +1,835 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import io +import mimetypes +import os +import re +import shutil +import zipfile +from urllib.parse import quote, unquote + +from PIL import Image, ImageFile, ImageOps + +import xhiveframework +from xhiveframework import _ +from xhiveframework.database.schema import SPECIAL_CHAR_PATTERN +from xhiveframework.model.document import Document +from xhiveframework.permissions import get_doctypes_with_read +from xhiveframework.utils import call_hook_method, cint, get_files_path, get_hook_method, get_url +from xhiveframework.utils.file_manager import is_safe_path +from xhiveframework.utils.image import optimize_image, strip_exif_data + +from .exceptions import ( + AttachmentLimitReached, + FileTypeNotAllowed, + FolderNotEmpty, + MaxFileSizeReachedError, +) +from .utils import * + +exclude_from_linked_with = True +ImageFile.LOAD_TRUNCATED_IMAGES = True +URL_PREFIXES = ("http://", "https://") + + +class File(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + attached_to_doctype: DF.Link | None + attached_to_field: DF.Data | None + attached_to_name: DF.Data | None + content_hash: DF.Data | None + file_name: DF.Data | None + file_size: DF.Int + file_type: DF.Data | None + file_url: DF.Code | None + folder: DF.Link | None + is_attachments_folder: DF.Check + is_folder: DF.Check + is_home_folder: DF.Check + is_private: DF.Check + old_parent: DF.Data | None + thumbnail_url: DF.SmallText | None + uploaded_to_dropbox: DF.Check + uploaded_to_google_drive: DF.Check + # end: auto-generated types + no_feed_on_delete = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # if content is set, file_url will be generated + # decode comes in the picture if content passed has to be decoded before writing to disk + + self.content = self.get("content") or b"" + self.decode = self.get("decode", False) + + @property + def is_remote_file(self): + if self.file_url: + return self.file_url.startswith(URL_PREFIXES) + return not self.content + + def autoname(self): + """Set name for folder""" + if self.is_folder: + if self.folder: + self.name = self.get_name_based_on_parent_folder() + else: + # home + self.name = self.file_name + else: + self.name = xhiveframework.generate_hash(length=10) + + def before_insert(self): + self.set_folder_name() + self.set_file_name() + self.validate_attachment_limit() + self.set_file_type() + self.validate_file_extension() + + if self.is_folder: + return + + if self.is_remote_file: + self.validate_remote_file() + else: + self.save_file(content=self.get_content()) + self.flags.new_file = True + xhiveframework.db.after_rollback.add(self.on_rollback) + + def after_insert(self): + if not self.is_folder: + self.create_attachment_record() + self.set_is_private() + self.set_file_name() + self.validate_duplicate_entry() + + def validate(self): + if self.is_folder: + return + + # Ensure correct formatting and type + self.file_url = unquote(self.file_url) if self.file_url else "" + + self.validate_attachment_references() + + # when dict is passed to get_doc for creation of new_doc, is_new returns None + # this case is handled inside handle_is_private_changed + if not self.is_new() and self.has_value_changed("is_private"): + self.handle_is_private_changed() + + self.validate_file_path() + self.validate_file_url() + self.validate_file_on_disk() + + self.file_size = xhiveframework.form_dict.file_size or self.file_size + + def validate_attachment_references(self): + if not self.attached_to_doctype: + return + + if not self.attached_to_name or not isinstance(self.attached_to_name, str | int): + xhiveframework.throw(_("Attached To Name must be a string or an integer"), xhiveframework.ValidationError) + + if self.attached_to_field and SPECIAL_CHAR_PATTERN.search(self.attached_to_field): + xhiveframework.throw(_("The fieldname you've specified in Attached To Field is invalid")) + + def after_rename(self, *args, **kwargs): + for successor in self.get_successors(): + setup_folder_path(successor, self.name) + + def on_trash(self): + if self.is_home_folder or self.is_attachments_folder: + xhiveframework.throw(_("Cannot delete Home and Attachments folders")) + self.validate_empty_folder() + self._delete_file_on_disk() + if not self.is_folder: + self.add_comment_in_reference_doc("Attachment Removed", _("Removed {0}").format(self.file_name)) + + def on_rollback(self): + rollback_flags = ("new_file", "original_content", "original_path") + + def pop_rollback_flags(): + for flag in rollback_flags: + self.flags.pop(flag, None) + + # following condition is only executed when an insert has been rolledback + if self.flags.new_file: + self._delete_file_on_disk() + pop_rollback_flags() + return + + # if original_content flag is set, this rollback should revert the file to its original state + if self.flags.original_content: + file_path = self.get_full_path() + + if isinstance(self.flags.original_content, bytes): + mode = "wb+" + elif isinstance(self.flags.original_content, str): + mode = "w+" + + with open(file_path, mode) as f: + f.write(self.flags.original_content) + os.fsync(f.fileno()) + pop_rollback_flags() + + # used in case file path (File.file_url) has been changed + if self.flags.original_path: + target = self.flags.original_path["old"] + source = self.flags.original_path["new"] + shutil.move(source, target) + pop_rollback_flags() + + def get_name_based_on_parent_folder(self) -> str | None: + if self.folder: + return os.path.join(self.folder, self.file_name) + + def get_successors(self): + return xhiveframework.get_all("File", filters={"folder": self.name}, pluck="name") + + def validate_file_path(self): + if self.is_remote_file: + return + + base_path = os.path.realpath(get_files_path(is_private=self.is_private)) + if not os.path.realpath(self.get_full_path()).startswith(base_path): + xhiveframework.throw( + _("The File URL you've entered is incorrect"), + title=_("Invalid File URL"), + ) + + def validate_file_url(self): + if self.is_remote_file or not self.file_url: + return + + if not self.file_url.startswith(("/files/", "/private/files/")): + # Probably an invalid URL since it doesn't start with http either + xhiveframework.throw( + _("URL must start with http:// or https://"), + title=_("Invalid URL"), + ) + + def handle_is_private_changed(self): + if self.is_remote_file: + return + + from pathlib import Path + + old_file_url = self.file_url + file_name = self.file_url.split("/")[-1] + private_file_path = Path(xhiveframework.get_site_path("private", "files", file_name)) + public_file_path = Path(xhiveframework.get_site_path("public", "files", file_name)) + + if cint(self.is_private): + source = public_file_path + target = private_file_path + url_starts_with = "/private/files/" + else: + source = private_file_path + target = public_file_path + url_starts_with = "/files/" + updated_file_url = f"{url_starts_with}{file_name}" + + # if a file document is created by passing dict throught get_doc and __local is not set, + # handle_is_private_changed would be executed; we're checking if updated_file_url is same + # as old_file_url to avoid a FileNotFoundError for this case. + if updated_file_url == old_file_url: + return + + if not source.exists(): + xhiveframework.throw( + _("Cannot find file {} on disk").format(source), + exc=FileNotFoundError, + ) + if target.exists(): + xhiveframework.throw( + _("A file with same name {} already exists").format(target), + exc=FileExistsError, + ) + + # Uses os.rename which is an atomic operation + shutil.move(source, target) + self.flags.original_path = {"old": source, "new": target} + xhiveframework.db.after_rollback.add(self.on_rollback) + + self.file_url = updated_file_url + update_existing_file_docs(self) + + if ( + not self.attached_to_doctype + or not self.attached_to_name + or not self.fetch_attached_to_field(old_file_url) + ): + return + + if xhiveframework.get_meta(self.attached_to_doctype).issingle: + xhiveframework.db.set_single_value( + self.attached_to_doctype, + self.attached_to_field, + self.file_url, + ) + else: + xhiveframework.db.set_value( + self.attached_to_doctype, + self.attached_to_name, + self.attached_to_field, + self.file_url, + ) + + def fetch_attached_to_field(self, old_file_url): + if self.attached_to_field: + return True + + reference_dict = xhiveframework.get_doc(self.attached_to_doctype, self.attached_to_name).as_dict() + + for key, value in reference_dict.items(): + if value == old_file_url: + self.attached_to_field = key + return True + + def validate_attachment_limit(self): + attachment_limit = 0 + if self.attached_to_doctype and self.attached_to_name: + attachment_limit = cint(xhiveframework.get_meta(self.attached_to_doctype).max_attachments) + + if attachment_limit: + current_attachment_count = len( + xhiveframework.get_all( + "File", + filters={ + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_name, + }, + limit=attachment_limit + 1, + ) + ) + + if current_attachment_count >= attachment_limit: + xhiveframework.throw( + _("Maximum Attachment Limit of {0} has been reached for {1} {2}.").format( + xhiveframework.bold(attachment_limit), self.attached_to_doctype, self.attached_to_name + ), + exc=AttachmentLimitReached, + title=_("Attachment Limit Reached"), + ) + + def validate_remote_file(self): + """Validates if file uploaded using URL already exist""" + site_url = get_url() + if self.file_url and "/files/" in self.file_url and self.file_url.startswith(site_url): + self.file_url = self.file_url.split(site_url, 1)[1] + + def set_folder_name(self): + """Make parent folders if not exists based on reference doctype and name""" + if self.folder: + return + + if self.attached_to_doctype: + self.folder = xhiveframework.db.get_value("File", {"is_attachments_folder": 1}) + + elif not self.is_home_folder: + self.folder = "Home" + + def set_file_type(self): + if self.is_folder: + return + + file_type = mimetypes.guess_type(self.file_name)[0] + if not file_type: + return + + file_extension = mimetypes.guess_extension(file_type) + self.file_type = file_extension.lstrip(".").upper() if file_extension else None + + def validate_file_on_disk(self): + """Validates existence file""" + full_path = self.get_full_path() + + if full_path.startswith(URL_PREFIXES): + return True + + if not os.path.exists(full_path): + xhiveframework.throw(_("File {0} does not exist").format(self.file_url), IOError) + + def validate_file_extension(self): + # Only validate uploaded files, not generated by code/integrations. + if not self.file_type or not xhiveframework.request: + return + + allowed_extensions = xhiveframework.get_system_settings("allowed_file_extensions") + if not allowed_extensions: + return + + if self.file_type not in allowed_extensions.splitlines(): + xhiveframework.throw(_("File type of {0} is not allowed").format(self.file_type), exc=FileTypeNotAllowed) + + def validate_duplicate_entry(self): + if not self.flags.ignore_duplicate_entry_error and not self.is_folder: + if not self.content_hash: + self.generate_content_hash() + + # check duplicate name + # check duplicate assignment + filters = { + "content_hash": self.content_hash, + "is_private": self.is_private, + "name": ("!=", self.name), + } + if self.attached_to_doctype and self.attached_to_name: + filters.update( + { + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_name, + } + ) + duplicate_file = xhiveframework.db.get_value("File", filters, ["name", "file_url"], as_dict=1) + + if duplicate_file: + duplicate_file_doc = xhiveframework.get_cached_doc("File", duplicate_file.name) + if duplicate_file_doc.exists_on_disk(): + # just use the url, to avoid uploading a duplicate + self.file_url = duplicate_file.file_url + + def set_file_name(self): + if not self.file_name and not self.file_url: + xhiveframework.throw( + _("Fields `file_name` or `file_url` must be set for File"), exc=xhiveframework.MandatoryError + ) + elif not self.file_name and self.file_url: + self.file_name = self.file_url.split("/")[-1] + else: + self.file_name = re.sub(r"/", "", self.file_name) + + def generate_content_hash(self): + if self.content_hash or not self.file_url or self.is_remote_file: + return + file_name = self.file_url.split("/")[-1] + try: + file_path = get_files_path(file_name, is_private=self.is_private) + with open(file_path, "rb") as f: + self.content_hash = get_content_hash(f.read()) + except OSError: + xhiveframework.throw(_("File {0} does not exist").format(file_path)) + + def make_thumbnail( + self, + set_as_thumbnail: bool = True, + width: int = 300, + height: int = 300, + suffix: str = "small", + crop: bool = False, + ) -> str: + from requests.exceptions import HTTPError, SSLError + + if not self.file_url: + return + + try: + if self.file_url.startswith(("/files", "/private/files")): + image, filename, extn = get_local_image(self.file_url) + else: + image, filename, extn = get_web_image(self.file_url) + except (HTTPError, SSLError, OSError, TypeError): + return + + size = width, height + if crop: + image = ImageOps.fit(image, size, Image.Resampling.LANCZOS) + else: + image.thumbnail(size, Image.Resampling.LANCZOS) + + thumbnail_url = f"{filename}_{suffix}.{extn}" + path = os.path.abspath(xhiveframework.get_site_path("public", thumbnail_url.lstrip("/"))) + + try: + image.save(path) + if set_as_thumbnail: + self.db_set("thumbnail_url", thumbnail_url) + + except OSError: + xhiveframework.msgprint(_("Unable to write file format for {0}").format(path)) + return + + return thumbnail_url + + def validate_empty_folder(self): + """Throw exception if folder is not empty""" + if self.is_folder and xhiveframework.get_all("File", filters={"folder": self.name}, limit=1): + xhiveframework.throw(_("Folder {0} is not empty").format(self.name), FolderNotEmpty) + + def _delete_file_on_disk(self): + """If file not attached to any other record, delete it""" + on_disk_file_not_shared = self.content_hash and not xhiveframework.get_all( + "File", + filters={"content_hash": self.content_hash, "name": ["!=", self.name]}, + limit=1, + ) + if on_disk_file_not_shared: + self.delete_file_data_content() + else: + self.delete_file_data_content(only_thumbnail=True) + + def unzip(self) -> list["File"]: + """Unzip current file and replace it by its children""" + if not self.file_url.endswith(".zip"): + xhiveframework.throw(_("{0} is not a zip file").format(self.file_name)) + + zip_path = self.get_full_path() + + files = [] + with zipfile.ZipFile(zip_path) as z: + for file in z.filelist: + if file.is_dir() or file.filename.startswith("__MACOSX/"): + # skip directories and macos hidden directory + continue + + filename = os.path.basename(file.filename) + if filename.startswith("."): + # skip hidden files + continue + + file_doc = xhiveframework.new_doc("File") + try: + file_doc.content = z.read(file.filename) + except zipfile.BadZipFile: + xhiveframework.throw(_("{0} is a not a valid zip file").format(self.file_name)) + file_doc.file_name = filename + file_doc.folder = self.folder + file_doc.is_private = self.is_private + file_doc.attached_to_doctype = self.attached_to_doctype + file_doc.attached_to_name = self.attached_to_name + file_doc.save() + files.append(file_doc) + + xhiveframework.delete_doc("File", self.name) + return files + + def exists_on_disk(self): + return os.path.exists(self.get_full_path()) + + def get_content(self) -> bytes: + if self.is_folder: + xhiveframework.throw(_("Cannot get file contents of a Folder")) + + if self.get("content"): + self._content = self.content + if self.decode: + self._content = decode_file_content(self._content) + self.decode = False + # self.content = None # TODO: This needs to happen; make it happen somehow + return self._content + + if self.file_url: + self.validate_file_url() + file_path = self.get_full_path() + + # read the file + with open(file_path, mode="rb") as f: + self._content = f.read() + try: + # for plain text files + self._content = self._content.decode() + except UnicodeDecodeError: + # for .png, .jpg, etc + pass + + return self._content + + def get_full_path(self): + """Returns file path from given file name""" + + file_path = self.file_url or self.file_name + + site_url = get_url() + if "/files/" in file_path and file_path.startswith(site_url): + file_path = file_path.split(site_url, 1)[1] + + if "/" not in file_path: + if self.is_private: + file_path = f"/private/files/{file_path}" + else: + file_path = f"/files/{file_path}" + + if file_path.startswith("/private/files/"): + file_path = get_files_path(*file_path.split("/private/files/", 1)[1].split("/"), is_private=1) + + elif file_path.startswith("/files/"): + file_path = get_files_path(*file_path.split("/files/", 1)[1].split("/")) + + elif file_path.startswith(URL_PREFIXES): + pass + + elif not self.file_url: + xhiveframework.throw(_("There is some problem with the file url: {0}").format(file_path)) + + if not is_safe_path(file_path): + xhiveframework.throw(_("Cannot access file path {0}").format(file_path)) + + if os.path.sep in self.file_name: + xhiveframework.throw(_("File name cannot have {0}").format(os.path.sep)) + + return file_path + + def write_file(self): + """write file to disk with a random name (to compare)""" + if self.is_remote_file: + return + + file_path = self.get_full_path() + + if isinstance(self._content, str): + self._content = self._content.encode() + + with open(file_path, "wb+") as f: + f.write(self._content) + os.fsync(f.fileno()) + + xhiveframework.db.after_rollback.add(self.on_rollback) + + return file_path + + def save_file( + self, + content: bytes | str | None = None, + decode=False, + ignore_existing_file_check=False, + overwrite=False, + ): + if self.is_remote_file: + return + + if not self.flags.new_file: + self.flags.original_content = self.get_content() + + if content: + self.content = content + self.decode = decode + self.get_content() + + if not self._content: + return + + file_exists = False + duplicate_file = None + + self.is_private = cint(self.is_private) + self.content_type = mimetypes.guess_type(self.file_name)[0] + + # transform file content based on site settings + if ( + self.content_type + and self.content_type == "image/jpeg" + and xhiveframework.get_system_settings("strip_exif_metadata_from_uploaded_images") + ): + self._content = strip_exif_data(self._content, self.content_type) + + self.file_size = self.check_max_file_size() + self.content_hash = get_content_hash(self._content) + + # check if a file exists with the same content hash and is also in the same folder (public or private) + if not ignore_existing_file_check: + duplicate_file = xhiveframework.get_value( + "File", + {"content_hash": self.content_hash, "is_private": self.is_private}, + ["file_url", "name"], + as_dict=True, + ) + + if duplicate_file: + file_doc: "File" = xhiveframework.get_cached_doc("File", duplicate_file.name) + if file_doc.exists_on_disk(): + self.file_url = duplicate_file.file_url + file_exists = True + + if not file_exists: + if not overwrite: + self.file_name = generate_file_name( + name=self.file_name, + suffix=self.content_hash[-6:], + is_private=self.is_private, + ) + call_hook_method("before_write_file", file_size=self.file_size) + write_file_method = get_hook_method("write_file") + if write_file_method: + return write_file_method(self) + return self.save_file_on_filesystem() + + def save_file_on_filesystem(self): + if self.is_private: + self.file_url = f"/private/files/{self.file_name}" + else: + self.file_url = f"/files/{self.file_name}" + + fpath = self.write_file() + + return {"file_name": os.path.basename(fpath), "file_url": self.file_url} + + def check_max_file_size(self): + from xhiveframework.core.api.file import get_max_file_size + + max_file_size = get_max_file_size() + file_size = len(self._content or b"") + + if file_size > max_file_size: + msg = _("File size exceeded the maximum allowed size of {0} MB").format(max_file_size / 1048576) + if xhiveframework.has_permission("System Settings", "write"): + msg += ".
    " + _("You can increase the limit from System Settings.") + xhiveframework.throw(msg, exc=MaxFileSizeReachedError) + + return file_size + + def delete_file_data_content(self, only_thumbnail=False): + method = get_hook_method("delete_file_data_content") + if method: + method(self, only_thumbnail=only_thumbnail) + else: + self.delete_file_from_filesystem(only_thumbnail=only_thumbnail) + + def delete_file_from_filesystem(self, only_thumbnail=False): + """Delete file, thumbnail from File document""" + if only_thumbnail: + delete_file(self.thumbnail_url) + else: + delete_file(self.file_url) + delete_file(self.thumbnail_url) + + def is_downloadable(self): + return has_permission(self, "read") + + def get_extension(self): + """returns split filename and extension""" + return os.path.splitext(self.file_name) + + def create_attachment_record(self): + icon = ' ' if self.is_private else "" + file_url = quote(xhiveframework.safe_encode(self.file_url), safe="/:") if self.file_url else self.file_name + file_name = self.file_name or self.file_url + + self.add_comment_in_reference_doc( + "Attachment", + _("Added {0}").format(f"{file_name}{icon}"), + ) + + def add_comment_in_reference_doc(self, comment_type, text): + if self.attached_to_doctype and self.attached_to_name: + try: + doc = xhiveframework.get_doc(self.attached_to_doctype, self.attached_to_name) + doc.add_comment(comment_type, text) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_messages() + + def set_is_private(self): + if self.file_url: + self.is_private = cint(self.file_url.startswith("/private")) + + @xhiveframework.whitelist() + def optimize_file(self): + if self.is_folder: + raise TypeError("Folders cannot be optimized") + + content_type = mimetypes.guess_type(self.file_name)[0] + is_local_image = content_type.startswith("image/") and self.file_size > 0 + is_svg = content_type == "image/svg+xml" + + if not is_local_image: + raise NotImplementedError("Only local image files can be optimized") + + if is_svg: + raise TypeError("Optimization of SVG images is not supported") + + original_content = self.get_content() + optimized_content = optimize_image( + content=original_content, + content_type=content_type, + ) + + self.save_file(content=optimized_content, overwrite=True) + self.save() + + @property + def unique_url(self) -> str: + """Unique URL contains file ID in URL to speed up permisison checks.""" + from urllib.parse import urlencode + + if self.is_private: + return self.file_url + "?" + urlencode({"fid": self.name}) + else: + return self.file_url + + @staticmethod + def zip_files(files): + zip_file = io.BytesIO() + zf = zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED) + for _file in files: + if isinstance(_file, str): + _file = xhiveframework.get_doc("File", _file) + if not isinstance(_file, File): + continue + if _file.is_folder: + continue + if not has_permission(_file, "read"): + continue + zf.writestr(_file.file_name, _file.get_content()) + zf.close() + return zip_file.getvalue() + + +def on_doctype_update(): + xhiveframework.db.add_index("File", ["attached_to_doctype", "attached_to_name"]) + + +def has_permission(doc, ptype=None, user=None, debug=False): + user = user or xhiveframework.session.user + + if user == "Administrator": + return True + + if ptype == "create": + return xhiveframework.has_permission("File", "create", user=user, debug=debug) + + if not doc.is_private and ptype in ("read", "select"): + return True + + if user != "Guest" and doc.owner == user: + return True + + if doc.attached_to_doctype and doc.attached_to_name: + attached_to_doctype = doc.attached_to_doctype + attached_to_name = doc.attached_to_name + + try: + ref_doc = xhiveframework.get_doc(attached_to_doctype, attached_to_name) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_last_message() + return False + + if ptype in ["write", "create", "delete"]: + return ref_doc.has_permission("write", debug=debug, user=user) + else: + return ref_doc.has_permission("read", debug=debug, user=user) + + return False + + +def get_permission_query_conditions(user: str | None = None) -> str: + user = user or xhiveframework.session.user + if user == "Administrator": + return "" + + readable_doctypes = ", ".join(repr(dt) for dt in get_doctypes_with_read()) + return f""" + (`tabFile`.`is_private` = 0) + OR (`tabFile`.`attached_to_doctype` IS NULL AND `tabFile`.`owner` = {user !r}) + OR (`tabFile`.`attached_to_doctype` IN ({readable_doctypes})) + """ + + +# Note: kept at the end to not cause circular, partial imports & maintain backwards compatibility +from xhiveframework.core.api.file import * diff --git a/xhiveframework/core/doctype/file/test_file.py b/xhiveframework/core/doctype/file/test_file.py new file mode 100644 index 0000000..dee1684 --- /dev/null +++ b/xhiveframework/core/doctype/file/test_file.py @@ -0,0 +1,878 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import base64 +import json +import os +import shutil +import tempfile +from contextlib import contextmanager +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.api.file import ( + create_new_folder, + get_attached_images, + get_files_in_folder, + move_file, + unzip_file, +) +from xhiveframework.core.doctype.file.exceptions import FileTypeNotAllowed +from xhiveframework.core.doctype.file.utils import delete_file, get_extension +from xhiveframework.exceptions import ValidationError +from xhiveframework.tests.utils import XhiveFrameworkTestCase, change_settings +from xhiveframework.utils import get_files_path, set_request + +if TYPE_CHECKING: + from xhiveframework.core.doctype.file.file import File + +test_content1 = "Hello" +test_content2 = "Hello World" + + +def make_test_doc(ignore_permissions=False): + d = xhiveframework.new_doc("ToDo") + d.description = "Test" + d.assigned_by = xhiveframework.session.user + d.save(ignore_permissions) + return d.doctype, d.name + + +@contextmanager +def make_test_image_file(private=False): + file_path = xhiveframework.get_app_path("xhiveframework", "tests/data/sample_image_for_optimization.jpg") + with open(file_path, "rb") as f: + file_content = f.read() + + test_file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "sample_image_for_optimization.jpg", + "content": file_content, + "is_private": private, + } + ).insert() + # remove those flags + _test_file: "File" = xhiveframework.get_doc("File", test_file.name) + + try: + yield _test_file + finally: + _test_file.delete() + + +class TestSimpleFile(XhiveFrameworkTestCase): + def setUp(self): + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + self.test_content = test_content1 + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "test1.txt", + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_docname, + "content": self.test_content, + } + ) + _file.save() + self.saved_file_url = _file.file_url + + def test_save(self): + _file = xhiveframework.get_doc("File", {"file_url": self.saved_file_url}) + content = _file.get_content() + self.assertEqual(content, self.test_content) + + +class TestFSRollbacks(XhiveFrameworkTestCase): + def test_rollback_from_file_system(self): + file_name = content = xhiveframework.generate_hash() + file = xhiveframework.new_doc("File", file_name=file_name, content=content).insert() + self.assertTrue(file.exists_on_disk()) + + xhiveframework.db.rollback() + self.assertFalse(file.exists_on_disk()) + + +class TestExtensionValidations(XhiveFrameworkTestCase): + @change_settings("System Settings", {"allowed_file_extensions": "JPG\nCSV"}) + def test_allowed_extension(self): + set_request(method="POST", path="/") + file_name = content = xhiveframework.generate_hash() + bad_file = xhiveframework.new_doc("File", file_name=f"{file_name}.png", content=content) + self.assertRaises(FileTypeNotAllowed, bad_file.insert) + + bad_file = xhiveframework.new_doc("File", file_name=f"{file_name}.csv", content=content).insert() + xhiveframework.db.rollback() + self.assertFalse(bad_file.exists_on_disk()) + + +class TestBase64File(XhiveFrameworkTestCase): + def setUp(self): + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + self.test_content = base64.b64encode(test_content1.encode("utf-8")) + _file: "File" = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "test_base64.txt", + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_docname, + "content": self.test_content, + "decode": True, + } + ) + _file.save() + self.saved_file_url = _file.file_url + + def test_saved_content(self): + _file = xhiveframework.get_doc("File", {"file_url": self.saved_file_url}) + content = _file.get_content() + self.assertEqual(content, test_content1) + + +class TestSameFileName(XhiveFrameworkTestCase): + def test_saved_content(self): + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + self.test_content1 = test_content1 + self.test_content2 = test_content2 + _file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "testing.txt", + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_docname, + "content": self.test_content1, + } + ) + _file1.save() + _file2 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "testing.txt", + "attached_to_doctype": self.attached_to_doctype, + "attached_to_name": self.attached_to_docname, + "content": self.test_content2, + } + ) + _file2.save() + self.saved_file_url1 = _file1.file_url + self.saved_file_url2 = _file2.file_url + + _file = xhiveframework.get_doc("File", {"file_url": self.saved_file_url1}) + content1 = _file.get_content() + self.assertEqual(content1, self.test_content1) + _file = xhiveframework.get_doc("File", {"file_url": self.saved_file_url2}) + content2 = _file.get_content() + self.assertEqual(content2, self.test_content2) + + def test_saved_content_private(self): + _file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "testing-private.txt", + "content": test_content1, + "is_private": 1, + } + ).insert() + _file2 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "testing-private.txt", + "content": test_content2, + "is_private": 1, + } + ).insert() + + _file = xhiveframework.get_doc("File", {"file_url": _file1.file_url}) + self.assertEqual(_file.get_content(), test_content1) + + _file = xhiveframework.get_doc("File", {"file_url": _file2.file_url}) + self.assertEqual(_file.get_content(), test_content2) + + +class TestSameContent(XhiveFrameworkTestCase): + def setUp(self): + self.attached_to_doctype1, self.attached_to_docname1 = make_test_doc() + self.attached_to_doctype2, self.attached_to_docname2 = make_test_doc() + self.test_content1 = test_content1 + self.test_content2 = test_content1 + self.orig_filename = "hello.txt" + self.dup_filename = "hello2.txt" + _file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": self.orig_filename, + "attached_to_doctype": self.attached_to_doctype1, + "attached_to_name": self.attached_to_docname1, + "content": self.test_content1, + } + ) + _file1.save() + + _file2 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": self.dup_filename, + "attached_to_doctype": self.attached_to_doctype2, + "attached_to_name": self.attached_to_docname2, + "content": self.test_content2, + } + ) + + _file2.save() + + def test_saved_content(self): + self.assertFalse(os.path.exists(get_files_path(self.dup_filename))) + + def test_attachment_limit(self): + doctype, docname = make_test_doc() + from xhiveframework.custom.doctype.property_setter.property_setter import make_property_setter + + limit_property = make_property_setter("ToDo", None, "max_attachments", 1, "int", for_doctype=True) + file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "test-attachment", + "attached_to_doctype": doctype, + "attached_to_name": docname, + "content": "test", + } + ) + + file1.insert() + + file2 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "test-attachment", + "attached_to_doctype": doctype, + "attached_to_name": docname, + "content": "test2", + } + ) + + self.assertRaises(xhiveframework.exceptions.AttachmentLimitReached, file2.insert) + limit_property.delete() + xhiveframework.clear_cache(doctype="ToDo") + + +class TestFile(XhiveFrameworkTestCase): + def setUp(self): + xhiveframework.set_user("Administrator") + self.delete_test_data() + self.upload_file() + + def tearDown(self): + try: + xhiveframework.get_doc("File", {"file_name": "file_copy.txt"}).delete() + except xhiveframework.DoesNotExistError: + pass + + def delete_test_data(self): + test_file_data = xhiveframework.get_all( + "File", + pluck="name", + filters={"is_home_folder": 0, "is_attachments_folder": 0}, + order_by="creation desc", + ) + for f in test_file_data: + xhiveframework.delete_doc("File", f) + + def upload_file(self): + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "file_copy.txt", + "attached_to_name": "", + "attached_to_doctype": "", + "folder": self.get_folder("Test Folder 1", "Home").name, + "content": "Testing file copy example.", + } + ) + _file.save() + self.saved_folder = _file.folder + self.saved_name = _file.name + self.saved_filename = get_files_path(_file.file_name) + + def get_folder(self, folder_name, parent_folder="Home"): + return xhiveframework.get_doc( + {"doctype": "File", "file_name": _(folder_name), "is_folder": 1, "folder": _(parent_folder)} + ).insert() + + def tests_after_upload(self): + self.assertEqual(self.saved_folder, _("Home/Test Folder 1")) + file_folder = xhiveframework.db.get_value("File", self.saved_name, "folder") + self.assertEqual(file_folder, _("Home/Test Folder 1")) + + def test_file_copy(self): + folder = self.get_folder("Test Folder 2", "Home") + + file = xhiveframework.get_doc("File", {"file_name": "file_copy.txt"}) + move_file([{"name": file.name}], folder.name, file.folder) + file = xhiveframework.get_doc("File", {"file_name": "file_copy.txt"}) + + self.assertEqual(_("Home/Test Folder 2"), file.folder) + + def test_folder_depth(self): + result1 = self.get_folder("d1", "Home") + self.assertEqual(result1.name, "Home/d1") + result2 = self.get_folder("d2", "Home/d1") + self.assertEqual(result2.name, "Home/d1/d2") + result3 = self.get_folder("d3", "Home/d1/d2") + self.assertEqual(result3.name, "Home/d1/d2/d3") + result4 = self.get_folder("d4", "Home/d1/d2/d3") + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "folder_copy.txt", + "attached_to_name": "", + "attached_to_doctype": "", + "folder": result4.name, + "content": "Testing folder copy example", + } + ) + _file.save() + + def test_folder_copy(self): + folder = self.get_folder("Test Folder 2", "Home") + folder = self.get_folder("Test Folder 3", "Home/Test Folder 2") + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "folder_copy.txt", + "attached_to_name": "", + "attached_to_doctype": "", + "folder": folder.name, + "content": "Testing folder copy example", + } + ) + _file.save() + + move_file([{"name": folder.name}], "Home/Test Folder 1", folder.folder) + + file = xhiveframework.get_doc("File", {"file_name": "folder_copy.txt"}) + file_copy_txt = xhiveframework.get_value("File", {"file_name": "file_copy.txt"}) + if file_copy_txt: + xhiveframework.get_doc("File", file_copy_txt).delete() + + self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder) + + def test_default_folder(self): + d = xhiveframework.get_doc({"doctype": "File", "file_name": _("Test_Folder"), "is_folder": 1}) + d.save() + self.assertEqual(d.folder, "Home") + + def test_on_delete(self): + file = xhiveframework.get_doc("File", {"file_name": "file_copy.txt"}) + file.delete() + + self.assertEqual(xhiveframework.db.get_value("File", _("Home/Test Folder 1"), "file_size"), 0) + + folder = self.get_folder("Test Folder 3", "Home/Test Folder 1") + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "folder_copy.txt", + "attached_to_name": "", + "attached_to_doctype": "", + "folder": folder.name, + "content": "Testing folder copy example", + } + ) + _file.save() + + folder = xhiveframework.get_doc("File", "Home/Test Folder 1/Test Folder 3") + self.assertRaises(ValidationError, folder.delete) + + def test_same_file_url_update(self): + attached_to_doctype1, attached_to_docname1 = make_test_doc() + attached_to_doctype2, attached_to_docname2 = make_test_doc() + + file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "file1.txt", + "attached_to_doctype": attached_to_doctype1, + "attached_to_name": attached_to_docname1, + "is_private": 1, + "content": test_content1, + } + ).insert() + + file2 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "file2.txt", + "attached_to_doctype": attached_to_doctype2, + "attached_to_name": attached_to_docname2, + "is_private": 1, + "content": test_content1, + } + ).insert() + + self.assertEqual(file1.is_private, file2.is_private, 1) + self.assertEqual(file1.file_url, file2.file_url) + self.assertTrue(os.path.exists(file1.get_full_path())) + + file1.is_private = 0 + file1.save() + + file2 = xhiveframework.get_doc("File", file2.name) + + self.assertEqual(file1.is_private, file2.is_private, 0) + self.assertEqual(file1.file_url, file2.file_url) + self.assertTrue(os.path.exists(file2.get_full_path())) + + def test_parent_directory_validation_in_file_url(self): + file1 = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "parent_dir.txt", + "is_private": 1, + "content": test_content1, + } + ).insert() + + file1.file_url = "/private/files/../test.txt" + self.assertRaises(ValidationError, file1.save) + + # No validation to see if file exists + file1.reload() + file1.file_url = "/private/files/parent_dir2.txt" + self.assertRaises(OSError, file1.save) + + def test_file_url_validation(self): + test_file: "File" = xhiveframework.new_doc("File") + test_file.update({"file_name": "logo", "file_url": "https://xhiveframework.io/files/xhiveframework.png"}) + + self.assertIsNone(test_file.validate()) + + # bad path + test_file.file_url = "/usr/bin/man" + self.assertRaisesRegex( + ValidationError, f"Cannot access file path {test_file.file_url}", test_file.validate + ) + + test_file.file_url = None + test_file.file_name = "/usr/bin/man" + self.assertRaisesRegex(ValidationError, "There is some problem with the file url", test_file.validate) + + test_file.file_url = None + test_file.file_name = "_file" + self.assertRaisesRegex(IOError, "does not exist", test_file.validate) + + test_file.file_url = None + test_file.file_name = "/private/files/_file" + self.assertRaisesRegex(ValidationError, "File name cannot have", test_file.validate) + + def test_make_thumbnail(self): + # test web image + test_file: "File" = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "logo", + "file_url": xhiveframework.utils.get_url("/_test/assets/image.jpg"), + } + ).insert(ignore_permissions=True) + + test_file.make_thumbnail() + self.assertEqual(test_file.thumbnail_url, "/files/image_small.jpg") + + # test web image without extension + test_file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "logo", + "file_url": xhiveframework.utils.get_url("/_test/assets/image"), + } + ).insert(ignore_permissions=True) + + test_file.make_thumbnail() + self.assertTrue(test_file.thumbnail_url.endswith("_small.jpg")) + + # test local image + test_file.db_set("thumbnail_url", None) + test_file.reload() + test_file.file_url = "/files/image_small.jpg" + test_file.make_thumbnail(suffix="xs", crop=True) + self.assertEqual(test_file.thumbnail_url, "/files/image_small_xs.jpg") + + xhiveframework.clear_messages() + test_file.db_set("thumbnail_url", None) + test_file.reload() + test_file.file_url = xhiveframework.utils.get_url("unknown.jpg") + test_file.make_thumbnail(suffix="xs") + self.assertEqual( + xhiveframework.message_log[0].get("message"), + f"File '{xhiveframework.utils.get_url('unknown.jpg')}' not found", + ) + self.assertEqual(test_file.thumbnail_url, None) + + def test_file_unzip(self): + file_path = xhiveframework.get_app_path("xhiveframework", "www/_test/assets/file.zip") + public_file_path = xhiveframework.get_site_path("public", "files") + try: + import shutil + + shutil.copy(file_path, public_file_path) + except Exception: + pass + + test_file = xhiveframework.get_doc( + { + "doctype": "File", + "file_url": "/files/file.zip", + } + ).insert(ignore_permissions=True) + + self.assertListEqual( + [file.file_name for file in unzip_file(test_file.name)], + ["css_asset.css", "image.jpg", "js_asset.min.js"], + ) + + test_file = xhiveframework.get_doc( + { + "doctype": "File", + "file_url": xhiveframework.utils.get_url("/_test/assets/image.jpg"), + } + ).insert(ignore_permissions=True) + self.assertRaisesRegex(ValidationError, "not a zip file", test_file.unzip) + + def test_create_file_without_file_url(self): + test_file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "logo", + "content": "xhiveframework", + } + ).insert() + assert test_file is not None + + def test_symlinked_files_folder(self): + files_dir = os.path.abspath(get_files_path()) + with convert_to_symlink(files_dir): + file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": "symlinked_folder_test.txt", + "content": "42", + } + ) + file.save() + file.content = "" + file._content = "" + file.save().reload() + self.assertIn("42", file.get_content()) + + +@contextmanager +def convert_to_symlink(directory): + """Moves a directory to temp directory and symlinks original path for testing""" + try: + new_directory = shutil.move(directory, tempfile.mkdtemp()) + os.symlink(new_directory, directory) + yield + finally: + os.unlink(directory) + shutil.move(new_directory, directory) + + +class TestAttachment(XhiveFrameworkTestCase): + test_doctype = "Test For Attachment" + + @classmethod + def setUpClass(cls): + super().setUpClass() + xhiveframework.get_doc( + doctype="DocType", + name=cls.test_doctype, + module="Custom", + custom=1, + fields=[ + {"label": "Title", "fieldname": "title", "fieldtype": "Data"}, + {"label": "Attachment", "fieldname": "attachment", "fieldtype": "Attach"}, + ], + ).insert(ignore_if_duplicate=True) + + @classmethod + def tearDownClass(cls): + xhiveframework.db.rollback() + xhiveframework.delete_doc("DocType", cls.test_doctype) + + def test_file_attachment_on_update(self): + doc = xhiveframework.get_doc(doctype=self.test_doctype, title="test for attachment on update").insert() + + file = xhiveframework.get_doc( + {"doctype": "File", "file_name": "test_attach.txt", "content": "Test Content"} + ).save() + + doc.attachment = file.file_url + doc.save() + + exists = xhiveframework.db.exists( + "File", + { + "file_name": "test_attach.txt", + "file_url": file.file_url, + "attached_to_doctype": self.test_doctype, + "attached_to_name": doc.name, + "attached_to_field": "attachment", + }, + ) + + self.assertTrue(exists) + + +class TestAttachmentsAccess(XhiveFrameworkTestCase): + def setUp(self) -> None: + xhiveframework.db.delete("File", {"is_folder": 0}) + + def test_list_private_attachments(self): + xhiveframework.set_user("test4@example.com") + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + + xhiveframework.new_doc( + "File", + file_name="test_user_attachment.txt", + attached_to_doctype=self.attached_to_doctype, + attached_to_name=self.attached_to_docname, + content="Testing User", + is_private=1, + ).insert() + + xhiveframework.new_doc( + "File", + file_name="test_user_standalone.txt", + content="User Home", + is_private=1, + ).insert() + + xhiveframework.set_user("test@example.com") + + xhiveframework.new_doc( + "File", + file_name="test_sm_attachment.txt", + attached_to_doctype=self.attached_to_doctype, + attached_to_name=self.attached_to_docname, + content="Testing System Manager", + is_private=1, + ).insert() + + xhiveframework.new_doc( + "File", + file_name="test_sm_standalone.txt", + content="System Manager Home", + is_private=1, + ).insert() + + system_manager_files = [file.file_name for file in get_files_in_folder("Home")["files"]] + system_manager_attachments_files = [ + file.file_name for file in get_files_in_folder("Home/Attachments")["files"] + ] + + xhiveframework.set_user("test4@example.com") + user_files = [file.file_name for file in get_files_in_folder("Home")["files"]] + user_attachments_files = [file.file_name for file in get_files_in_folder("Home/Attachments")["files"]] + + self.assertIn("test_sm_standalone.txt", system_manager_files) + self.assertNotIn("test_sm_standalone.txt", user_files) + + self.assertIn("test_user_standalone.txt", user_files) + self.assertNotIn("test_user_standalone.txt", system_manager_files) + + self.assertIn("test_sm_attachment.txt", system_manager_attachments_files) + self.assertIn("test_sm_attachment.txt", user_attachments_files) + self.assertIn("test_user_attachment.txt", system_manager_attachments_files) + self.assertIn("test_user_attachment.txt", user_attachments_files) + + def test_list_public_single_file(self): + """Ensure that users are able to list public standalone files.""" + xhiveframework.set_user("test@example.com") + xhiveframework.new_doc( + "File", + file_name="test_public_single.txt", + content="Public single File", + is_private=0, + ).insert() + + xhiveframework.set_user("test4@example.com") + files = [file.file_name for file in get_files_in_folder("Home")["files"]] + self.assertIn("test_public_single.txt", files) + + def test_list_public_attachment(self): + """Ensure that users are able to list public attachments.""" + xhiveframework.set_user("test@example.com") + self.attached_to_doctype, self.attached_to_docname = make_test_doc() + xhiveframework.new_doc( + "File", + file_name="test_public_attachment.txt", + attached_to_doctype=self.attached_to_doctype, + attached_to_name=self.attached_to_docname, + content="Public Attachment", + is_private=0, + ).insert() + + xhiveframework.set_user("test4@example.com") + files = [file.file_name for file in get_files_in_folder("Home/Attachments")["files"]] + self.assertIn("test_public_attachment.txt", files) + + def tearDown(self) -> None: + xhiveframework.set_user("Administrator") + xhiveframework.db.rollback() + + +class TestFileUtils(XhiveFrameworkTestCase): + def test_extract_images_from_doc(self): + # with filename in data URI + todo = xhiveframework.get_doc( + { + "doctype": "ToDo", + "description": 'Test ', + } + ).insert() + self.assertTrue(xhiveframework.db.exists("File", {"attached_to_name": todo.name})) + self.assertIn('', todo.description) + self.assertListEqual(get_attached_images("ToDo", [todo.name])[todo.name], ["/files/pix.png"]) + + # without filename in data URI + todo = xhiveframework.get_doc( + { + "doctype": "ToDo", + "description": 'Test ', + } + ).insert() + filename = xhiveframework.db.exists("File", {"attached_to_name": todo.name}) + self.assertIn(f' None: + xhiveframework.db.delete("File", {"is_folder": 0}) + xhiveframework.get_doc( + doctype="DocType", + name="Test For Attachment", + module="Custom", + custom=1, + fields=[ + {"label": "Title", "fieldname": "title", "fieldtype": "Data"}, + {"label": "Attachment", "fieldname": "attachment", "fieldtype": "Attach"}, + ], + ).insert(ignore_if_duplicate=True) + + def tearDown(self) -> None: + xhiveframework.set_user("Administrator") + xhiveframework.db.rollback() + xhiveframework.delete_doc("DocType", "Test For Attachment") + + def test_attach_unattached_guest_file(self): + """Ensure that unattached files are attached on doc update.""" + f = xhiveframework.new_doc( + "File", + file_name="test_private_guest_attachment.txt", + content="Guest Home", + is_private=1, + ).insert(ignore_permissions=True) + + d = xhiveframework.new_doc("Test For Attachment") + d.title = "Test for attachment on update" + d.attachment = f.file_url + d.assigned_by = xhiveframework.session.user + d.save() + + self.assertTrue( + xhiveframework.db.exists( + "File", + { + "file_name": "test_private_guest_attachment.txt", + "file_url": f.file_url, + "attached_to_doctype": "Test For Attachment", + "attached_to_name": d.name, + "attached_to_field": "attachment", + }, + ) + ) + + def test_list_private_guest_single_file(self): + """Ensure that guests are not able to read private standalone guest files.""" + xhiveframework.set_user("Guest") + + file = xhiveframework.new_doc( + "File", + file_name="test_private_guest_single_txt", + content="Private single File", + is_private=1, + ).insert(ignore_permissions=True) + + self.assertFalse(file.is_downloadable()) + + def test_list_private_guest_attachment(self): + """Ensure that guests are not able to read private guest attachments.""" + xhiveframework.set_user("Guest") + + self.attached_to_doctype, self.attached_to_docname = make_test_doc(ignore_permissions=True) + + file = xhiveframework.new_doc( + "File", + file_name="test_private_guest_attachment.txt", + attached_to_doctype=self.attached_to_doctype, + attached_to_name=self.attached_to_docname, + content="Private Attachment", + is_private=1, + ).insert(ignore_permissions=True) + + self.assertFalse(file.is_downloadable()) diff --git a/xhiveframework/core/doctype/file/utils.py b/xhiveframework/core/doctype/file/utils.py new file mode 100644 index 0000000..7068ef9 --- /dev/null +++ b/xhiveframework/core/doctype/file/utils.py @@ -0,0 +1,429 @@ +import hashlib +import mimetypes +import os +import re +from io import BytesIO +from typing import TYPE_CHECKING, Optional +from urllib.parse import unquote + +import filetype + +import xhiveframework +from xhiveframework import _, safe_decode +from xhiveframework.utils import cint, cstr, encode, get_files_path, random_string, strip +from xhiveframework.utils.file_manager import safe_b64decode +from xhiveframework.utils.image import optimize_image + +if TYPE_CHECKING: + from PIL.ImageFile import ImageFile + from requests.models import Response + + from xhiveframework.model.document import Document + + from .file import File + + +def make_home_folder() -> None: + home = xhiveframework.get_doc( + {"doctype": "File", "is_folder": 1, "is_home_folder": 1, "file_name": _("Home")} + ).insert(ignore_if_duplicate=True) + + xhiveframework.get_doc( + { + "doctype": "File", + "folder": home.name, + "is_folder": 1, + "is_attachments_folder": 1, + "file_name": _("Attachments"), + } + ).insert(ignore_if_duplicate=True) + + +def setup_folder_path(filename: str, new_parent: str) -> None: + file: "File" = xhiveframework.get_doc("File", filename) + file.folder = new_parent + file.save() + + if file.is_folder: + from xhiveframework.model.rename_doc import rename_doc + + rename_doc("File", file.name, file.get_name_based_on_parent_folder(), ignore_permissions=True) + + +def get_extension( + filename, + extn: str | None = None, + content: bytes | None = None, + response: Optional["Response"] = None, +) -> str: + mimetype = None + + if response: + content_type = response.headers.get("Content-Type") + + if content_type: + _extn = mimetypes.guess_extension(content_type) + if _extn: + return _extn[1:] + + if extn: + # remove '?' char and parameters from extn if present + if "?" in extn: + extn = extn.split("?", 1)[0] + + mimetype = mimetypes.guess_type(filename + "." + extn)[0] + + if mimetype is None and extn is None and content: + # detect file extension by using filetype matchers + _type_info = filetype.match(content) + if _type_info: + extn = _type_info.extension + + return extn + + +def get_local_image(file_url: str) -> tuple["ImageFile", str, str]: + from PIL import Image + + if file_url.startswith("/private"): + file_url_path = (file_url.lstrip("/"),) + else: + file_url_path = ("public", file_url.lstrip("/")) + + file_path = xhiveframework.get_site_path(*file_url_path) + + try: + image = Image.open(file_path) + except OSError: + xhiveframework.throw(_("Unable to read file format for {0}").format(file_url)) + + content = None + + try: + filename, extn = file_url.rsplit(".", 1) + except ValueError: + # no extn + with open(file_path) as f: + content = f.read() + + filename = file_url + extn = None + + extn = get_extension(filename, extn, content) + + return image, filename, extn + + +def get_web_image(file_url: str) -> tuple["ImageFile", str, str]: + import requests + import requests.exceptions + from PIL import Image + + file_url = xhiveframework.utils.get_url(file_url) + r = requests.get(file_url, stream=True) + try: + r.raise_for_status() + except requests.exceptions.HTTPError as e: + if "404" in e.args[0]: + xhiveframework.msgprint(_("File '{0}' not found").format(file_url)) + else: + xhiveframework.msgprint(_("Unable to read file format for {0}").format(file_url)) + raise + + try: + image = Image.open(BytesIO(r.content)) + except Exception as e: + xhiveframework.msgprint(_("Image link '{0}' is not valid").format(file_url), raise_exception=e) + + try: + filename, extn = file_url.rsplit("/", 1)[1].rsplit(".", 1) + except ValueError: + # the case when the file url doesn't have filename or extension + # but is fetched due to a query string. example: https://encrypted-tbn3.gstatic.com/images?q=something + filename = get_random_filename() + extn = None + + extn = get_extension(filename, extn, r.content) + filename = "/files/" + strip(unquote(filename)) + + return image, filename, extn + + +def delete_file(path: str) -> None: + """Delete file from `public folder`""" + if path: + if ".." in path.split("/"): + xhiveframework.throw( + _("It is risky to delete this file: {0}. Please contact your System Manager.").format(path) + ) + + parts = os.path.split(path.strip("/")) + if parts[0] == "files": + path = xhiveframework.utils.get_site_path("public", "files", parts[-1]) + + else: + path = xhiveframework.utils.get_site_path("private", "files", parts[-1]) + + path = encode(path) + if os.path.exists(path): + os.remove(path) + + +def remove_file_by_url(file_url: str, doctype: str | None = None, name: str | None = None) -> "Document": + if doctype and name: + fid = xhiveframework.db.get_value( + "File", {"file_url": file_url, "attached_to_doctype": doctype, "attached_to_name": name} + ) + else: + fid = xhiveframework.db.get_value("File", {"file_url": file_url}) + + if fid: + from xhiveframework.utils.file_manager import remove_file + + return remove_file(fid=fid) + + +def get_content_hash(content: bytes | str) -> str: + if isinstance(content, str): + content = content.encode() + return hashlib.md5(content, usedforsecurity=False).hexdigest() # nosec + + +def generate_file_name(name: str, suffix: str | None = None, is_private: bool = False) -> str: + """Generate conflict-free file name. Suffix will be ignored if name available. If the + provided suffix doesn't result in an available path, a random suffix will be picked. + """ + + def path_exists(name, is_private): + return os.path.exists(encode(get_files_path(name, is_private=is_private))) + + if not path_exists(name, is_private): + return name + + candidate_path = get_file_name(name, suffix) + + if path_exists(candidate_path, is_private): + return generate_file_name(name, is_private=is_private) + return candidate_path + + +def get_file_name(fname: str, optional_suffix: str | None = None) -> str: + # convert to unicode + fname = cstr(fname) + partial, extn = os.path.splitext(fname) + suffix = optional_suffix or xhiveframework.generate_hash(length=6) + + return f"{partial}{suffix}{extn}" + + +def extract_images_from_doc(doc: "Document", fieldname: str): + content = doc.get(fieldname) + content = extract_images_from_html(doc, content) + if xhiveframework.flags.has_dataurl: + doc.set(fieldname, content) + + +def extract_images_from_html(doc: "Document", content: str, is_private: bool = False): + xhiveframework.flags.has_dataurl = False + + def _save_file(match): + data = match.group(1).split("data:")[1] + headers, content = data.split(",") + mtype = headers.split(";", 1)[0] + + if isinstance(content, str): + content = content.encode("utf-8") + if b"," in content: + content = content.split(b",")[1] + content = safe_b64decode(content) + + content = optimize_image(content, mtype) + + if "filename=" in headers: + filename = headers.split("filename=")[-1] + filename = safe_decode(filename).split(";", 1)[0] + + else: + filename = get_random_filename(content_type=mtype) + + if doc.meta.istable: + doctype = doc.parenttype + name = doc.parent + else: + doctype = doc.doctype + name = doc.name + + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": filename, + "attached_to_doctype": doctype, + "attached_to_name": name, + "content": content, + "decode": False, + "is_private": is_private, + } + ) + _file.save(ignore_permissions=True) + file_url = _file.unique_url + xhiveframework.flags.has_dataurl = True + + return f']*src\s*=\s*["\'](?=data:)(.*?)["\']', _save_file, content) + + return content + + +def get_random_filename(content_type: str | None = None) -> str: + extn = None + if content_type: + extn = mimetypes.guess_extension(content_type) + + return random_string(7) + (extn or "") + + +def update_existing_file_docs(doc: "File") -> None: + # Update is private and file url of all file docs that point to the same file + file_doctype = xhiveframework.qb.DocType("File") + ( + xhiveframework.qb.update(file_doctype) + .set(file_doctype.file_url, doc.file_url) + .set(file_doctype.is_private, doc.is_private) + .where(file_doctype.content_hash == doc.content_hash) + .where(file_doctype.name != doc.name) + ).run() + + +def attach_files_to_document(doc: "Document", event) -> None: + """Runs on on_update hook of all documents. + Goes through every file linked with the Attach and Attach Image field and attaches + the file to the document if not already attached. If no file is found, a new file + is created. + """ + + attach_fields = doc.meta.get("fields", {"fieldtype": ["in", ["Attach", "Attach Image"]]}) + + for df in attach_fields: + # this method runs in on_update hook of all documents + # we dont want the update to fail if file cannot be attached for some reason + value = doc.get(df.fieldname) + if not (value or "").startswith(("/files", "/private/files")): + continue + + if xhiveframework.db.exists( + "File", + { + "file_url": value, + "attached_to_name": doc.name, + "attached_to_doctype": doc.doctype, + "attached_to_field": df.fieldname, + }, + ): + continue + + unattached_file = xhiveframework.db.exists( + "File", + { + "file_url": value, + "attached_to_name": None, + "attached_to_doctype": None, + "attached_to_field": None, + }, + ) + + if unattached_file: + xhiveframework.db.set_value( + "File", + unattached_file, + field={ + "attached_to_name": doc.name, + "attached_to_doctype": doc.doctype, + "attached_to_field": df.fieldname, + "is_private": cint(value.startswith("/private")), + }, + ) + continue + + file: "File" = xhiveframework.get_doc( + doctype="File", + file_url=value, + attached_to_name=doc.name, + attached_to_doctype=doc.doctype, + attached_to_field=df.fieldname, + folder="Home/Attachments", + ) + try: + file.insert(ignore_permissions=True) + except Exception: + doc.log_error("Error Attaching File") + + +def relink_files(doc, fieldname, temp_doc_name): + if not temp_doc_name: + return + from xhiveframework.utils.data import add_to_date, now_datetime + + """ + Relink files attached to incorrect document name to the new document name + by check if file with temp name exists that was created in last 60 minutes + """ + mislinked_file = xhiveframework.db.exists( + "File", + { + "file_url": doc.get(fieldname), + "attached_to_name": temp_doc_name, + "attached_to_doctype": doc.doctype, + "attached_to_field": fieldname, + "creation": ( + "between", + [add_to_date(date=now_datetime(), minutes=-60), now_datetime()], + ), + }, + ) + """If file exists, attach it to the new docname""" + if mislinked_file: + xhiveframework.db.set_value( + "File", + mislinked_file, + field={ + "attached_to_name": doc.name, + }, + ) + return + + +def relink_mismatched_files(doc: "Document") -> None: + if not doc.get("__temporary_name", None): + return + attach_fields = doc.meta.get("fields", {"fieldtype": ["in", ["Attach", "Attach Image"]]}) + for df in attach_fields: + if doc.get(df.fieldname): + relink_files(doc, df.fieldname, doc.__temporary_name) + # delete temporary name after relinking is done + doc.delete_key("__temporary_name") + + +def decode_file_content(content: bytes) -> bytes: + if isinstance(content, str): + content = content.encode("utf-8") + if b"," in content: + content = content.split(b",")[1] + return safe_b64decode(content) + + +def find_file_by_url(path: str, name: str | None = None) -> Optional["File"]: + filters = {"file_url": str(path)} + if name: + filters["name"] = str(name) + + files = xhiveframework.get_all("File", filters=filters, fields="*") + + # this file might be attached to multiple documents + # if the file is accessible from any one of those documents + # then it should be downloadable + for file_data in files: + file: "File" = xhiveframework.get_doc(doctype="File", **file_data) + if file.is_downloadable(): + return file diff --git a/xhiveframework/core/doctype/has_domain/__init__.py b/xhiveframework/core/doctype/has_domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/has_domain/has_domain.json b/xhiveframework/core/doctype/has_domain/has_domain.json new file mode 100644 index 0000000..c34626b --- /dev/null +++ b/xhiveframework/core/doctype/has_domain/has_domain.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2017-05-03 15:20:22.326623", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "domain" + ], + "fields": [ + { + "fieldname": "domain", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Domain", + "options": "Domain" + } + ], + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:53.381248", + "modified_by": "Administrator", + "module": "Core", + "name": "Has Domain", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/has_domain/has_domain.py b/xhiveframework/core/doctype/has_domain/has_domain.py new file mode 100644 index 0000000..12f0530 --- /dev/null +++ b/xhiveframework/core/doctype/has_domain/has_domain.py @@ -0,0 +1,21 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class HasDomain(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + domain: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/has_role/__init__.py b/xhiveframework/core/doctype/has_role/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/has_role/has_role.json b/xhiveframework/core/doctype/has_role/has_role.json new file mode 100644 index 0000000..689e804 --- /dev/null +++ b/xhiveframework/core/doctype/has_role/has_role.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:34", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "role" + ], + "fields": [ + { + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:54.382064", + "modified_by": "Administrator", + "module": "Core", + "name": "Has Role", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/has_role/has_role.py b/xhiveframework/core/doctype/has_role/has_role.py new file mode 100644 index 0000000..2cae768 --- /dev/null +++ b/xhiveframework/core/doctype/has_role/has_role.py @@ -0,0 +1,25 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class HasRole(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + role: DF.Link | None + + # end: auto-generated types + def before_insert(self): + if xhiveframework.db.exists("Has Role", {"parent": self.parent, "role": self.role}): + xhiveframework.throw(xhiveframework._("User '{0}' already has the role '{1}'").format(self.parent, self.role)) diff --git a/xhiveframework/core/doctype/installed_application/__init__.py b/xhiveframework/core/doctype/installed_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/installed_application/installed_application.json b/xhiveframework/core/doctype/installed_application/installed_application.json new file mode 100644 index 0000000..1f32c55 --- /dev/null +++ b/xhiveframework/core/doctype/installed_application/installed_application.json @@ -0,0 +1,49 @@ +{ + "actions": [], + "creation": "2020-05-11 17:44:54.674657", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "app_name", + "app_version", + "git_branch" + ], + "fields": [ + { + "fieldname": "git_branch", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Git Branch", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "app_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Application Name", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "app_version", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Application Version", + "read_only": 1, + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-12 10:09:49.148087", + "modified_by": "Administrator", + "module": "Core", + "name": "Installed Application", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/installed_application/installed_application.py b/xhiveframework/core/doctype/installed_application/installed_application.py new file mode 100644 index 0000000..86c63cf --- /dev/null +++ b/xhiveframework/core/doctype/installed_application/installed_application.py @@ -0,0 +1,24 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class InstalledApplication(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + app_name: DF.Data + app_version: DF.Data + git_branch: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/installed_applications/__init__.py b/xhiveframework/core/doctype/installed_applications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/installed_applications/installed_applications.js b/xhiveframework/core/doctype/installed_applications/installed_applications.js new file mode 100644 index 0000000..18160eb --- /dev/null +++ b/xhiveframework/core/doctype/installed_applications/installed_applications.js @@ -0,0 +1,67 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Installed Applications", { + refresh: function (frm) { + frm.add_custom_button(__("Update Hooks Resolution Order"), () => { + frm.trigger("show_update_order_dialog"); + }); + }, + + show_update_order_dialog() { + const dialog = new xhiveframework.ui.Dialog({ + title: __("Update Hooks Resolution Order"), + fields: [ + { + fieldname: "apps", + fieldtype: "Table", + label: __("Installed Apps"), + cannot_add_rows: true, + cannot_delete_rows: true, + in_place_edit: true, + data: [], + fields: [ + { + fieldtype: "Data", + fieldname: "app_name", + label: __("App Name"), + in_list_view: 1, + read_only: 1, + }, + ], + }, + ], + primary_action: function () { + const new_order = this.get_values()["apps"].map((row) => row.app_name); + xhiveframework.call({ + method: "xhiveframework.core.doctype.installed_applications.installed_applications.update_installed_apps_order", + freeze: true, + args: { + new_order: new_order, + }, + }); + this.hide(); + }, + primary_action_label: __("Update Order"), + }); + + xhiveframework + .xcall( + "xhiveframework.core.doctype.installed_applications.installed_applications.get_installed_app_order" + ) + .then((data) => { + data.forEach((app) => { + dialog.fields_dict.apps.df.data.push({ + app_name: app, + }); + }); + + dialog.fields_dict.apps.grid.refresh(); + // hack: change checkboxes to drag handles. + let grid = $(dialog.fields_dict.apps.grid.parent); + grid.find(".grid-row-check:first").remove() && + grid.find(".grid-row-check").replaceWith(xhiveframework.utils.icon("menu")); + dialog.show(); + }); + }, +}); diff --git a/xhiveframework/core/doctype/installed_applications/installed_applications.json b/xhiveframework/core/doctype/installed_applications/installed_applications.json new file mode 100644 index 0000000..f2345e6 --- /dev/null +++ b/xhiveframework/core/doctype/installed_applications/installed_applications.json @@ -0,0 +1,42 @@ +{ + "actions": [], + "creation": "2020-05-11 17:45:41.587750", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "installed_applications" + ], + "fields": [ + { + "fieldname": "installed_applications", + "fieldtype": "Table", + "label": "Installed Applications", + "options": "Installed Application", + "read_only": 1 + } + ], + "issingle": 1, + "links": [], + "modified": "2020-05-12 10:09:14.310622", + "modified_by": "Administrator", + "module": "Core", + "name": "Installed Applications", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/installed_applications/installed_applications.py b/xhiveframework/core/doctype/installed_applications/installed_applications.py new file mode 100644 index 0000000..c6594a1 --- /dev/null +++ b/xhiveframework/core/doctype/installed_applications/installed_applications.py @@ -0,0 +1,87 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class InvalidAppOrder(xhiveframework.ValidationError): + pass + + +class InstalledApplications(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.installed_application.installed_application import InstalledApplication + from xhiveframework.types import DF + + installed_applications: DF.Table[InstalledApplication] + + # end: auto-generated types + def update_versions(self): + self.delete_key("installed_applications") + for app in xhiveframework.utils.get_installed_apps_info(): + self.append( + "installed_applications", + { + "app_name": app.get("app_name"), + "app_version": app.get("version") or "UNVERSIONED", + "git_branch": app.get("branch") or "UNVERSIONED", + }, + ) + self.save() + + +@xhiveframework.whitelist() +def update_installed_apps_order(new_order: list[str] | str): + """Change the ordering of `installed_apps` global + + This list is used to resolve hooks and by default it's order of installation on site. + + Sometimes it might not be the ordering you want, so thie function is provided to override it. + """ + xhiveframework.only_for("System Manager") + + if isinstance(new_order, str): + new_order = json.loads(new_order) + + xhiveframework.local.request_cache and xhiveframework.local.request_cache.clear() + existing_order = xhiveframework.get_installed_apps(_ensure_on_bench=True) + + if set(existing_order) != set(new_order) or not isinstance(new_order, list): + xhiveframework.throw( + _("You are only allowed to update order, do not remove or add apps."), exc=InvalidAppOrder + ) + + # Ensure xhiveframework is always first regardless of user's preference. + if "xhiveframework" in new_order: + new_order.remove("xhiveframework") + new_order.insert(0, "xhiveframework") + + xhiveframework.db.set_global("installed_apps", json.dumps(new_order)) + + _create_version_log_for_change(existing_order, new_order) + + +def _create_version_log_for_change(old, new): + version = xhiveframework.new_doc("Version") + version.ref_doctype = "DefaultValue" + version.docname = "installed_apps" + version.data = xhiveframework.as_json({"changed": [["current", json.dumps(old), json.dumps(new)]]}) + version.flags.ignore_links = True # This is a fake doctype + version.flags.ignore_permissions = True + version.insert() + + +@xhiveframework.whitelist() +def get_installed_app_order() -> list[str]: + xhiveframework.only_for("System Manager") + + return xhiveframework.get_installed_apps(_ensure_on_bench=True) diff --git a/xhiveframework/core/doctype/installed_applications/test_installed_applications.py b/xhiveframework/core/doctype/installed_applications/test_installed_applications.py new file mode 100644 index 0000000..90a8853 --- /dev/null +++ b/xhiveframework/core/doctype/installed_applications/test_installed_applications.py @@ -0,0 +1,16 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.core.doctype.installed_applications.installed_applications import ( + InvalidAppOrder, + update_installed_apps_order, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestInstalledApplications(XhiveFrameworkTestCase): + def test_order_change(self): + update_installed_apps_order(["xhiveframework"]) + self.assertRaises(InvalidAppOrder, update_installed_apps_order, []) + self.assertRaises(InvalidAppOrder, update_installed_apps_order, ["xhiveframework", "deepmind"]) diff --git a/xhiveframework/core/doctype/language/__init__.py b/xhiveframework/core/doctype/language/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/language/language.js b/xhiveframework/core/doctype/language/language.js new file mode 100644 index 0000000..3c50630 --- /dev/null +++ b/xhiveframework/core/doctype/language/language.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Language", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/language/language.json b/xhiveframework/core/doctype/language/language.json new file mode 100644 index 0000000..c9110bb --- /dev/null +++ b/xhiveframework/core/doctype/language/language.json @@ -0,0 +1,80 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:language_code", + "creation": "2014-08-22 16:12:17.249590", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "enabled", + "language_code", + "language_name", + "flag", + "based_on" + ], + "fields": [ + { + "fieldname": "language_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Language Code", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "language_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Language Name", + "reqd": 1 + }, + { + "fieldname": "flag", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Flag" + }, + { + "fieldname": "based_on", + "fieldtype": "Link", + "label": "Based On", + "options": "Language" + }, + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + } + ], + "icon": "fa fa-globe", + "in_create": 1, + "links": [], + "modified": "2023-04-13 13:48:38.127995", + "modified_by": "Administrator", + "module": "Core", + "name": "Language", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "role": "All", + "read": 1 + } + ], + "search_fields": "language_name", + "show_title_field_in_link": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "language_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/language/language.py b/xhiveframework/core/doctype/language/language.py new file mode 100644 index 0000000..2c49ac1 --- /dev/null +++ b/xhiveframework/core/doctype/language/language.py @@ -0,0 +1,84 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json +import re + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class Language(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + based_on: DF.Link | None + enabled: DF.Check + flag: DF.Data | None + language_code: DF.Data + language_name: DF.Data + + # end: auto-generated types + def validate(self): + validate_with_regex(self.language_code, "Language Code") + + def before_rename(self, old, new, merge=False): + validate_with_regex(new, "Name") + + def on_update(self): + xhiveframework.cache.delete_value("languages_with_name") + xhiveframework.cache.delete_value("languages") + + +def validate_with_regex(name, label): + pattern = re.compile("^[a-zA-Z]+[-_]*[a-zA-Z]+$") + if not pattern.match(name): + xhiveframework.throw( + _( + """{0} must begin and end with a letter and can only contain letters, + hyphen or underscore.""" + ).format(label) + ) + + +def export_languages_json(): + """Export list of all languages""" + languages = xhiveframework.get_all("Language", fields=["name", "language_name"]) + languages = [{"name": d.language_name, "code": d.name} for d in languages] + + languages.sort(key=lambda a: a["code"]) + + with open(xhiveframework.get_app_path("xhiveframework", "geo", "languages.json"), "w") as f: + f.write(xhiveframework.as_json(languages)) + + +def sync_languages(): + """Sync xhiveframework/geo/languages.json with Language""" + with open(xhiveframework.get_app_path("xhiveframework", "geo", "languages.json")) as f: + data = json.loads(f.read()) + + for l in data: + if not xhiveframework.db.exists("Language", l["code"]): + xhiveframework.get_doc( + { + "doctype": "Language", + "language_code": l["code"], + "language_name": l["name"], + "enabled": 1, + } + ).insert() + + +def update_language_names(): + """Update xhiveframework/geo/languages.json names (for use via patch)""" + with open(xhiveframework.get_app_path("xhiveframework", "geo", "languages.json")) as f: + data = json.loads(f.read()) + + for l in data: + xhiveframework.db.set_value("Language", l["code"], "language_name", l["name"]) diff --git a/xhiveframework/core/doctype/language/test_language.py b/xhiveframework/core/doctype/language/test_language.py new file mode 100644 index 0000000..c2d00c1 --- /dev/null +++ b/xhiveframework/core/doctype/language/test_language.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Language') + + +class TestLanguage(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/log_setting_user/__init__.py b/xhiveframework/core/doctype/log_setting_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/log_setting_user/log_setting_user.js b/xhiveframework/core/doctype/log_setting_user/log_setting_user.js new file mode 100644 index 0000000..19231af --- /dev/null +++ b/xhiveframework/core/doctype/log_setting_user/log_setting_user.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Log Setting User", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/log_setting_user/log_setting_user.json b/xhiveframework/core/doctype/log_setting_user/log_setting_user.json new file mode 100644 index 0000000..7f4b0ef --- /dev/null +++ b/xhiveframework/core/doctype/log_setting_user/log_setting_user.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "autoname": "field:user", + "creation": "2020-10-08 13:09:36.034430", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "reqd": 1, + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-10-08 17:22:04.690348", + "modified_by": "Administrator", + "module": "Core", + "name": "Log Setting User", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/log_setting_user/log_setting_user.py b/xhiveframework/core/doctype/log_setting_user/log_setting_user.py new file mode 100644 index 0000000..78c0d81 --- /dev/null +++ b/xhiveframework/core/doctype/log_setting_user/log_setting_user.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class LogSettingUser(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + user: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/log_setting_user/test_log_setting_user.py b/xhiveframework/core/doctype/log_setting_user/test_log_setting_user.py new file mode 100644 index 0000000..609e809 --- /dev/null +++ b/xhiveframework/core/doctype/log_setting_user/test_log_setting_user.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestLogSettingUser(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/log_settings/__init__.py b/xhiveframework/core/doctype/log_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/log_settings/log_settings.js b/xhiveframework/core/doctype/log_settings/log_settings.js new file mode 100644 index 0000000..97d8a00 --- /dev/null +++ b/xhiveframework/core/doctype/log_settings/log_settings.js @@ -0,0 +1,14 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Log Settings", { + refresh: (frm) => { + frm.set_query("ref_doctype", "logs_to_clear", () => { + const added_doctypes = frm.doc.logs_to_clear.map((r) => r.ref_doctype); + return { + query: "xhiveframework.core.doctype.log_settings.log_settings.get_log_doctypes", + filters: [["name", "not in", added_doctypes]], + }; + }); + }, +}); diff --git a/xhiveframework/core/doctype/log_settings/log_settings.json b/xhiveframework/core/doctype/log_settings/log_settings.json new file mode 100644 index 0000000..5a9dd15 --- /dev/null +++ b/xhiveframework/core/doctype/log_settings/log_settings.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2020-10-08 12:12:21.694424", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "logs_to_clear" + ], + "fields": [ + { + "fieldname": "logs_to_clear", + "fieldtype": "Table", + "label": "Logs to Clear", + "options": "Logs To Clear" + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2022-06-11 02:17:30.803721", + "modified_by": "Administrator", + "module": "Core", + "name": "Log Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/log_settings/log_settings.py b/xhiveframework/core/doctype/log_settings/log_settings.py new file mode 100644 index 0000000..b608fbf --- /dev/null +++ b/xhiveframework/core/doctype/log_settings/log_settings.py @@ -0,0 +1,192 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from typing import Protocol, runtime_checkable + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.base_document import get_controller +from xhiveframework.model.document import Document +from xhiveframework.utils import cint +from xhiveframework.utils.caching import site_cache + + +@runtime_checkable +class LogType(Protocol): + """Interface requirement for doctypes that can be cleared using log settings.""" + + @staticmethod + def clear_old_logs(days: int) -> None: + ... + + +@site_cache +def _supports_log_clearing(doctype: str) -> bool: + try: + controller = get_controller(doctype) + return issubclass(controller, LogType) + except Exception: + return False + + +class LogSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.logs_to_clear.logs_to_clear import LogsToClear + from xhiveframework.types import DF + + logs_to_clear: DF.Table[LogsToClear] + + # end: auto-generated types + def validate(self): + self.remove_unsupported_doctypes() + self._deduplicate_entries() + self.add_default_logtypes() + + def remove_unsupported_doctypes(self): + for entry in list(self.logs_to_clear): + if _supports_log_clearing(entry.ref_doctype): + continue + + msg = _("{} does not support automated log clearing.").format(xhiveframework.bold(entry.ref_doctype)) + if xhiveframework.conf.developer_mode: + msg += "
    " + _("Implement `clear_old_logs` method to enable auto error clearing.") + xhiveframework.msgprint(msg, title=_("DocType not supported by Log Settings.")) + self.remove(entry) + + def _deduplicate_entries(self): + seen = set() + for entry in list(self.logs_to_clear): + if entry.ref_doctype in seen: + self.remove(entry) + seen.add(entry.ref_doctype) + + def add_default_logtypes(self): + existing_logtypes = {d.ref_doctype for d in self.logs_to_clear} + added_logtypes = set() + default_logtypes_retention = xhiveframework.get_hooks("default_log_clearing_doctypes", {}) + + for logtype, retentions in default_logtypes_retention.items(): + if logtype not in existing_logtypes and _supports_log_clearing(logtype): + if not xhiveframework.db.exists("DocType", logtype): + continue + + self.append("logs_to_clear", {"ref_doctype": logtype, "days": cint(retentions[-1])}) + added_logtypes.add(logtype) + + if added_logtypes: + xhiveframework.msgprint(_("Added default log doctypes: {}").format(",".join(added_logtypes)), alert=True) + + def clear_logs(self): + """ + Log settings can clear any log type that's registered to it and provides a method to delete old logs. + + Check `LogDoctype` above for interface that doctypes need to implement. + """ + + for entry in self.logs_to_clear: + controller: LogType = get_controller(entry.ref_doctype) + func = controller.clear_old_logs + + # Only pass what the method can handle, this is considering any + # future addition that might happen to the required interface. + kwargs = xhiveframework.get_newargs(func, {"days": entry.days}) + func(**kwargs) + xhiveframework.db.commit() + + def register_doctype(self, doctype: str, days=30): + existing_logtypes = {d.ref_doctype for d in self.logs_to_clear} + + if doctype not in existing_logtypes and _supports_log_clearing(doctype): + self.append("logs_to_clear", {"ref_doctype": doctype, "days": cint(days)}) + else: + for entry in self.logs_to_clear: + if entry.ref_doctype == doctype: + entry.days = days + break + + +def run_log_clean_up(): + doc = xhiveframework.get_doc("Log Settings") + doc.remove_unsupported_doctypes() + doc.add_default_logtypes() + doc.save() + doc.clear_logs() + + +@xhiveframework.whitelist() +def has_unseen_error_log(): + if xhiveframework.get_all("Error Log", filters={"seen": 0}, limit=1): + return { + "show_alert": True, + "message": _("You have unseen {0}").format( + ' Error Logs ' + ), + } + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_log_doctypes(doctype, txt, searchfield, start, page_len, filters): + filters = filters or {} + + filters.extend( + [ + ["istable", "=", 0], + ["issingle", "=", 0], + ["name", "like", f"%%{txt}%%"], + ] + ) + doctypes = xhiveframework.get_list("DocType", filters=filters, pluck="name") + + supported_doctypes = [(d,) for d in doctypes if _supports_log_clearing(d)] + + return supported_doctypes[start:page_len] + + +LOG_DOCTYPES = [ + "Scheduled Job Log", + "Activity Log", + "Route History", + "Email Queue", + "Email Queue Recipient", + "Error Log", +] + + +def clear_log_table(doctype, days=90): + """If any logtype table grows too large then clearing it with DELETE query + is not feasible in reasonable time. This command copies recent data to new + table and replaces current table with new smaller table. + + ref: https://mariadb.com/kb/en/big-deletes/#deleting-more-than-half-a-table + """ + from xhiveframework.utils import get_table_name + + if doctype not in LOG_DOCTYPES: + raise xhiveframework.ValidationError(f"Unsupported logging DocType: {doctype}") + + original = get_table_name(doctype) + temporary = f"{original} temp_table" + backup = f"{original} backup_table" + + try: + xhiveframework.db.sql_ddl(f"CREATE TABLE `{temporary}` LIKE `{original}`") + + # Copy all recent data to new table + xhiveframework.db.sql( + f"""INSERT INTO `{temporary}` + SELECT * FROM `{original}` + WHERE `{original}`.`modified` > NOW() - INTERVAL '{days}' DAY""" + ) + xhiveframework.db.sql_ddl(f"RENAME TABLE `{original}` TO `{backup}`, `{temporary}` TO `{original}`") + except Exception: + xhiveframework.db.rollback() + xhiveframework.db.sql_ddl(f"DROP TABLE IF EXISTS `{temporary}`") + raise + else: + xhiveframework.db.sql_ddl(f"DROP TABLE `{backup}`") diff --git a/xhiveframework/core/doctype/log_settings/test_log_settings.py b/xhiveframework/core/doctype/log_settings/test_log_settings.py new file mode 100644 index 0000000..5ff0008 --- /dev/null +++ b/xhiveframework/core/doctype/log_settings/test_log_settings.py @@ -0,0 +1,104 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +from datetime import datetime + +import xhiveframework +from xhiveframework.core.doctype.log_settings.log_settings import _supports_log_clearing, run_log_clean_up +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import add_to_date, now_datetime + + +class TestLogSettings(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + xhiveframework.db.set_single_value( + "Log Settings", + { + "clear_error_log_after": 1, + "clear_activity_log_after": 1, + "clear_email_queue_after": 1, + }, + ) + + def setUp(self) -> None: + if self._testMethodName == "test_delete_logs": + self.datetime = xhiveframework._dict() + self.datetime.current = now_datetime() + self.datetime.past = add_to_date(self.datetime.current, days=-4) + setup_test_logs(self.datetime.past) + + def tearDown(self) -> None: + if self._testMethodName == "test_delete_logs": + del self.datetime + + def test_delete_logs(self): + # make sure test data is present + activity_log_count = xhiveframework.db.count("Activity Log", {"creation": ("<=", self.datetime.past)}) + error_log_count = xhiveframework.db.count("Error Log", {"creation": ("<=", self.datetime.past)}) + email_queue_count = xhiveframework.db.count("Email Queue", {"creation": ("<=", self.datetime.past)}) + + self.assertNotEqual(activity_log_count, 0) + self.assertNotEqual(error_log_count, 0) + self.assertNotEqual(email_queue_count, 0) + + # run clean up job + run_log_clean_up() + + # test if logs are deleted + activity_log_count = xhiveframework.db.count("Activity Log", {"creation": ("<", self.datetime.past)}) + error_log_count = xhiveframework.db.count("Error Log", {"creation": ("<", self.datetime.past)}) + email_queue_count = xhiveframework.db.count("Email Queue", {"creation": ("<", self.datetime.past)}) + + self.assertEqual(activity_log_count, 0) + self.assertEqual(error_log_count, 0) + self.assertEqual(email_queue_count, 0) + + def test_logtype_identification(self): + supported_types = [ + "Error Log", + "Activity Log", + "Email Queue", + "Route History", + "Scheduled Job Log", + ] + + for lt in supported_types: + self.assertTrue(_supports_log_clearing(lt), f"{lt} should be recognized as log type") + + unsupported_types = ["DocType", "User", "Non Existing dt"] + for dt in unsupported_types: + self.assertFalse(_supports_log_clearing(dt), f"{dt} shouldn't be recognized as log type") + + +def setup_test_logs(past: datetime) -> None: + activity_log = xhiveframework.get_doc( + { + "doctype": "Activity Log", + "subject": "Test subject", + "full_name": "test user2", + } + ).insert(ignore_permissions=True) + activity_log.db_set("creation", past) + + error_log = xhiveframework.get_doc( + { + "doctype": "Error Log", + "method": "test_method", + "error": "traceback", + } + ).insert(ignore_permissions=True) + error_log.db_set("creation", past) + + doc1 = xhiveframework.get_doc( + { + "doctype": "Email Queue", + "sender": "test1@example.com", + "message": "This is a test email1", + "priority": 1, + "expose_recipients": "test@receiver.com", + } + ).insert(ignore_permissions=True) + doc1.db_set("creation", past) diff --git a/xhiveframework/core/doctype/logs_to_clear/__init__.py b/xhiveframework/core/doctype/logs_to_clear/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.json b/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.json new file mode 100644 index 0000000..df19ccd --- /dev/null +++ b/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2022-06-11 02:02:39.472511", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "days" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Log DocType", + "options": "DocType", + "reqd": 1 + }, + { + "default": "30", + "fieldname": "days", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Clear Logs After (days)", + "non_negative": 1, + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-06-13 02:51:36.857786", + "modified_by": "Administrator", + "module": "Core", + "name": "Logs To Clear", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.py b/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.py new file mode 100644 index 0000000..c73dc7d --- /dev/null +++ b/xhiveframework/core/doctype/logs_to_clear/logs_to_clear.py @@ -0,0 +1,23 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class LogsToClear(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + days: DF.Int + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + ref_doctype: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/module_def/README.md b/xhiveframework/core/doctype/module_def/README.md new file mode 100644 index 0000000..ff89390 --- /dev/null +++ b/xhiveframework/core/doctype/module_def/README.md @@ -0,0 +1 @@ +Module of the application (e.g. Core, Accounts etc.) \ No newline at end of file diff --git a/xhiveframework/core/doctype/module_def/__init__.py b/xhiveframework/core/doctype/module_def/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/module_def/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/module_def/module_def.js b/xhiveframework/core/doctype/module_def/module_def.js new file mode 100644 index 0000000..f07a2de --- /dev/null +++ b/xhiveframework/core/doctype/module_def/module_def.js @@ -0,0 +1,20 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Module Def", { + refresh: function (frm) { + xhiveframework.xcall("xhiveframework.core.doctype.module_def.module_def.get_installed_apps").then((r) => { + frm.set_df_property("app_name", "options", JSON.parse(r)); + if (!frm.doc.app_name) { + frm.set_value("app_name", "xhiveframework"); + } + }); + + if (!xhiveframework.boot.developer_mode) { + frm.set_df_property("custom", "read_only", 1); + if (frm.is_new()) { + frm.set_value("custom", 1); + } + } + }, +}); diff --git a/xhiveframework/core/doctype/module_def/module_def.json b/xhiveframework/core/doctype/module_def/module_def.json new file mode 100644 index 0000000..e9a03bc --- /dev/null +++ b/xhiveframework/core/doctype/module_def/module_def.json @@ -0,0 +1,166 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:module_name", + "creation": "2013-01-10 16:34:03", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "module_name", + "custom", + "package", + "app_name", + "restrict_to_domain", + "connections_tab" + ], + "fields": [ + { + "fieldname": "module_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Module Name", + "oldfieldname": "module_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "depends_on": "eval:!doc.custom", + "fieldname": "app_name", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "App Name", + "reqd": 1 + }, + { + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict To Domain", + "options": "Domain" + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "label": "Custom" + }, + { + "depends_on": "custom", + "fieldname": "package", + "fieldtype": "Link", + "label": "Package", + "options": "Package" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + } + ], + "icon": "fa fa-sitemap", + "idx": 1, + "links": [ + { + "group": "DocType", + "link_doctype": "DocType", + "link_fieldname": "module" + }, + { + "group": "DocType", + "link_doctype": "Client Script", + "link_fieldname": "module" + }, + { + "group": "DocType", + "link_doctype": "Server Script", + "link_fieldname": "module" + }, + { + "group": "Website", + "link_doctype": "Web Page", + "link_fieldname": "module" + }, + { + "group": "Website", + "link_doctype": "Web Template", + "link_fieldname": "module" + }, + { + "group": "Website", + "link_doctype": "Website Theme", + "link_fieldname": "module" + }, + { + "group": "Website", + "link_doctype": "Web Form", + "link_fieldname": "module" + }, + { + "group": "Customization", + "link_doctype": "Workspace", + "link_fieldname": "module" + }, + { + "group": "Customization", + "link_doctype": "Custom Field", + "link_fieldname": "module" + }, + { + "group": "Customization", + "link_doctype": "Property Setter", + "link_fieldname": "module" + }, + { + "group": "Customization", + "link_doctype": "Print Format", + "link_fieldname": "module" + }, + { + "group": "Customization", + "link_doctype": "Notification", + "link_fieldname": "module" + } + ], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Core", + "name": "Module Def", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "select": 1, + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "select": 1, + "write": 1 + }, + { + "read": 1, + "report": 1, + "role": "All", + "select": 1 + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/module_def/module_def.py b/xhiveframework/core/doctype/module_def/module_def.py new file mode 100644 index 0000000..be06005 --- /dev/null +++ b/xhiveframework/core/doctype/module_def/module_def.py @@ -0,0 +1,89 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import os +from pathlib import Path + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.modules.export_file import delete_folder + + +class ModuleDef(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + app_name: DF.Literal[None] + custom: DF.Check + module_name: DF.Data + package: DF.Link | None + restrict_to_domain: DF.Link | None + + # end: auto-generated types + def on_update(self): + """If in `developer_mode`, create folder for module and + add in `modules.txt` of app if missing.""" + xhiveframework.clear_cache() + if not self.custom and xhiveframework.conf.get("developer_mode"): + self.create_modules_folder() + self.add_to_modules_txt() + + def create_modules_folder(self): + """Creates a folder `[app]/[module]` and adds `__init__.py`""" + module_path = xhiveframework.get_app_path(self.app_name, self.name) + if not os.path.exists(module_path): + os.mkdir(module_path) + with open(os.path.join(module_path, "__init__.py"), "w") as f: + f.write("") + + def add_to_modules_txt(self): + """Adds to `[app]/modules.txt`""" + modules = None + if not xhiveframework.local.module_app.get(xhiveframework.scrub(self.name)): + with open(xhiveframework.get_app_path(self.app_name, "modules.txt")) as f: + content = f.read() + if self.name not in content.splitlines(): + modules = list(filter(None, content.splitlines())) + modules.append(self.name) + + if modules: + with open(xhiveframework.get_app_path(self.app_name, "modules.txt"), "w") as f: + f.write("\n".join(modules)) + + xhiveframework.clear_cache() + xhiveframework.setup_module_map() + + def on_trash(self): + """Delete module name from modules.txt""" + + if not xhiveframework.conf.get("developer_mode") or xhiveframework.flags.in_uninstall or self.custom: + return + + if xhiveframework.local.module_app.get(xhiveframework.scrub(self.name)): + xhiveframework.db.after_commit.add(self.delete_module_from_file) + + def delete_module_from_file(self): + delete_folder(self.module_name, "Module Def", self.name) + modules = [] + + modules_txt = Path(xhiveframework.get_app_path(self.app_name, "modules.txt")) + modules = [m for m in modules_txt.read_text().splitlines() if m] + + if self.name in modules: + modules.remove(self.name) + + if modules: + modules_txt.write_text("\n".join(modules)) + xhiveframework.clear_cache() + xhiveframework.setup_module_map() + + +@xhiveframework.whitelist() +def get_installed_apps(): + return json.dumps(xhiveframework.get_installed_apps()) diff --git a/xhiveframework/core/doctype/module_def/module_def_list.js b/xhiveframework/core/doctype/module_def/module_def_list.js new file mode 100644 index 0000000..b2067fb --- /dev/null +++ b/xhiveframework/core/doctype/module_def/module_def_list.js @@ -0,0 +1,16 @@ +xhiveframework.listview_settings["Module Def"] = { + onload: function (list_view) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.module_def.module_def.get_installed_apps", + callback: (r) => { + const field = list_view.page.fields_dict.app_name; + if (!field) return; + + const options = JSON.parse(r.message); + options.unshift(""); // Add empty option + field.df.options = options; + field.set_options(); + }, + }); + }, +}; diff --git a/xhiveframework/core/doctype/module_def/test_module_def.py b/xhiveframework/core/doctype/module_def/test_module_def.py new file mode 100644 index 0000000..819d38a --- /dev/null +++ b/xhiveframework/core/doctype/module_def/test_module_def.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Module Def') + + +class TestModuleDef(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/module_profile/__init__.py b/xhiveframework/core/doctype/module_profile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/module_profile/module_profile.js b/xhiveframework/core/doctype/module_profile/module_profile.js new file mode 100644 index 0000000..15d0755 --- /dev/null +++ b/xhiveframework/core/doctype/module_profile/module_profile.js @@ -0,0 +1,23 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Module Profile", { + refresh: function (frm) { + if (has_common(xhiveframework.user_roles, ["Administrator", "System Manager"])) { + if (!frm.module_editor && frm.doc.__onload && frm.doc.__onload.all_modules) { + const module_area = $(frm.fields_dict.module_html.wrapper); + frm.module_editor = new xhiveframework.ModuleEditor(frm, module_area); + } + } + + if (frm.module_editor) { + frm.module_editor.show(); + } + }, + + validate: function (frm) { + if (frm.module_editor) { + frm.module_editor.set_modules_in_table(); + } + }, +}); diff --git a/xhiveframework/core/doctype/module_profile/module_profile.json b/xhiveframework/core/doctype/module_profile/module_profile.json new file mode 100644 index 0000000..32bc757 --- /dev/null +++ b/xhiveframework/core/doctype/module_profile/module_profile.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "autoname": "field:module_profile_name", + "creation": "2020-12-22 22:00:30.614475", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "module_profile_name", + "module_html", + "block_modules" + ], + "fields": [ + { + "fieldname": "module_profile_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Module Profile Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "module_html", + "fieldtype": "HTML", + "label": "Module HTML" + }, + { + "fieldname": "block_modules", + "fieldtype": "Table", + "hidden": 1, + "label": "Block Modules", + "options": "Block Module", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "link_doctype": "User", + "link_fieldname": "module_profile" + } + ], + "modified": "2021-12-03 15:47:21.296443", + "modified_by": "Administrator", + "module": "Core", + "name": "Module Profile", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/module_profile/module_profile.py b/xhiveframework/core/doctype/module_profile/module_profile.py new file mode 100644 index 0000000..1262193 --- /dev/null +++ b/xhiveframework/core/doctype/module_profile/module_profile.py @@ -0,0 +1,24 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class ModuleProfile(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.block_module.block_module import BlockModule + from xhiveframework.types import DF + + block_modules: DF.Table[BlockModule] + module_profile_name: DF.Data + + # end: auto-generated types + def onload(self): + from xhiveframework.config import get_modules_from_all_apps + + self.set_onload("all_modules", sorted(m.get("module_name") for m in get_modules_from_all_apps())) diff --git a/xhiveframework/core/doctype/module_profile/test_module_profile.py b/xhiveframework/core/doctype/module_profile/test_module_profile.py new file mode 100644 index 0000000..a3c3232 --- /dev/null +++ b/xhiveframework/core/doctype/module_profile/test_module_profile.py @@ -0,0 +1,29 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestModuleProfile(XhiveFrameworkTestCase): + def test_make_new_module_profile(self): + if not xhiveframework.db.get_value("Module Profile", "_Test Module Profile"): + xhiveframework.get_doc( + { + "doctype": "Module Profile", + "module_profile_name": "_Test Module Profile", + "block_modules": [{"module": "Accounts"}], + } + ).insert() + + # add to user and check + if not xhiveframework.db.get_value("User", "test-for-module_profile@example.com"): + new_user = xhiveframework.get_doc( + {"doctype": "User", "email": "test-for-module_profile@example.com", "first_name": "Test User"} + ).insert() + else: + new_user = xhiveframework.get_doc("User", "test-for-module_profile@example.com") + + new_user.module_profile = "_Test Module Profile" + new_user.save() + + self.assertEqual(new_user.block_modules[0].module, "Accounts") diff --git a/xhiveframework/core/doctype/navbar_item/__init__.py b/xhiveframework/core/doctype/navbar_item/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/navbar_item/navbar_item.js b/xhiveframework/core/doctype/navbar_item/navbar_item.js new file mode 100644 index 0000000..c266612 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_item/navbar_item.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Navbar Item", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/navbar_item/navbar_item.json b/xhiveframework/core/doctype/navbar_item/navbar_item.json new file mode 100644 index 0000000..541d785 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_item/navbar_item.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "creation": "2020-08-01 23:38:41.783206", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "item_label", + "item_type", + "route", + "action", + "hidden", + "is_standard" + ], + "fields": [ + { + "columns": 2, + "fieldname": "item_label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Label", + "mandatory_depends_on": "eval:doc.item_type == 'Route' || doc.item_type == 'Action'", + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 2, + "fieldname": "item_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Item Type", + "options": "Route\nAction\nSeparator", + "read_only_depends_on": "eval:doc.is_standard", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Hidden", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "columns": 4, + "depends_on": "eval:doc.item_type == 'Route'", + "fieldname": "route", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Route", + "mandatory_depends_on": "eval:doc.item_type == 'Route'", + "read_only_depends_on": "eval:doc.is_standard", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:doc.item_type == 'Action'", + "fieldname": "action", + "fieldtype": "Data", + "label": "Action", + "mandatory_depends_on": "eval:doc.item_type == 'Action'", + "read_only_depends_on": "eval:doc.is_standard", + "show_days": 1, + "show_seconds": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-11-02 10:57:37.709262", + "modified_by": "Administrator", + "module": "Core", + "name": "Navbar Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/navbar_item/navbar_item.py b/xhiveframework/core/doctype/navbar_item/navbar_item.py new file mode 100644 index 0000000..bc7d128 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_item/navbar_item.py @@ -0,0 +1,27 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class NavbarItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action: DF.Data | None + hidden: DF.Check + is_standard: DF.Check + item_label: DF.Data | None + item_type: DF.Literal["Route", "Action", "Separator"] + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + route: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/navbar_item/test_navbar_item.py b/xhiveframework/core/doctype/navbar_item/test_navbar_item.py new file mode 100644 index 0000000..6123891 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_item/test_navbar_item.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNavbarItem(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/navbar_settings/__init__.py b/xhiveframework/core/doctype/navbar_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/navbar_settings/navbar_settings.js b/xhiveframework/core/doctype/navbar_settings/navbar_settings.js new file mode 100644 index 0000000..631eaa1 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_settings/navbar_settings.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Navbar Settings", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/navbar_settings/navbar_settings.json b/xhiveframework/core/doctype/navbar_settings/navbar_settings.json new file mode 100644 index 0000000..8fc0c83 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_settings/navbar_settings.json @@ -0,0 +1,91 @@ +{ + "actions": [], + "creation": "2020-08-01 23:41:12.577160", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "logo_section", + "app_logo", + "column_break_3", + "logo_width", + "section_break_2", + "settings_dropdown", + "help_dropdown" + ], + "fields": [ + { + "fieldname": "app_logo", + "fieldtype": "Attach Image", + "label": "Application Logo", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "settings_dropdown", + "fieldtype": "Table", + "label": "Settings Dropdown", + "options": "Navbar Item", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "help_dropdown", + "fieldtype": "Table", + "label": "Help Dropdown", + "options": "Navbar Item", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Dropdowns", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "logo_section", + "fieldtype": "Section Break", + "label": "Application Logo", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "logo_width", + "fieldtype": "Int", + "label": "Logo Width", + "show_days": 1, + "show_seconds": 1 + } + ], + "issingle": 1, + "links": [], + "modified": "2020-08-06 18:11:29.955835", + "modified_by": "Administrator", + "module": "Core", + "name": "Navbar Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/navbar_settings/navbar_settings.py b/xhiveframework/core/doctype/navbar_settings/navbar_settings.py new file mode 100644 index 0000000..b2bdcf1 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_settings/navbar_settings.py @@ -0,0 +1,55 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class NavbarSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.navbar_item.navbar_item import NavbarItem + from xhiveframework.types import DF + + app_logo: DF.AttachImage | None + help_dropdown: DF.Table[NavbarItem] + logo_width: DF.Int + settings_dropdown: DF.Table[NavbarItem] + + # end: auto-generated types + def validate(self): + self.validate_standard_navbar_items() + + def validate_standard_navbar_items(self): + doc_before_save = self.get_doc_before_save() + + if not doc_before_save: + return + + before_save_items = [ + item + for item in doc_before_save.help_dropdown + doc_before_save.settings_dropdown + if item.is_standard + ] + + after_save_items = [item for item in self.help_dropdown + self.settings_dropdown if item.is_standard] + + if not xhiveframework.flags.in_patch and (len(before_save_items) > len(after_save_items)): + xhiveframework.throw(_("Please hide the standard navbar items instead of deleting them")) + + +def get_app_logo(): + app_logo = xhiveframework.db.get_single_value("Navbar Settings", "app_logo", cache=True) + if not app_logo: + app_logo = xhiveframework.get_hooks("app_logo_url")[-1] + + return app_logo + + +def get_navbar_settings(): + return xhiveframework.get_single("Navbar Settings") diff --git a/xhiveframework/core/doctype/navbar_settings/test_navbar_settings.py b/xhiveframework/core/doctype/navbar_settings/test_navbar_settings.py new file mode 100644 index 0000000..b7c0803 --- /dev/null +++ b/xhiveframework/core/doctype/navbar_settings/test_navbar_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNavbarSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/package/__init__.py b/xhiveframework/core/doctype/package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/package/licenses/GNU Affero General Public License.md b/xhiveframework/core/doctype/package/licenses/GNU Affero General Public License.md new file mode 100644 index 0000000..c7f159a --- /dev/null +++ b/xhiveframework/core/doctype/package/licenses/GNU Affero General Public License.md @@ -0,0 +1,614 @@ +### GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/xhiveframework/core/doctype/package/licenses/GNU General Public License.md b/xhiveframework/core/doctype/package/licenses/GNU General Public License.md new file mode 100644 index 0000000..c4580f2 --- /dev/null +++ b/xhiveframework/core/doctype/package/licenses/GNU General Public License.md @@ -0,0 +1,617 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. diff --git a/xhiveframework/core/doctype/package/licenses/MIT License.md b/xhiveframework/core/doctype/package/licenses/MIT License.md new file mode 100644 index 0000000..c038ee7 --- /dev/null +++ b/xhiveframework/core/doctype/package/licenses/MIT License.md @@ -0,0 +1,17 @@ +### MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/xhiveframework/core/doctype/package/package.js b/xhiveframework/core/doctype/package/package.js new file mode 100644 index 0000000..36ec020 --- /dev/null +++ b/xhiveframework/core/doctype/package/package.js @@ -0,0 +1,20 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Package", { + validate: function (frm) { + if (!frm.doc.package_name) { + frm.set_value("package_name", frm.doc.name.toLowerCase().replace(" ", "-")); + } + }, + + license_type: function (frm) { + xhiveframework + .call("xhiveframework.core.doctype.package.package.get_license_text", { + license_type: frm.doc.license_type, + }) + .then((r) => { + frm.set_value("license", r.message); + }); + }, +}); diff --git a/xhiveframework/core/doctype/package/package.json b/xhiveframework/core/doctype/package/package.json new file mode 100644 index 0000000..285e17a --- /dev/null +++ b/xhiveframework/core/doctype/package/package.json @@ -0,0 +1,76 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2021-09-04 11:54:35.155687", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "package_name", + "readme", + "license_type", + "license" + ], + "fields": [ + { + "fieldname": "readme", + "fieldtype": "Markdown Editor", + "label": "Readme" + }, + { + "fieldname": "package_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Package Name", + "reqd": 1 + }, + { + "fieldname": "license_type", + "fieldtype": "Select", + "label": "License Type", + "options": "\nMIT License\nGNU General Public License\nGNU Affero General Public License" + }, + { + "fieldname": "license", + "fieldtype": "Markdown Editor", + "label": "License" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "group": "Modules", + "link_doctype": "Module Def", + "link_fieldname": "package" + }, + { + "group": "Release", + "link_doctype": "Package Release", + "link_fieldname": "package" + } + ], + "modified": "2021-09-05 13:15:01.130982", + "modified_by": "Administrator", + "module": "Core", + "name": "Package", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/package/package.py b/xhiveframework/core/doctype/package/package.py new file mode 100644 index 0000000..7b7c849 --- /dev/null +++ b/xhiveframework/core/doctype/package/package.py @@ -0,0 +1,42 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import os + +import xhiveframework +from xhiveframework.model.document import Document + +LICENSES = ( + "GNU Affero General Public License", + "GNU General Public License", + "MIT License", +) + + +class Package(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + license: DF.MarkdownEditor | None + license_type: DF.Literal[ + "", "MIT License", "GNU General Public License", "GNU Affero General Public License" + ] + package_name: DF.Data + readme: DF.MarkdownEditor | None + + # end: auto-generated types + def validate(self): + if not self.package_name: + self.package_name = self.name.lower().replace(" ", "-") + + +@xhiveframework.whitelist() +def get_license_text(license_type: str) -> str | None: + if license_type in LICENSES: + with open(os.path.join(os.path.dirname(__file__), "licenses", license_type + ".md")) as textfile: + return textfile.read() diff --git a/xhiveframework/core/doctype/package/test_package.py b/xhiveframework/core/doctype/package/test_package.py new file mode 100644 index 0000000..0df8eb6 --- /dev/null +++ b/xhiveframework/core/doctype/package/test_package.py @@ -0,0 +1,112 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +import json +import os + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPackage(XhiveFrameworkTestCase): + def test_package_release(self): + make_test_package() + make_test_module() + make_test_doctype() + make_test_server_script() + make_test_web_page() + + # make release + xhiveframework.get_doc(dict(doctype="Package Release", package="Test Package", publish=1)).insert() + + self.assertTrue(os.path.exists(xhiveframework.get_site_path("packages", "test-package"))) + self.assertTrue( + os.path.exists(xhiveframework.get_site_path("packages", "test-package", "test_module_for_package")) + ) + self.assertTrue( + os.path.exists( + xhiveframework.get_site_path( + "packages", + "test-package", + "test_module_for_package", + "doctype", + "test_doctype_for_package", + ) + ) + ) + with open( + xhiveframework.get_site_path( + "packages", + "test-package", + "test_module_for_package", + "doctype", + "test_doctype_for_package", + "test_doctype_for_package.json", + ) + ) as f: + doctype = json.loads(f.read()) + self.assertEqual(doctype["doctype"], "DocType") + self.assertEqual(doctype["name"], "Test DocType for Package") + self.assertEqual(doctype["fields"][0]["fieldname"], "test_field") + + +def make_test_package(): + if not xhiveframework.db.exists("Package", "Test Package"): + xhiveframework.get_doc( + dict(doctype="Package", name="Test Package", package_name="test-package", readme="# Test Package") + ).insert() + + +def make_test_module(): + if not xhiveframework.db.exists("Module Def", "Test Module for Package"): + xhiveframework.get_doc( + dict( + doctype="Module Def", + module_name="Test Module for Package", + custom=1, + app_name="xhiveframework", + package="Test Package", + ) + ).insert() + + +def make_test_doctype(): + if not xhiveframework.db.exists("DocType", "Test DocType for Package"): + xhiveframework.get_doc( + dict( + doctype="DocType", + name="Test DocType for Package", + custom=1, + module="Test Module for Package", + autoname="Prompt", + fields=[dict(fieldname="test_field", fieldtype="Data", label="Test Field")], + ) + ).insert() + + +def make_test_server_script(): + if not xhiveframework.db.exists("Server Script", "Test Script for Package"): + xhiveframework.get_doc( + dict( + doctype="Server Script", + name="Test Script for Package", + module="Test Module for Package", + script_type="DocType Event", + reference_doctype="Test DocType for Package", + doctype_event="Before Save", + script='xhiveframework.msgprint("Test")', + ) + ).insert() + + +def make_test_web_page(): + if not xhiveframework.db.exists("Web Page", "test-web-page-for-package"): + xhiveframework.get_doc( + dict( + doctype="Web Page", + module="Test Module for Package", + main_section="Some content", + published=1, + title="Test Web Page for Package", + ) + ).insert() diff --git a/xhiveframework/core/doctype/package_import/__init__.py b/xhiveframework/core/doctype/package_import/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/package_import/package_import.js b/xhiveframework/core/doctype/package_import/package_import.js new file mode 100644 index 0000000..11be35d --- /dev/null +++ b/xhiveframework/core/doctype/package_import/package_import.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Package Import", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/package_import/package_import.json b/xhiveframework/core/doctype/package_import/package_import.json new file mode 100644 index 0000000..f3c6168 --- /dev/null +++ b/xhiveframework/core/doctype/package_import/package_import.json @@ -0,0 +1,65 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:Package Import at {creation}", + "creation": "2021-09-05 16:36:46.680094", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "attach_package", + "activate", + "force", + "log" + ], + "fields": [ + { + "fieldname": "attach_package", + "fieldtype": "Attach", + "label": "Attach Package" + }, + { + "default": "0", + "fieldname": "activate", + "fieldtype": "Check", + "label": "Activate" + }, + { + "fieldname": "log", + "fieldtype": "Code", + "label": "Log", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "force", + "fieldtype": "Check", + "label": "Force" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-09-05 21:30:04.796090", + "modified_by": "Administrator", + "module": "Core", + "name": "Package Import", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/package_import/package_import.py b/xhiveframework/core/doctype/package_import/package_import.py new file mode 100644 index 0000000..24d4be4 --- /dev/null +++ b/xhiveframework/core/doctype/package_import/package_import.py @@ -0,0 +1,80 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import json +import os +import subprocess + +import xhiveframework +from xhiveframework.desk.form.load import get_attachments +from xhiveframework.model.document import Document +from xhiveframework.model.sync import get_doc_files +from xhiveframework.modules.import_file import import_doc, import_file_by_path +from xhiveframework.utils import get_files_path + + +class PackageImport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + activate: DF.Check + attach_package: DF.Attach | None + force: DF.Check + log: DF.Code | None + + # end: auto-generated types + def validate(self): + if self.activate: + self.import_package() + + def import_package(self): + attachment = get_attachments(self.doctype, self.name) + + if not attachment: + xhiveframework.throw(xhiveframework._("Please attach the package")) + + attachment = attachment[0] + + # get package_name from file (package_name-0.0.0.tar.gz) + package_name = attachment.file_name.split(".", 1)[0].rsplit("-", 1)[0] + if not os.path.exists(xhiveframework.get_site_path("packages")): + os.makedirs(xhiveframework.get_site_path("packages")) + + # extract + subprocess.check_output( + [ + "tar", + "xzf", + get_files_path(attachment.file_name, is_private=attachment.is_private), + "-C", + xhiveframework.get_site_path("packages"), + ] + ) + + package_path = xhiveframework.get_site_path("packages", package_name) + + # import Package + with open(os.path.join(package_path, package_name + ".json")) as packagefile: + doc_dict = json.loads(packagefile.read()) + + xhiveframework.flags.package = import_doc(doc_dict) + + # collect modules + files = [] + log = [] + for module in os.listdir(package_path): + module_path = os.path.join(package_path, module) + if os.path.isdir(module_path): + files = get_doc_files(files, module_path) + + # import files + for file in files: + import_file_by_path(file, force=self.force, ignore_version=True) + log.append(f"Imported {file}") + + self.log = "\n".join(log) diff --git a/xhiveframework/core/doctype/package_import/test_package_import.py b/xhiveframework/core/doctype/package_import/test_package_import.py new file mode 100644 index 0000000..28b26f3 --- /dev/null +++ b/xhiveframework/core/doctype/package_import/test_package_import.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPackageImport(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/package_release/__init__.py b/xhiveframework/core/doctype/package_release/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/package_release/package_release.js b/xhiveframework/core/doctype/package_release/package_release.js new file mode 100644 index 0000000..2ea4cc0 --- /dev/null +++ b/xhiveframework/core/doctype/package_release/package_release.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Package Release", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/package_release/package_release.json b/xhiveframework/core/doctype/package_release/package_release.json new file mode 100644 index 0000000..b651d69 --- /dev/null +++ b/xhiveframework/core/doctype/package_release/package_release.json @@ -0,0 +1,95 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2021-09-05 12:59:01.932327", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "package", + "publish", + "path", + "column_break_3", + "major", + "minor", + "patch", + "section_break_7", + "release_notes" + ], + "fields": [ + { + "fieldname": "package", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Package", + "options": "Package", + "reqd": 1 + }, + { + "fieldname": "major", + "fieldtype": "Int", + "label": "Major" + }, + { + "fieldname": "minor", + "fieldtype": "Int", + "label": "Minor" + }, + { + "fieldname": "patch", + "fieldtype": "Int", + "label": "Patch", + "no_copy": 1 + }, + { + "fieldname": "path", + "fieldtype": "Small Text", + "label": "Path", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "release_notes", + "fieldtype": "Markdown Editor", + "label": "Release Notes" + }, + { + "default": "0", + "fieldname": "publish", + "fieldtype": "Check", + "label": "Publish" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-09-05 16:04:32.860988", + "modified_by": "Administrator", + "module": "Core", + "name": "Package Release", + "naming_rule": "By script", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/package_release/package_release.py b/xhiveframework/core/doctype/package_release/package_release.py new file mode 100644 index 0000000..a956541 --- /dev/null +++ b/xhiveframework/core/doctype/package_release/package_release.py @@ -0,0 +1,128 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import os +import subprocess + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.modules.export_file import export_doc +from xhiveframework.query_builder.functions import Max + + +class PackageRelease(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + major: DF.Int + minor: DF.Int + package: DF.Link + patch: DF.Int + path: DF.SmallText | None + publish: DF.Check + release_notes: DF.MarkdownEditor | None + + # end: auto-generated types + def set_version(self): + # set the next patch release by default + doctype = xhiveframework.qb.DocType("Package Release") + if not self.major: + self.major = ( + xhiveframework.qb.from_(doctype) + .where(doctype.package == self.package) + .select(Max(doctype.minor)) + .run()[0][0] + or 0 + ) + + if not self.minor: + self.minor = ( + xhiveframework.qb.from_(doctype) + .where(doctype.package == self.package) + .select(Max("minor")) + .run()[0][0] + or 0 + ) + if not self.patch: + value = ( + xhiveframework.qb.from_(doctype) + .where(doctype.package == self.package) + .select(Max("patch")) + .run()[0][0] + or 0 + ) + self.patch = value + 1 + + def autoname(self): + self.set_version() + self.name = "{}-{}.{}.{}".format( + xhiveframework.db.get_value("Package", self.package, "package_name"), self.major, self.minor, self.patch + ) + + def validate(self): + if self.publish: + self.export_files() + + def export_files(self): + """Export all the documents in this package to site/packages folder""" + package = xhiveframework.get_doc("Package", self.package) + + self.export_modules() + self.export_package_files(package) + self.make_tarfile(package) + + def export_modules(self): + for m in xhiveframework.get_all("Module Def", dict(package=self.package)): + module = xhiveframework.get_doc("Module Def", m.name) + for l in module.meta.links: + if l.link_doctype == "Module Def": + continue + # all documents of the type in the module + for d in xhiveframework.get_all(l.link_doctype, dict(module=m.name)): + export_doc(xhiveframework.get_doc(l.link_doctype, d.name)) + + def export_package_files(self, package): + # write readme + with open(xhiveframework.get_site_path("packages", package.package_name, "README.md"), "w") as readme: + readme.write(package.readme) + + # write license + if package.license: + with open(xhiveframework.get_site_path("packages", package.package_name, "LICENSE.md"), "w") as license: + license.write(package.license) + + # write package.json as `xhiveframework_package.json` + with open( + xhiveframework.get_site_path("packages", package.package_name, package.package_name + ".json"), "w" + ) as packagefile: + packagefile.write(xhiveframework.as_json(package.as_dict(no_nulls=True))) + + def make_tarfile(self, package): + # make tarfile + filename = f"{self.name}.tar.gz" + subprocess.check_output( + ["tar", "czf", filename, package.package_name], cwd=xhiveframework.get_site_path("packages") + ) + + # move file + subprocess.check_output( + ["mv", xhiveframework.get_site_path("packages", filename), xhiveframework.get_site_path("public", "files")] + ) + + # make attachment + file = xhiveframework.get_doc( + dict( + doctype="File", + file_url="/" + os.path.join("files", filename), + attached_to_doctype=self.doctype, + attached_to_name=self.name, + ) + ) + + file.flags.ignore_duplicate_entry_error = True + file.insert() diff --git a/xhiveframework/core/doctype/package_release/test_package_release.py b/xhiveframework/core/doctype/package_release/test_package_release.py new file mode 100644 index 0000000..3668ceb --- /dev/null +++ b/xhiveframework/core/doctype/package_release/test_package_release.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPackageRelease(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/page/README.md b/xhiveframework/core/doctype/page/README.md new file mode 100644 index 0000000..0a06292 --- /dev/null +++ b/xhiveframework/core/doctype/page/README.md @@ -0,0 +1 @@ +A page (view) in the application. A page is made to define custom user interfaces and contains server side and client side code. Standard events are attached to the page and called when the page is loaded, shown etc. \ No newline at end of file diff --git a/xhiveframework/core/doctype/page/__init__.py b/xhiveframework/core/doctype/page/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/page/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/page/page.js b/xhiveframework/core/doctype/page/page.js new file mode 100644 index 0000000..cad514a --- /dev/null +++ b/xhiveframework/core/doctype/page/page.js @@ -0,0 +1,16 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Page", { + refresh: function (frm) { + if (!xhiveframework.boot.developer_mode && xhiveframework.session.user != "Administrator") { + // make the document read-only + frm.set_read_only(); + } + if (!frm.is_new() && !frm.doc.istable) { + frm.add_custom_button(__("Go to {0} Page", [frm.doc.title || frm.doc.name]), () => { + xhiveframework.set_route(frm.doc.name); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/page/page.json b/xhiveframework/core/doctype/page/page.json new file mode 100644 index 0000000..2105bc7 --- /dev/null +++ b/xhiveframework/core/doctype/page/page.json @@ -0,0 +1,133 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:page_name", + "creation": "2012-12-20 17:16:49", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "system_page", + "page_html", + "page_name", + "title", + "icon", + "column_break0", + "module", + "restrict_to_domain", + "standard", + "section_break0", + "roles" + ], + "fields": [ + { + "default": "0", + "fieldname": "system_page", + "fieldtype": "Check", + "label": "System Page" + }, + { + "fieldname": "page_html", + "fieldtype": "Section Break", + "label": "Page HTML", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "page_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Page Name", + "oldfieldname": "page_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "no_copy": 1 + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "in_list_view": 1, + "label": "icon" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Module", + "oldfieldname": "module", + "oldfieldtype": "Select", + "options": "Module Def", + "reqd": 1 + }, + { + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict To Domain", + "options": "Domain" + }, + { + "fieldname": "standard", + "fieldtype": "Select", + "label": "Standard", + "oldfieldname": "standard", + "oldfieldtype": "Select", + "options": "Yes\nNo", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "section_break0", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.standard == 'Yes'", + "fieldname": "roles", + "fieldtype": "Table", + "label": "Roles", + "oldfieldname": "roles", + "oldfieldtype": "Table", + "options": "Has Role" + } + ], + "icon": "fa fa-file", + "idx": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Core", + "name": "Page", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/page/page.py b/xhiveframework/core/doctype/page/page.py new file mode 100644 index 0000000..f83e18b --- /dev/null +++ b/xhiveframework/core/doctype/page/page.py @@ -0,0 +1,190 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import os + +import xhiveframework +from xhiveframework import _, conf, safe_decode +from xhiveframework.build import html_to_js_template +from xhiveframework.core.doctype.custom_role.custom_role import get_custom_allowed_roles +from xhiveframework.desk.form.meta import get_code_files_via_hooks, get_js +from xhiveframework.desk.utils import validate_route_conflict +from xhiveframework.model.document import Document +from xhiveframework.model.utils import render_include + + +class Page(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.types import DF + + icon: DF.Data | None + module: DF.Link + page_name: DF.Data + restrict_to_domain: DF.Link | None + roles: DF.Table[HasRole] + standard: DF.Literal["Yes", "No"] + system_page: DF.Check + title: DF.Data | None + + # end: auto-generated types + def autoname(self): + """ + Creates a url friendly name for this page. + Will restrict the name to 30 characters, if there exists a similar name, + it will add name-1, name-2 etc. + """ + from xhiveframework.utils import cint + + if (self.name and self.name.startswith("New Page")) or not self.name: + self.name = self.page_name.lower().replace('"', "").replace("'", "").replace(" ", "-")[:20] + if xhiveframework.db.exists("Page", self.name): + cnt = xhiveframework.db.sql( + """select name from tabPage + where name like "%s-%%" order by name desc limit 1""" + % self.name + ) + if cnt: + cnt = cint(cnt[0][0].split("-")[-1]) + 1 + else: + cnt = 1 + self.name += "-" + str(cnt) + + def validate(self): + validate_route_conflict(self.doctype, self.name) + + if self.is_new() and not getattr(conf, "developer_mode", 0): + xhiveframework.throw(_("Not in Developer Mode")) + + # setting ignore_permissions via update_setup_wizard_access (setup_wizard.py) + if xhiveframework.session.user != "Administrator" and not self.flags.ignore_permissions: + xhiveframework.throw(_("Only Administrator can edit")) + + # export + def on_update(self): + """ + Writes the .json for this page and if write_content is checked, + it will write out a .html file + """ + if self.flags.do_not_update_json: + return + + from xhiveframework.core.doctype.doctype.doctype import make_module_and_roles + + make_module_and_roles(self, "roles") + + from xhiveframework.modules.utils import export_module_json + + path = export_module_json(self, self.standard == "Yes", self.module) + + if path: + # js + if not os.path.exists(path + ".js"): + with open(path + ".js", "w") as f: + f.write( + f"""xhiveframework.pages['{self.name}'].on_page_load = function(wrapper) {{ + var page = xhiveframework.ui.make_app_page({{ + parent: wrapper, + title: '{self.title}', + single_column: true + }}); +}}""" + ) + + def as_dict(self, no_nulls=False): + d = super().as_dict(no_nulls=no_nulls) + for key in ("script", "style", "content"): + d[key] = self.get(key) + return d + + def on_trash(self): + delete_custom_role("page", self.name) + + def is_permitted(self): + """Returns true if Has Role is not set or the user is allowed.""" + from xhiveframework.utils import has_common + + allowed = [d.role for d in xhiveframework.get_all("Has Role", fields=["role"], filters={"parent": self.name})] + + custom_roles = get_custom_allowed_roles("page", self.name) + allowed.extend(custom_roles) + + if not allowed: + return True + + roles = xhiveframework.get_roles() + + if has_common(roles, allowed): + return True + + def load_assets(self): + import os + + from xhiveframework.modules import get_module_path, scrub + + self.script = "" + + page_name = scrub(self.name) + + path = os.path.join(get_module_path(self.module), "page", page_name) + + # script + fpath = os.path.join(path, page_name + ".js") + if os.path.exists(fpath): + with open(fpath) as f: + self.script = render_include(f.read()) + self.script += f"\n\n//# sourceURL={page_name}.js" + + # css + fpath = os.path.join(path, page_name + ".css") + if os.path.exists(fpath): + with open(fpath) as f: + self.style = safe_decode(f.read()) + + # html as js template + for fname in os.listdir(path): + if fname.endswith(".html"): + with open(os.path.join(path, fname)) as f: + template = f.read() + if "" in template: + context = xhiveframework._dict({}) + try: + out = xhiveframework.get_attr( + "{app}.{module}.page.{page}.{page}.get_context".format( + app=xhiveframework.local.module_app[scrub(self.module)], + module=scrub(self.module), + page=page_name, + ) + )(context) + + if out: + context = out + except (AttributeError, ImportError): + pass + + template = xhiveframework.render_template(template, context) + self.script = html_to_js_template(fname, template) + self.script + + # flag for not caching this page + self._dynamic_page = True + + if xhiveframework.lang != "en": + from xhiveframework.translate import get_lang_js + + self.script += get_lang_js("page", self.name) + + for path in get_code_files_via_hooks("page_js", self.name): + js = get_js(path) + if js: + self.script += "\n\n" + js + + +def delete_custom_role(field, docname): + name = xhiveframework.db.get_value("Custom Role", {field: docname}, "name") + if name: + xhiveframework.delete_doc("Custom Role", name) diff --git a/xhiveframework/core/doctype/page/patches/drop_unused_pages.py b/xhiveframework/core/doctype/page/patches/drop_unused_pages.py new file mode 100644 index 0000000..b084194 --- /dev/null +++ b/xhiveframework/core/doctype/page/patches/drop_unused_pages.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + for name in ("desktop", "space"): + xhiveframework.delete_doc("Page", name) diff --git a/xhiveframework/core/doctype/page/test_page.py b/xhiveframework/core/doctype/page/test_page.py new file mode 100644 index 0000000..277acb3 --- /dev/null +++ b/xhiveframework/core/doctype/page/test_page.py @@ -0,0 +1,14 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Page") + + +class TestPage(XhiveFrameworkTestCase): + def test_naming(self): + self.assertRaises( + xhiveframework.NameError, + xhiveframework.get_doc(dict(doctype="Page", page_name="DocType", module="Core")).insert, + ) diff --git a/xhiveframework/core/doctype/page/test_records.json b/xhiveframework/core/doctype/page/test_records.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/xhiveframework/core/doctype/page/test_records.json @@ -0,0 +1 @@ +[] diff --git a/xhiveframework/core/doctype/patch_log/README.md b/xhiveframework/core/doctype/patch_log/README.md new file mode 100644 index 0000000..080490e --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/README.md @@ -0,0 +1 @@ +Log of patch executed. Used by `patch_handler` to check if a particular patch is executed or not. \ No newline at end of file diff --git a/xhiveframework/core/doctype/patch_log/__init__.py b/xhiveframework/core/doctype/patch_log/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/patch_log/patch_log.js b/xhiveframework/core/doctype/patch_log/patch_log.js new file mode 100644 index 0000000..cbff884 --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/patch_log.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, XhiveFramework Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Patch Log", { + refresh: function (frm) { + frm.disable_save(); + }, +}); diff --git a/xhiveframework/core/doctype/patch_log/patch_log.json b/xhiveframework/core/doctype/patch_log/patch_log.json new file mode 100644 index 0000000..6be3ce0 --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/patch_log.json @@ -0,0 +1,69 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-01-17 11:36:45", + "description": "List of patches executed", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "patch", + "skipped", + "traceback" + ], + "fields": [ + { + "fieldname": "patch", + "fieldtype": "Code", + "label": "Patch", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "skipped", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Skipped", + "read_only": 1 + }, + { + "depends_on": "eval:doc.skipped == 1", + "fieldname": "traceback", + "fieldtype": "Code", + "label": "Traceback", + "read_only": 1 + } + ], + "icon": "fa fa-cog", + "idx": 1, + "links": [], + "modified": "2023-06-07 00:00:01.369265", + "modified_by": "Administrator", + "module": "Core", + "name": "Patch Log", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator" + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager" + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "patch", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/patch_log/patch_log.py b/xhiveframework/core/doctype/patch_log/patch_log.py new file mode 100644 index 0000000..10c25ff --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/patch_log.py @@ -0,0 +1,27 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class PatchLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + patch: DF.Code | None + skipped: DF.Check + traceback: DF.Code | None + # end: auto-generated types + pass + + +def before_migrate(): + xhiveframework.reload_doc("core", "doctype", "patch_log") diff --git a/xhiveframework/core/doctype/patch_log/test_patch_log.py b/xhiveframework/core/doctype/patch_log/test_patch_log.py new file mode 100644 index 0000000..c2f2636 --- /dev/null +++ b/xhiveframework/core/doctype/patch_log/test_patch_log.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Patch Log') + + +class TestPatchLog(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/permission_inspector/__init__.py b/xhiveframework/core/doctype/permission_inspector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/permission_inspector/permission_inspector.js b/xhiveframework/core/doctype/permission_inspector/permission_inspector.js new file mode 100644 index 0000000..18c02b3 --- /dev/null +++ b/xhiveframework/core/doctype/permission_inspector/permission_inspector.js @@ -0,0 +1,24 @@ +// Copyright (c) 2024, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +const call_debug = (frm) => { + frm.trigger("debug"); +}; + +xhiveframework.ui.form.on("Permission Inspector", { + refresh(frm) { + frm.disable_save(); + }, + docname: call_debug, + ref_doctype(frm) { + frm.doc.docname = ""; // Usually doctype change invalidates docname + call_debug(frm); + }, + user: call_debug, + permission_type: call_debug, + debug(frm) { + if (frm.doc.ref_doctype && frm.doc.user) { + frm.call("debug"); + } + }, +}); diff --git a/xhiveframework/core/doctype/permission_inspector/permission_inspector.json b/xhiveframework/core/doctype/permission_inspector/permission_inspector.json new file mode 100644 index 0000000..466f7e6 --- /dev/null +++ b/xhiveframework/core/doctype/permission_inspector/permission_inspector.json @@ -0,0 +1,90 @@ +{ + "actions": [], + "allow_rename": 1, + "beta": 1, + "creation": "2024-01-03 17:43:27.257317", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "column_break_mcqo", + "docname", + "column_break_xbrd", + "user", + "column_break_nvaa", + "permission_type", + "section_break_hkjp", + "output" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Document", + "options": "ref_doctype" + }, + { + "fieldname": "column_break_mcqo", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_xbrd", + "fieldtype": "Column Break" + }, + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "reqd": 1 + }, + { + "fieldname": "section_break_hkjp", + "fieldtype": "Section Break" + }, + { + "fieldname": "output", + "fieldtype": "Code", + "label": "Output", + "read_only": 1 + }, + { + "fieldname": "column_break_nvaa", + "fieldtype": "Column Break" + }, + { + "fieldname": "permission_type", + "fieldtype": "Select", + "label": "Permission Type", + "options": "read\nwrite\ncreate\ndelete\nsubmit\ncancel\nselect\namend\nprint\nemail\nreport\nimport\nexport\nshare" + } + ], + "index_web_pages_for_search": 1, + "is_virtual": 1, + "issingle": 1, + "links": [], + "modified": "2024-01-10 14:17:49.722593", + "modified_by": "Administrator", + "module": "Core", + "name": "Permission Inspector", + "owner": "Administrator", + "permissions": [ + { + "read": 1, + "role": "System Manager", + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/permission_inspector/permission_inspector.py b/xhiveframework/core/doctype/permission_inspector/permission_inspector.py new file mode 100644 index 0000000..0cc7546 --- /dev/null +++ b/xhiveframework/core/doctype/permission_inspector/permission_inspector.py @@ -0,0 +1,75 @@ +# Copyright (c) 2024, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.permissions import _pop_debug_log, has_permission + + +class PermissionInspector(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + docname: DF.DynamicLink | None + output: DF.Code | None + permission_type: DF.Literal[ + "read", + "write", + "create", + "delete", + "submit", + "cancel", + "select", + "amend", + "print", + "email", + "report", + "import", + "export", + "share", + ] + ref_doctype: DF.Link + user: DF.Link + # end: auto-generated types + + @xhiveframework.whitelist() + def debug(self): + if not (self.ref_doctype and self.user): + return + + result = has_permission( + self.ref_doctype, ptype=self.permission_type, doc=self.docname, user=self.user, debug=True + ) + + self.output = "\n==============================\n".join(_pop_debug_log()) + self.output += "\n\n" + f"Ouput of has_permission: {result}" + + # None of these apply, overriden for sanity. + def load_from_db(self): + super(Document, self).__init__({"modified": None, "permission_type": "read"}) + + def db_insert(self, *args, **kwargs): + ... + + def db_update(self): + ... + + @staticmethod + def get_list(args): + ... + + @staticmethod + def get_count(args): + ... + + @staticmethod + def get_stats(args): + ... + + def delete(self): + ... diff --git a/xhiveframework/core/doctype/permission_inspector/test_permission_inspector.py b/xhiveframework/core/doctype/permission_inspector/test_permission_inspector.py new file mode 100644 index 0000000..4db77c8 --- /dev/null +++ b/xhiveframework/core/doctype/permission_inspector/test_permission_inspector.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPermissionInspector(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/prepared_report/__init__.py b/xhiveframework/core/doctype/prepared_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/prepared_report/prepared_report.js b/xhiveframework/core/doctype/prepared_report/prepared_report.js new file mode 100644 index 0000000..8858151 --- /dev/null +++ b/xhiveframework/core/doctype/prepared_report/prepared_report.js @@ -0,0 +1,55 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Prepared Report", { + render_filter_values: function (frm, filters) { + var wrapper = $(frm.fields_dict["filter_values"].wrapper).empty(); + + let filter_table = $(` + + + + + + + +
    ${__("Filter")}${__("Value")}
    `); + + Object.keys(filters).forEach((key) => { + const filter_row = $(` + ${xhiveframework.model.unscrub(key)} + ${filters[key]} + `); + filter_table.find("tbody").append(filter_row); + }); + + wrapper.append(filter_table); + }, + + refresh: function (frm) { + frm.disable_save(); + + const filters = JSON.parse(frm.doc.filters); + if (!$.isEmptyObject(filters)) { + frm.toggle_display(["filter_values"], 1); + frm.events.render_filter_values(frm, filters); + } + + // always keep report_name hidden - we do this as we can't set mandatory and hidden + // property on a docfield at the same time + frm.toggle_display(["report_name"], 0); + + if (frm.doc.status == "Completed") { + frm.page.set_primary_action(__("Show Report"), () => { + xhiveframework.route_options = filters; + xhiveframework.set_route( + "query-report", + frm.doc.report_name, + xhiveframework.utils.make_query_string({ + prepared_report_name: frm.doc.name, + }) + ); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/prepared_report/prepared_report.json b/xhiveframework/core/doctype/prepared_report/prepared_report.json new file mode 100644 index 0000000..ec86237 --- /dev/null +++ b/xhiveframework/core/doctype/prepared_report/prepared_report.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2018-06-25 18:39:11.152960", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status", + "report_name", + "queued_by", + "job_id", + "column_break_4", + "queued_at", + "report_end_time", + "section_break_7", + "error_message", + "filters_sb", + "filters", + "filter_values" + ], + "fields": [ + { + "fieldname": "report_name", + "fieldtype": "Data", + "label": "Report Name", + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "default": "Queued", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Error\nQueued\nCompleted\nStarted", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "report_end_time", + "fieldtype": "Datetime", + "label": "Finished At", + "read_only": 1 + }, + { + "depends_on": "eval:doc.status == 'Error'", + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "error_message", + "fieldtype": "Text", + "label": "Error Message", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "filters_sb", + "fieldtype": "Section Break", + "label": "Filters" + }, + { + "fieldname": "filters", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Filters", + "read_only": 1 + }, + { + "fieldname": "filter_values", + "fieldtype": "HTML", + "hidden": 1, + "label": "Filter Values" + }, + { + "fieldname": "job_id", + "fieldtype": "Data", + "label": "Job ID", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "queued_by", + "fieldtype": "Data", + "is_virtual": 1, + "label": "Queued By", + "read_only": 1 + }, + { + "fieldname": "queued_at", + "fieldtype": "Datetime", + "is_virtual": 1, + "label": "Queued At", + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2024-03-07 12:01:58.209879", + "modified_by": "Administrator", + "module": "Core", + "name": "Prepared Report", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Prepared Report User", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [ + { + "color": "Blue", + "title": "Queued" + }, + { + "color": "Red", + "title": "Error" + }, + { + "color": "Green", + "title": "Completed" + } + ], + "title_field": "report_name" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/prepared_report/prepared_report.py b/xhiveframework/core/doctype/prepared_report/prepared_report.py new file mode 100644 index 0000000..046738f --- /dev/null +++ b/xhiveframework/core/doctype/prepared_report/prepared_report.py @@ -0,0 +1,284 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +import gzip +import json +from contextlib import suppress +from typing import Any + +from rq import get_current_job + +import xhiveframework +from xhiveframework.desk.form.load import get_attachments +from xhiveframework.desk.query_report import generate_report_result +from xhiveframework.model.document import Document +from xhiveframework.monitor import add_data_to_monitor +from xhiveframework.utils import add_to_date, now +from xhiveframework.utils.background_jobs import enqueue + +# If prepared report runs for longer than this time it's automatically considered as failed +FAILURE_THRESHOLD = 60 * 60 +REPORT_TIMEOUT = 25 * 60 + + +class PreparedReport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + error_message: DF.Text | None + filters: DF.SmallText | None + job_id: DF.Data | None + queued_at: DF.Datetime | None + queued_by: DF.Data | None + report_end_time: DF.Datetime | None + report_name: DF.Data + status: DF.Literal["Error", "Queued", "Completed", "Started"] + + # end: auto-generated types + @property + def queued_by(self): + return self.owner + + @property + def queued_at(self): + return self.creation + + @staticmethod + def clear_old_logs(days=30): + prepared_reports_to_delete = xhiveframework.get_all( + "Prepared Report", + filters={"modified": ["<", xhiveframework.utils.add_days(xhiveframework.utils.now(), -days)]}, + ) + + for batch in xhiveframework.utils.create_batch(prepared_reports_to_delete, 100): + enqueue(method=delete_prepared_reports, reports=batch) + + def before_insert(self): + self.status = "Queued" + + def on_trash(self): + """Remove pending job from queue, if already running then kill the job.""" + if self.status not in ("Started", "Queued"): + return + + with suppress(Exception): + job = xhiveframework.get_doc("RQ Job", self.job_id) + job.stop_job() if self.status == "Started" else job.delete() + + def after_insert(self): + enqueue( + generate_report, + queue="long", + prepared_report=self.name, + timeout=REPORT_TIMEOUT, + enqueue_after_commit=True, + ) + + def get_prepared_data(self, with_file_name=False): + if attachments := get_attachments(self.doctype, self.name): + attachment = attachments[0] + attached_file = xhiveframework.get_doc("File", attachment.name) + + if with_file_name: + return (gzip.decompress(attached_file.get_content()), attachment.file_name) + return gzip.decompress(attached_file.get_content()) + + +def generate_report(prepared_report): + update_job_id(prepared_report) + + instance: PreparedReport = xhiveframework.get_doc("Prepared Report", prepared_report) + report = xhiveframework.get_doc("Report", instance.report_name) + + add_data_to_monitor(report=instance.report_name) + + try: + report.custom_columns = [] + + if report.report_type == "Custom Report": + custom_report_doc = report + reference_report = custom_report_doc.reference_report + report = xhiveframework.get_doc("Report", reference_report) + if custom_report_doc.json: + data = json.loads(custom_report_doc.json) + if data: + report.custom_columns = data["columns"] + + result = generate_report_result(report=report, filters=instance.filters, user=instance.owner) + create_json_gz_file(result, instance.doctype, instance.name, instance.report_name) + + instance.status = "Completed" + except Exception: + instance.status = "Error" + instance.error_message = xhiveframework.get_traceback(with_context=True) + + instance.report_end_time = xhiveframework.utils.now() + instance.save(ignore_permissions=True) + + xhiveframework.publish_realtime( + "report_generated", + {"report_name": instance.report_name, "name": instance.name}, + user=xhiveframework.session.user, + ) + + +def update_job_id(prepared_report): + job = get_current_job() + + xhiveframework.db.set_value( + "Prepared Report", + prepared_report, + { + "job_id": job and job.id, + "status": "Started", + }, + ) + + xhiveframework.db.commit() + + +@xhiveframework.whitelist() +def make_prepared_report(report_name, filters=None): + """run reports in background""" + prepared_report = xhiveframework.get_doc( + { + "doctype": "Prepared Report", + "report_name": report_name, + "filters": process_filters_for_prepared_report(filters), + } + ).insert(ignore_permissions=True) + + return {"name": prepared_report.name} + + +def process_filters_for_prepared_report(filters: dict[str, Any] | str) -> str: + if isinstance(filters, str): + filters = json.loads(filters) + + # This looks like an insanity but, without this it'd be very hard to find Prepared Reports matching given condition + # We're ensuring that spacing is consistent. e.g. JS seems to put no spaces after ":", Python on the other hand does. + # We are also ensuring that order of keys is same so generated JSON string will be identical too. + # PS: xhiveframework.as_json sorts keys + return xhiveframework.as_json(filters, indent=None, separators=(",", ":")) + + +@xhiveframework.whitelist() +def get_reports_in_queued_state(report_name, filters): + return xhiveframework.get_all( + "Prepared Report", + filters={ + "report_name": report_name, + "filters": process_filters_for_prepared_report(filters), + "status": ("in", ("Queued", "Started")), + "owner": xhiveframework.session.user, + }, + ) + + +def get_completed_prepared_report(filters, user, report_name): + return xhiveframework.db.get_value( + "Prepared Report", + filters={ + "status": "Completed", + "filters": process_filters_for_prepared_report(filters), + "owner": user, + "report_name": report_name, + }, + ) + + +def expire_stalled_report(): + xhiveframework.db.set_value( + "Prepared Report", + { + "status": "Started", + "modified": ("<", add_to_date(now(), seconds=-FAILURE_THRESHOLD, as_datetime=True)), + }, + { + "status": "Failed", + "error_message": xhiveframework._("Report timed out."), + }, + update_modified=False, + ) + + +@xhiveframework.whitelist() +def delete_prepared_reports(reports): + reports = xhiveframework.parse_json(reports) + for report in reports: + prepared_report = xhiveframework.get_doc("Prepared Report", report["name"]) + if prepared_report.has_permission(): + prepared_report.delete(ignore_permissions=True, delete_permanently=True) + + +def create_json_gz_file(data, dt, dn, report_name): + # Storing data in CSV file causes information loss + # Reports like P&L Statement were completely unsuable because of this + json_filename = "{}_{}.json.gz".format( + xhiveframework.scrub(report_name), xhiveframework.utils.data.format_datetime(xhiveframework.utils.now(), "Y-m-d-H-M") + ) + encoded_content = xhiveframework.safe_encode(xhiveframework.as_json(data, indent=None, separators=(",", ":"))) + compressed_content = gzip.compress(encoded_content) + + # Call save() file function to upload and attach the file + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": json_filename, + "attached_to_doctype": dt, + "attached_to_name": dn, + "content": compressed_content, + "is_private": 1, + } + ) + _file.save(ignore_permissions=True) + + +@xhiveframework.whitelist() +def download_attachment(dn): + pr = xhiveframework.get_doc("Prepared Report", dn) + if not pr.has_permission("read"): + xhiveframework.throw(xhiveframework._("Cannot Download Report due to insufficient permissions")) + + data, file_name = pr.get_prepared_data(with_file_name=True) + xhiveframework.local.response.filename = file_name[:-3] + xhiveframework.local.response.filecontent = data + xhiveframework.local.response.type = "binary" + + +def get_permission_query_condition(user): + if not user: + user = xhiveframework.session.user + if user == "Administrator": + return None + + from xhiveframework.utils.user import UserPermissions + + user = UserPermissions(user) + + if "System Manager" in user.roles: + return None + + reports = [xhiveframework.db.escape(report) for report in user.get_all_reports().keys()] + + return """`tabPrepared Report`.report_name in ({reports})""".format(reports=",".join(reports)) + + +def has_permission(doc, user): + if not user: + user = xhiveframework.session.user + if user == "Administrator": + return True + + from xhiveframework.utils.user import UserPermissions + + user = UserPermissions(user) + + if "System Manager" in user.roles: + return True + + return doc.report_name in user.get_all_reports().keys() diff --git a/xhiveframework/core/doctype/prepared_report/prepared_report_list.js b/xhiveframework/core/doctype/prepared_report/prepared_report_list.js new file mode 100644 index 0000000..725f14d --- /dev/null +++ b/xhiveframework/core/doctype/prepared_report/prepared_report_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Prepared Report"] = { + onload: function (list_view) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(list_view.doctype); + }); + }, +}; diff --git a/xhiveframework/core/doctype/prepared_report/test_prepared_report.py b/xhiveframework/core/doctype/prepared_report/test_prepared_report.py new file mode 100644 index 0000000..9216716 --- /dev/null +++ b/xhiveframework/core/doctype/prepared_report/test_prepared_report.py @@ -0,0 +1,92 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import json +import time +from contextlib import contextmanager + +import xhiveframework +from xhiveframework.desk.query_report import generate_report_result, get_report_doc +from xhiveframework.query_builder.utils import db_type_is +from xhiveframework.tests.test_query_builder import run_only_if +from xhiveframework.tests.utils import XhiveFrameworkTestCase, timeout + + +class TestPreparedReport(XhiveFrameworkTestCase): + @classmethod + def tearDownClass(cls): + for r in xhiveframework.get_all("Prepared Report", pluck="name"): + xhiveframework.delete_doc("Prepared Report", r, force=True, delete_permanently=True) + + xhiveframework.db.commit() + + @timeout(seconds=20) + def wait_for_status(self, report, status): + xhiveframework.db.commit() # Flush changes first + while True: + xhiveframework.db.rollback() # read new data + report.reload() + if report.status == status: + break + time.sleep(0.5) + + def create_prepared_report(self, report=None, commit=True): + doc = xhiveframework.get_doc( + { + "doctype": "Prepared Report", + "report_name": report or "Database Storage Usage By Tables", + } + ).insert() + + if commit: + xhiveframework.db.commit() + + return doc + + def test_queueing(self): + doc = self.create_prepared_report() + self.assertEqual("Queued", doc.status) + self.assertTrue(doc.queued_at) + + self.wait_for_status(doc, "Completed") + + doc = xhiveframework.get_last_doc("Prepared Report") + self.assertTrue(doc.job_id) + self.assertTrue(doc.report_end_time) + + def test_prepared_data(self): + doc = self.create_prepared_report() + self.wait_for_status(doc, "Completed") + + prepared_data = json.loads(doc.get_prepared_data().decode("utf-8")) + generated_data = generate_report_result(get_report_doc("Database Storage Usage By Tables")) + self.assertEqual(len(prepared_data["columns"]), len(generated_data["columns"])) + self.assertEqual(len(prepared_data["result"]), len(generated_data["result"])) + self.assertEqual(len(prepared_data), len(generated_data)) + + @run_only_if(db_type_is.MARIADB) + def test_start_status_and_kill_jobs(self): + with test_report(report_type="Query Report", query="select sleep(10)") as report: + doc = self.create_prepared_report(report.name) + self.wait_for_status(doc, "Started") + job_id = doc.job_id + + doc.delete() + time.sleep(1) + job = xhiveframework.get_doc("RQ Job", job_id) + self.assertEqual(job.status, "stopped") + + +@contextmanager +def test_report(**args): + try: + report = xhiveframework.new_doc("Report") + report.update(args) + if not report.report_name: + report.report_name = xhiveframework.generate_hash() + if not report.ref_doctype: + report.ref_doctype = "ToDo" + report.insert() + xhiveframework.db.commit() + yield report + finally: + report.delete() diff --git a/xhiveframework/core/doctype/recorder/__init__.py b/xhiveframework/core/doctype/recorder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/recorder/recorder.js b/xhiveframework/core/doctype/recorder/recorder.js new file mode 100644 index 0000000..01a32f2 --- /dev/null +++ b/xhiveframework/core/doctype/recorder/recorder.js @@ -0,0 +1,84 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Recorder", { + onload: function (frm) { + frm.fields_dict.sql_queries.grid.only_sortable(); + }, + refresh: function (frm) { + frm.disable_save(); + frm._sort_order = {}; + frm.trigger("setup_sort"); + }, + + setup_sort: function (frm) { + const sortable_fields = ["index", "duration", "exact_copies", "normalized_copies"]; + sortable_fields.forEach((field) => { + const field_header = $(`.col[data-fieldname='${field}']`)[0]; + $(field_header).click(() => { + let sort_order = frm._sort_order[field] || -1; + let grid = frm.fields_dict.sql_queries.grid; + grid.data.sort((a, b) => sort_order * (a[field] - b[field])); + frm._sort_order[field] = -1 * sort_order; // reverse for next click + grid.refresh(); + frm.trigger("setup_sort"); // grid creates new elements again, resetup listeners. + }); + }); + }, +}); + +xhiveframework.ui.form.on("Recorder Query", "form_render", function (frm, cdt, cdn) { + let row = xhiveframework.get_doc(cdt, cdn); + let stack = JSON.parse(row.stack); + render_html_field(stack, "stack_html", __("Stack Trace")); + + let explain_result = JSON.parse(row.explain_result); + render_html_field(explain_result, "sql_explain_html", __("SQL Explain")); + + function render_html_field(parsed_json, fieldname, label) { + let html = + "
    "; + if (parsed_json.length == 0) { + html += ""; + } else { + html = create_html_table(parsed_json, html); + } + + let field_wrapper = + frm.fields_dict[row.parentfield].grid.grid_rows_by_docname[cdn].grid_form.fields_dict[ + fieldname + ].wrapper; + $(html).appendTo(field_wrapper); + } + + function create_html_table(table_content, html) { + html += ` + + `; + return html; + } +}); diff --git a/xhiveframework/core/doctype/recorder/recorder.json b/xhiveframework/core/doctype/recorder/recorder.json new file mode 100644 index 0000000..72291db --- /dev/null +++ b/xhiveframework/core/doctype/recorder/recorder.json @@ -0,0 +1,149 @@ +{ + "actions": [], + "creation": "2023-08-01 12:06:49.630877", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "path", + "number_of_queries", + "time_in_queries", + "method", + "column_break_qo53", + "cmd", + "time", + "duration", + "event_type", + "section_break_1skt", + "request_headers", + "section_break_sgro", + "form_dict", + "section_break_9jhm", + "sql_queries", + "section_break_optn", + "profile" + ], + "fields": [ + { + "fieldname": "path", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Path" + }, + { + "depends_on": "eval:doc.event_type==\"HTTP Request\"", + "fieldname": "cmd", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "CMD" + }, + { + "fieldname": "duration", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Duration" + }, + { + "fieldname": "time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Time" + }, + { + "fieldname": "number_of_queries", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Number of Queries" + }, + { + "fieldname": "time_in_queries", + "fieldtype": "Float", + "label": "Time in Queries" + }, + { + "fieldname": "column_break_qo53", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_1skt", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.event_type==\"HTTP Request\"", + "fieldname": "request_headers", + "fieldtype": "Code", + "label": "Request Headers" + }, + { + "fieldname": "section_break_sgro", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:doc.event_type==\"HTTP Request\"", + "fieldname": "form_dict", + "fieldtype": "Code", + "label": "Form Dict" + }, + { + "depends_on": "eval:doc.event_type==\"HTTP Request\"", + "fieldname": "method", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Method", + "options": "GET\nPOST\nPUT\nDELETE\nPATCH\nHEAD\nOPTIONS" + }, + { + "fieldname": "sql_queries", + "fieldtype": "Table", + "label": "SQL Queries", + "options": "Recorder Query" + }, + { + "fieldname": "section_break_9jhm", + "fieldtype": "Section Break" + }, + { + "fieldname": "event_type", + "fieldtype": "Data", + "hidden": 1, + "label": "Event Type" + }, + { + "fieldname": "section_break_optn", + "fieldtype": "Section Break" + }, + { + "fieldname": "profile", + "fieldtype": "Code", + "label": "cProfile Output", + "read_only": 1 + } + ], + "hide_toolbar": 1, + "in_create": 1, + "index_web_pages_for_search": 1, + "is_virtual": 1, + "links": [], + "modified": "2024-02-01 22:13:26.505174", + "modified_by": "Administrator", + "module": "Core", + "name": "Recorder", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1 + } + ], + "sort_field": "duration", + "sort_order": "DESC", + "states": [], + "title_field": "path" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/recorder/recorder.py b/xhiveframework/core/doctype/recorder/recorder.py new file mode 100644 index 0000000..7a11f28 --- /dev/null +++ b/xhiveframework/core/doctype/recorder/recorder.py @@ -0,0 +1,104 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.recorder import get as get_recorder_data +from xhiveframework.utils import cint, evaluate_filters, make_filter_dict + + +class Recorder(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.recorder_query.recorder_query import RecorderQuery + from xhiveframework.types import DF + + cmd: DF.Data | None + duration: DF.Float + event_type: DF.Data | None + form_dict: DF.Code | None + method: DF.Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] + number_of_queries: DF.Int + path: DF.Data | None + profile: DF.Code | None + request_headers: DF.Code | None + sql_queries: DF.Table[RecorderQuery] + time: DF.Datetime | None + time_in_queries: DF.Float + # end: auto-generated types + + def load_from_db(self): + request_data = get_recorder_data(self.name) + if not request_data: + raise xhiveframework.DoesNotExistError + request = serialize_request(request_data) + super(Document, self).__init__(request) + + @staticmethod + def get_list(args): + start = cint(args.get("start")) or 0 + page_length = cint(args.get("page_length")) or 20 + requests = Recorder.get_filtered_requests(args)[start : start + page_length] + + if order_by_statment := args.get("order_by"): + if "." in order_by_statment: + order_by_statment = order_by_statment.split(".")[1] + + if " " in order_by_statment: + sort_key, sort_order = order_by_statment.split(" ", 1) + else: + sort_key = order_by_statment + sort_order = "desc" + + sort_key = sort_key.replace("`", "") + return sorted(requests, key=lambda r: r.get(sort_key) or 0, reverse=bool(sort_order == "desc")) + + return sorted(requests, key=lambda r: r.duration, reverse=1) + + @staticmethod + def get_count(args): + return len(Recorder.get_filtered_requests(args)) + + @staticmethod + def get_filtered_requests(args): + filters = args.get("filters") + requests = [serialize_request(request) for request in get_recorder_data()] + return [req for req in requests if evaluate_filters(req, filters)] + + @staticmethod + def get_stats(args): + pass + + @staticmethod + def delete(self): + pass + + def db_insert(self, *args, **kwargs): + pass + + def db_update(self): + pass + + +def serialize_request(request): + request = xhiveframework._dict(request) + if request.get("calls"): + for i in request.calls: + i["stack"] = xhiveframework.as_json(i["stack"]) + i["explain_result"] = xhiveframework.as_json(i["explain_result"]) + request.update( + name=request.get("uuid"), + number_of_queries=request.get("queries"), + time_in_queries=request.get("time_queries"), + request_headers=xhiveframework.as_json(request.get("headers"), indent=4), + form_dict=xhiveframework.as_json(request.get("form_dict"), indent=4), + sql_queries=request.get("calls"), + modified=request.get("time"), + creation=request.get("time"), + ) + + return request diff --git a/xhiveframework/core/doctype/recorder/recorder_list.js b/xhiveframework/core/doctype/recorder/recorder_list.js new file mode 100644 index 0000000..132af0c --- /dev/null +++ b/xhiveframework/core/doctype/recorder/recorder_list.js @@ -0,0 +1,212 @@ +xhiveframework.listview_settings["Recorder"] = { + hide_name_column: true, + + onload(listview) { + listview.page.sidebar.remove(); + if (!has_common(xhiveframework.user_roles, ["Administrator", "System Manager"])) return; + + if (listview.list_view_settings) { + listview.list_view_settings.disable_comment_count = true; + } + + listview.page.add_button(__("Clear"), () => { + xhiveframework.xcall("xhiveframework.recorder.delete").then(listview.refresh); + }); + + listview.page.add_menu_item(__("Import"), () => { + new xhiveframework.ui.FileUploader({ + folder: this.current_folder, + on_success: (file) => { + if (cur_list.data.length > 0) { + // don't replace existing capture + return; + } + xhiveframework.call({ + method: "xhiveframework.recorder.import_data", + args: { + file: file.file_url, + }, + callback: function () { + listview.refresh(); + }, + }); + }, + }); + }); + + listview.page.add_menu_item(__("Export"), () => { + xhiveframework.call({ + method: "xhiveframework.recorder.export_data", + callback: function (r) { + const data = r.message; + const filename = `${data[0]["uuid"]}..${data[data.length - 1]["uuid"]}.json`; + + const el = document.createElement("a"); + el.setAttribute( + "href", + "data:application/json," + encodeURIComponent(JSON.stringify(data)) + ); + el.setAttribute("download", filename); + el.click(); + }, + }); + }); + + setInterval(() => { + if (listview.list_view_settings.disable_auto_refresh) { + return; + } + if (!listview.enabled) return; + + const route = xhiveframework.get_route() || []; + if (route[0] != "List" || "Recorder" != route[1]) { + return; + } + + listview.refresh(); + }, 5000); + }, + + refresh(listview) { + this.fetch_recorder_status(listview).then(() => this.refresh_controls(listview)); + }, + + refresh_controls(listview) { + this.setup_recorder_controls(listview); + this.update_indicators(listview); + }, + + fetch_recorder_status(listview) { + return xhiveframework.xcall("xhiveframework.recorder.status").then((status) => { + listview.enabled = Boolean(status); + }); + }, + + setup_recorder_controls(listview) { + let me = this; + listview.page.set_primary_action(listview.enabled ? __("Stop") : __("Start"), () => { + if (listview.enabled) { + me.stop_recorder(listview); + } else { + me.start_recorder(listview); + } + }); + }, + + stop_recorder(listview) { + let me = this; + xhiveframework.xcall("xhiveframework.recorder.stop", {}).then(() => { + listview.refresh(); + listview.enabled = false; + me.refresh_controls(listview); + }); + }, + + start_recorder(listview) { + let me = this; + xhiveframework.prompt( + [ + { + fieldtype: "Section Break", + fieldname: "req_job_section", + }, + { + fieldtype: "Column Break", + fieldname: "web_request_columns", + label: "Web Requests", + }, + { + fieldname: "record_requests", + fieldtype: "Check", + label: "Record Web Requests", + default: 1, + }, + { + fieldname: "request_filter", + fieldtype: "Data", + label: "Request path filter", + default: "/", + depends_on: "record_requests", + description: `This will be used for filtering paths which will be recorded. + You can use this to avoid slowing down other traffic. + e.g. /api/method/xhiveerp. Leave it empty to record every request.`, + }, + { + fieldtype: "Column Break", + fieldname: "background_col", + label: "Background Jobs", + }, + + { + fieldname: "record_jobs", + fieldtype: "Check", + label: "Record Background Jobs", + default: 1, + }, + { + fieldname: "jobs_filter", + fieldtype: "Data", + label: "Background Jobs filter", + default: "", + depends_on: "record_jobs", + description: `This will be used for filtering jobs which will be recorded. + You can use this to avoid slowing down other jobs. e.g. email_queue.pull. + Leave it empty to record every job.`, + }, + { + fieldtype: "Section Break", + fieldname: "sql_section", + label: "SQL", + }, + { + fieldname: "record_sql", + fieldtype: "Check", + label: "Record SQL queries", + default: 1, + }, + { + fieldname: "explain", + fieldtype: "Check", + label: "Generate EXPLAIN for SQL queries", + default: 1, + }, + { + fieldname: "capture_stack", + fieldtype: "Check", + label: "Capture callstack of SQL queries", + default: 1, + }, + { + fieldtype: "Section Break", + fieldname: "python_section", + label: "Python", + }, + { + fieldname: "profile", + fieldtype: "Check", + label: "Run cProfile", + default: 0, + description: + "Warning: cProfile adds a lot of overhead. For best results, disable stack capturing when using cProfile.", + }, + ], + (values) => { + xhiveframework.xcall("xhiveframework.recorder.start", values).then(() => { + listview.refresh(); + listview.enabled = true; + me.refresh_controls(listview); + }); + }, + __("Configure Recorder"), + __("Start Recording") + ); + }, + + update_indicators(listview) { + if (listview.enabled) { + listview.page.set_indicator(__("Active"), "green"); + } else { + listview.page.set_indicator(__("Inactive"), "red"); + } + }, +}; diff --git a/xhiveframework/core/doctype/recorder/test_recorder.py b/xhiveframework/core/doctype/recorder/test_recorder.py new file mode 100644 index 0000000..e7b08de --- /dev/null +++ b/xhiveframework/core/doctype/recorder/test_recorder.py @@ -0,0 +1,77 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +import re + +import xhiveframework +import xhiveframework.recorder +from xhiveframework.core.doctype.recorder.recorder import serialize_request +from xhiveframework.recorder import get as get_recorder_data +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import set_request + + +class TestRecorder(XhiveFrameworkTestCase): + def setUp(self): + self.start_recoder() + + def tearDown(self) -> None: + xhiveframework.recorder.stop() + + def start_recoder(self): + xhiveframework.recorder.stop() + xhiveframework.recorder.delete() + set_request(path="/api/method/ping") + xhiveframework.recorder.start() + xhiveframework.recorder.record() + + def stop_recorder(self): + xhiveframework.recorder.dump() + + def test_recorder_list(self): + xhiveframework.get_all("User") # trigger one query + self.stop_recorder() + requests = xhiveframework.get_all("Recorder") + self.assertGreaterEqual(len(requests), 1) + request = xhiveframework.get_doc("Recorder", requests[0].name) + self.assertGreaterEqual(len(request.sql_queries), 1) + queries = [sql_query.query for sql_query in request.sql_queries] + match_flag = 0 + for query in queries: + if bool(re.match("^[select.*from `tabUser`]", query, flags=re.IGNORECASE)): + match_flag = 1 + break + self.assertEqual(match_flag, 1) + + def test_recorder_list_filters(self): + user = xhiveframework.qb.DocType("User") + xhiveframework.qb.from_(user).select("name").run() + self.stop_recorder() + + set_request(path="/api/method/abc") + xhiveframework.recorder.start() + xhiveframework.recorder.record() + xhiveframework.get_all("User") + self.stop_recorder() + + requests = xhiveframework.get_list( + "Recorder", filters={"path": ("like", "/api/method/ping"), "number_of_queries": 1} + ) + self.assertGreaterEqual(len(requests), 1) + requests = xhiveframework.get_list("Recorder", filters={"path": ("like", "/api/method/test")}) + self.assertEqual(len(requests), 0) + + requests = xhiveframework.get_list("Recorder", filters={"method": "GET"}) + self.assertGreaterEqual(len(requests), 1) + requests = xhiveframework.get_list("Recorder", filters={"method": "POST"}) + self.assertEqual(len(requests), 0) + + requests = xhiveframework.get_list("Recorder", order_by="path desc") + self.assertEqual(requests[0].path, "/api/method/ping") + + def test_recorder_serialization(self): + xhiveframework.get_all("User") # trigger one query + self.stop_recorder() + requests = xhiveframework.get_all("Recorder") + request_doc = get_recorder_data(requests[0].name) + self.assertIsInstance(serialize_request(request_doc), dict) diff --git a/xhiveframework/core/doctype/recorder_query/__init__.py b/xhiveframework/core/doctype/recorder_query/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/recorder_query/recorder_query.js b/xhiveframework/core/doctype/recorder_query/recorder_query.js new file mode 100644 index 0000000..fd70277 --- /dev/null +++ b/xhiveframework/core/doctype/recorder_query/recorder_query.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +// xhiveframework.ui.form.on("Recorder Query", { +// refresh(frm) { + +// }, +// }); diff --git a/xhiveframework/core/doctype/recorder_query/recorder_query.json b/xhiveframework/core/doctype/recorder_query/recorder_query.json new file mode 100644 index 0000000..4a529ad --- /dev/null +++ b/xhiveframework/core/doctype/recorder_query/recorder_query.json @@ -0,0 +1,106 @@ +{ + "actions": [], + "creation": "2023-08-01 17:04:12.173774", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "index", + "query", + "duration", + "column_break_qmju", + "exact_copies", + "normalized_query", + "normalized_copies", + "section_break_dygy", + "stack_html", + "stack", + "section_break_kvkb", + "sql_explain_html", + "explain_result" + ], + "fields": [ + { + "fieldname": "query", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Query", + "length": 2 + }, + { + "fieldname": "normalized_query", + "fieldtype": "Data", + "label": "Normalized Query" + }, + { + "fieldname": "duration", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Duration" + }, + { + "fieldname": "exact_copies", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Exact Copies" + }, + { + "fieldname": "normalized_copies", + "fieldtype": "Int", + "label": "Normalized Copies" + }, + { + "fieldname": "column_break_qmju", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_dygy", + "fieldtype": "Section Break" + }, + { + "fieldname": "stack", + "fieldtype": "Text", + "hidden": 1, + "print_hide": 1 + }, + { + "fieldname": "stack_html", + "fieldtype": "HTML", + "label": "Stack Trace" + }, + { + "fieldname": "section_break_kvkb", + "fieldtype": "Section Break" + }, + { + "fieldname": "explain_result", + "fieldtype": "Text", + "hidden": 1, + "print_hide": 1 + }, + { + "fieldname": "sql_explain_html", + "fieldtype": "HTML", + "label": "SQL Explain" + }, + { + "fieldname": "index", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Index" + } + ], + "index_web_pages_for_search": 1, + "is_virtual": 1, + "istable": 1, + "links": [], + "modified": "2023-08-07 13:12:23.496002", + "modified_by": "Administrator", + "module": "Core", + "name": "Recorder Query", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/recorder_query/recorder_query.py b/xhiveframework/core/doctype/recorder_query/recorder_query.py new file mode 100644 index 0000000..4523d73 --- /dev/null +++ b/xhiveframework/core/doctype/recorder_query/recorder_query.py @@ -0,0 +1,53 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class RecorderQuery(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + duration: DF.Float + exact_copies: DF.Int + explain_result: DF.Text | None + index: DF.Int + normalized_copies: DF.Int + normalized_query: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + query: DF.Data + stack: DF.Text | None + # end: auto-generated types + pass + + def db_insert(self, *args, **kwargs): + pass + + def load_from_db(self): + pass + + def db_update(self): + pass + + @staticmethod + def get_list(args): + pass + + @staticmethod + def get_count(args): + pass + + @staticmethod + def get_stats(args): + pass + + def delete(self): + pass diff --git a/xhiveframework/core/doctype/recorder_query/test_recorder_query.py b/xhiveframework/core/doctype/recorder_query/test_recorder_query.py new file mode 100644 index 0000000..f5a8fd9 --- /dev/null +++ b/xhiveframework/core/doctype/recorder_query/test_recorder_query.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestRecorderQuery(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/report/README.md b/xhiveframework/core/doctype/report/README.md new file mode 100644 index 0000000..99702e7 --- /dev/null +++ b/xhiveframework/core/doctype/report/README.md @@ -0,0 +1 @@ +Standard / Custom report. Reports can be of types Query Report or Script Report. \ No newline at end of file diff --git a/xhiveframework/core/doctype/report/__init__.py b/xhiveframework/core/doctype/report/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/report/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/report/boilerplate/controller.js b/xhiveframework/core/doctype/report/boilerplate/controller.js new file mode 100644 index 0000000..af24c8d --- /dev/null +++ b/xhiveframework/core/doctype/report/boilerplate/controller.js @@ -0,0 +1,8 @@ +// Copyright (c) {year}, {app_publisher} and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["{name}"] = {{ + "filters": [ + + ] +}}; diff --git a/xhiveframework/core/doctype/report/boilerplate/controller.py b/xhiveframework/core/doctype/report/boilerplate/controller.py new file mode 100644 index 0000000..4c087c1 --- /dev/null +++ b/xhiveframework/core/doctype/report/boilerplate/controller.py @@ -0,0 +1,9 @@ +# Copyright (c) {year}, {app_publisher} and contributors +# For license information, please see license.txt + +# import xhiveframework + + +def execute(filters=None): + columns, data = [], [] + return columns, data diff --git a/xhiveframework/core/doctype/report/report.js b/xhiveframework/core/doctype/report/report.js new file mode 100644 index 0000000..6cecdf6 --- /dev/null +++ b/xhiveframework/core/doctype/report/report.js @@ -0,0 +1,68 @@ +xhiveframework.ui.form.on("Report", { + refresh: function (frm) { + if (frm.doc.is_standard === "Yes" && !xhiveframework.boot.developer_mode) { + // make the document read-only + frm.disable_form(); + } else { + frm.enable_save(); + } + + let doc = frm.doc; + if (!doc.__islocal) { + frm.add_custom_button( + __("Show Report"), + function () { + switch (doc.report_type) { + case "Report Builder": + xhiveframework.set_route("List", doc.ref_doctype, "Report", doc.name); + break; + case "Query Report": + xhiveframework.set_route("query-report", doc.name); + break; + case "Script Report": + xhiveframework.set_route("query-report", doc.name); + break; + case "Custom Report": + xhiveframework.set_route("query-report", doc.name); + break; + } + }, + "fa fa-table" + ); + } + + if (doc.is_standard === "Yes" && frm.perm[0].write) { + frm.add_custom_button( + doc.disabled ? __("Enable Report") : __("Disable Report"), + function () { + frm.call("toggle_disable", { + disable: doc.disabled ? 0 : 1, + }).then(() => { + frm.reload_doc(); + }); + }, + doc.disabled ? "fa fa-check" : "fa fa-off" + ); + } + + frm.set_query("ref_doctype", () => { + return { + filters: { + istable: 0, + }, + }; + }); + }, + + ref_doctype: function (frm) { + if (frm.doc.ref_doctype) { + frm.trigger("set_doctype_roles"); + } + }, + + set_doctype_roles: function (frm) { + return frm.call("set_doctype_roles").then(() => { + frm.refresh_field("roles"); + }); + }, +}); diff --git a/xhiveframework/core/doctype/report/report.json b/xhiveframework/core/doctype/report/report.json new file mode 100644 index 0000000..0c64c88 --- /dev/null +++ b/xhiveframework/core/doctype/report/report.json @@ -0,0 +1,242 @@ +{ + "actions": [], + "autoname": "field:report_name", + "creation": "2013-03-09 15:45:57", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "report_name", + "ref_doctype", + "reference_report", + "is_standard", + "module", + "column_break_4", + "report_type", + "letter_head", + "add_total_row", + "disabled", + "prepared_report", + "filters_section", + "filters", + "columns_section", + "columns", + "section_break_6", + "query", + "report_script", + "client_code_section", + "javascript", + "json", + "permission_rules", + "roles" + ], + "fields": [ + { + "fieldname": "report_name", + "fieldtype": "Data", + "label": "Report Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Ref DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "reference_report", + "fieldtype": "Data", + "label": "Reference Report" + }, + { + "fieldname": "is_standard", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Is Standard", + "options": "No\nYes", + "reqd": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def" + }, + { + "default": "0", + "fieldname": "add_total_row", + "fieldtype": "Check", + "label": "Add Total Row" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "report_type", + "fieldtype": "Select", + "label": "Report Type", + "options": "Report Builder\nQuery Report\nScript Report\nCustom Report", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "depends_on": "eval: doc.is_standard == \"No\"", + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Query / Script" + }, + { + "depends_on": "eval:doc.report_type==\"Query Report\"", + "fieldname": "query", + "fieldtype": "Code", + "label": "Query" + }, + { + "depends_on": "eval:doc.report_type==\"Script Report\" && doc.is_standard===\"No\"", + "description": "JavaScript Format: xhiveframework.query_reports['REPORTNAME'] = {}", + "fieldname": "javascript", + "fieldtype": "Code", + "label": "Javascript" + }, + { + "depends_on": "eval:doc.report_type==\"Report Builder\" || \"Custom Report\"", + "fieldname": "json", + "fieldtype": "Code", + "label": "JSON", + "read_only": 1 + }, + { + "fieldname": "permission_rules", + "fieldtype": "Section Break" + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "label": "Roles", + "options": "Has Role" + }, + { + "default": "0", + "fieldname": "prepared_report", + "fieldtype": "Check", + "label": "Prepared Report" + }, + { + "depends_on": "eval:(doc.report_type===\"Script Report\" \n|| doc.report_type==\"Query Report\") \n&& doc.is_standard===\"No\"", + "description": "Filters will be accessible via filters.

    Send output as result = [result], or for old style data = [columns], [result]", + "fieldname": "report_script", + "fieldtype": "Code", + "label": "Script" + }, + { + "collapsible": 1, + "collapsible_depends_on": "filters", + "depends_on": "eval:doc.report_type != \"Custom Report\"", + "fieldname": "filters_section", + "fieldtype": "Section Break", + "label": "Filters" + }, + { + "depends_on": "eval:doc.report_type != \"Custom Report\"", + "fieldname": "filters", + "fieldtype": "Table", + "label": "Filters", + "options": "Report Filter" + }, + { + "collapsible": 1, + "collapsible_depends_on": "columns", + "depends_on": "eval:doc.report_type != \"Custom Report\"", + "fieldname": "columns_section", + "fieldtype": "Section Break", + "label": "Columns" + }, + { + "depends_on": "eval:doc.report_type != \"Custom Report\"", + "fieldname": "columns", + "fieldtype": "Table", + "label": "Columns", + "options": "Report Column" + }, + { + "collapsible": 1, + "collapsible_depends_on": "javascript", + "fieldname": "client_code_section", + "fieldtype": "Section Break", + "label": "Client Code" + } + ], + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-04-07 18:18:11.782178", + "modified_by": "Administrator", + "module": "Core", + "name": "Report", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Report Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User" + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/report/report.py b/xhiveframework/core/doctype/report/report.py new file mode 100644 index 0000000..074e592 --- /dev/null +++ b/xhiveframework/core/doctype/report/report.py @@ -0,0 +1,418 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import datetime +import json + +import xhiveframework +import xhiveframework.desk.query_report +from xhiveframework import _, scrub +from xhiveframework.core.doctype.custom_role.custom_role import get_custom_allowed_roles +from xhiveframework.core.doctype.page.page import delete_custom_role +from xhiveframework.desk.reportview import append_totals_row +from xhiveframework.model.document import Document +from xhiveframework.modules import make_boilerplate +from xhiveframework.modules.export_file import export_to_files +from xhiveframework.utils import cint, cstr +from xhiveframework.utils.safe_exec import check_safe_sql_query, safe_exec + + +class Report(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.core.doctype.report_column.report_column import ReportColumn + from xhiveframework.core.doctype.report_filter.report_filter import ReportFilter + from xhiveframework.types import DF + + add_total_row: DF.Check + columns: DF.Table[ReportColumn] + disabled: DF.Check + filters: DF.Table[ReportFilter] + is_standard: DF.Literal["No", "Yes"] + javascript: DF.Code | None + json: DF.Code | None + letter_head: DF.Link | None + module: DF.Link | None + prepared_report: DF.Check + query: DF.Code | None + ref_doctype: DF.Link + reference_report: DF.Data | None + report_name: DF.Data + report_script: DF.Code | None + report_type: DF.Literal["Report Builder", "Query Report", "Script Report", "Custom Report"] + roles: DF.Table[HasRole] + + # end: auto-generated types + def validate(self): + """only administrator can save standard report""" + if not self.module: + self.module = xhiveframework.db.get_value("DocType", self.ref_doctype, "module") + + if not self.is_standard: + self.is_standard = "No" + if ( + xhiveframework.session.user == "Administrator" + and getattr(xhiveframework.local.conf, "developer_mode", 0) == 1 + ): + self.is_standard = "Yes" + + if self.is_standard == "No": + # allow only script manager to edit scripts + if self.report_type != "Report Builder": + xhiveframework.only_for("Script Manager", True) + + if xhiveframework.db.get_value("Report", self.name, "is_standard") == "Yes": + xhiveframework.throw(_("Cannot edit a standard report. Please duplicate and create a new report")) + + if self.is_standard == "Yes" and xhiveframework.session.user != "Administrator": + xhiveframework.throw(_("Only Administrator can save a standard report. Please rename and save.")) + + if self.report_type == "Report Builder": + self.update_report_json() + + def before_insert(self): + self.set_doctype_roles() + + def on_update(self): + self.export_doc() + + def before_export(self, doc): + doc.letterhead = None + doc.prepared_report = 0 + + def on_trash(self): + if ( + self.is_standard == "Yes" + and not cint(getattr(xhiveframework.local.conf, "developer_mode", 0)) + and not (xhiveframework.flags.in_migrate or xhiveframework.flags.in_patch) + ): + xhiveframework.throw(_("You are not allowed to delete Standard Report")) + delete_custom_role("report", self.name) + + def get_columns(self): + return [d.as_dict(no_default_fields=True, no_child_table_fields=True) for d in self.columns] + + @xhiveframework.whitelist() + def set_doctype_roles(self): + if not self.get("roles") and self.is_standard == "No": + meta = xhiveframework.get_meta(self.ref_doctype) + if not meta.istable: + roles = [{"role": d.role} for d in meta.permissions if d.permlevel == 0] + self.set("roles", roles) + + def is_permitted(self): + """Returns true if Has Role is not set or the user is allowed.""" + from xhiveframework.utils import has_common + + allowed = [d.role for d in xhiveframework.get_all("Has Role", fields=["role"], filters={"parent": self.name})] + + custom_roles = get_custom_allowed_roles("report", self.name) + + if custom_roles: + allowed = custom_roles + + if not allowed: + return True + + if has_common(xhiveframework.get_roles(), allowed): + return True + + def update_report_json(self): + if not self.json: + self.json = "{}" + + def export_doc(self): + if xhiveframework.flags.in_import: + return + + if self.is_standard == "Yes" and (xhiveframework.local.conf.get("developer_mode") or 0) == 1: + export_to_files(record_list=[["Report", self.name]], record_module=self.module, create_init=True) + + self.create_report_py() + + def create_report_py(self): + if self.report_type == "Script Report": + make_boilerplate("controller.py", self, {"name": self.name}) + make_boilerplate("controller.js", self, {"name": self.name}) + + def execute_query_report(self, filters): + if not self.query: + xhiveframework.throw(_("Must specify a Query to run"), title=_("Report Document Error")) + + check_safe_sql_query(self.query) + + result = [list(t) for t in xhiveframework.db.sql(self.query, filters)] + columns = self.get_columns() or [cstr(c[0]) for c in xhiveframework.db.get_description()] + + return [columns, result] + + def execute_script_report(self, filters): + # save the timestamp to automatically set to prepared + threshold = 15 + res = [] + + start_time = datetime.datetime.now() + + # The JOB + if self.is_standard == "Yes": + res = self.execute_module(filters) + else: + res = self.execute_script(filters) + + # automatically set as prepared + execution_time = (datetime.datetime.now() - start_time).total_seconds() + if execution_time > threshold and not self.prepared_report: + xhiveframework.enqueue(enable_prepared_report, report=self.name) + + xhiveframework.cache.hset("report_execution_time", self.name, execution_time) + + return res + + def execute_module(self, filters): + # report in python module + module = self.module or xhiveframework.db.get_value("DocType", self.ref_doctype, "module") + method_name = get_report_module_dotted_path(module, self.name) + ".execute" + return xhiveframework.get_attr(method_name)(xhiveframework._dict(filters)) + + def execute_script(self, filters): + # server script + loc = {"filters": xhiveframework._dict(filters), "data": None, "result": None} + safe_exec(self.report_script, None, loc, script_filename=f"Report {self.name}") + if loc["data"]: + return loc["data"] + else: + return self.get_columns(), loc["result"] + + def get_data( + self, + filters=None, + limit=None, + user=None, + as_dict=False, + ignore_prepared_report=False, + are_default_filters=True, + ): + if self.report_type in ("Query Report", "Script Report", "Custom Report"): + columns, result = self.run_query_report( + filters, user, ignore_prepared_report, are_default_filters + ) + else: + columns, result = self.run_standard_report(filters, limit, user) + + if as_dict: + result = self.build_data_dict(result, columns) + + return columns, result + + def run_query_report( + self, filters=None, user=None, ignore_prepared_report=False, are_default_filters=True + ): + columns, result = [], [] + data = xhiveframework.desk.query_report.run( + self.name, + filters=filters, + user=user, + ignore_prepared_report=ignore_prepared_report, + are_default_filters=are_default_filters, + ) + + for d in data.get("columns"): + if isinstance(d, dict): + col = xhiveframework._dict(d) + if not col.fieldname: + col.fieldname = col.label + columns.append(col) + else: + fieldtype, options = "Data", None + parts = d.split(":") + if len(parts) > 1: + if parts[1]: + fieldtype, options = parts[1], None + if fieldtype and "/" in fieldtype: + fieldtype, options = fieldtype.split("/") + + columns.append( + xhiveframework._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0], options=options) + ) + + result += data.get("result") + + return columns, result + + def run_standard_report(self, filters, limit, user): + params = json.loads(self.json) + columns = self.get_standard_report_columns(params) + result = [] + order_by, group_by, group_by_args = self.get_standard_report_order_by(params) + + _result = xhiveframework.get_list( + self.ref_doctype, + fields=[ + get_group_by_field(group_by_args, c[1]) + if c[0] == "_aggregate_column" and group_by_args + else Report._format([c[1], c[0]]) + for c in columns + ], + filters=self.get_standard_report_filters(params, filters), + order_by=order_by, + group_by=group_by, + as_list=True, + limit=limit, + user=user, + ) + + columns = self.build_standard_report_columns(columns, group_by_args) + + result = result + [list(d) for d in _result] + + if params.get("add_totals_row"): + result = append_totals_row(result) + + return columns, result + + @staticmethod + def _format(parts): + # sort by is saved as DocType.fieldname, covert it to sql + return "`tab{}`.`{}`".format(*parts) + + def get_standard_report_columns(self, params): + if params.get("fields"): + columns = params.get("fields") + elif params.get("columns"): + columns = params.get("columns") + elif params.get("fields"): + columns = params.get("fields") + else: + columns = [["name", self.ref_doctype]] + columns.extend( + [df.fieldname, self.ref_doctype] + for df in xhiveframework.get_meta(self.ref_doctype).fields + if df.in_list_view + ) + return columns + + def get_standard_report_filters(self, params, filters): + _filters = params.get("filters") or [] + + if filters: + for key, value in filters.items(): + condition, _value = "=", value + if isinstance(value, list | tuple): + condition, _value = value + _filters.append([key, condition, _value]) + + return _filters + + def get_standard_report_order_by(self, params): + group_by_args = None + if params.get("sort_by"): + order_by = Report._format(params.get("sort_by").split(".")) + " " + params.get("sort_order") + + elif params.get("order_by"): + order_by = params.get("order_by") + else: + order_by = Report._format([self.ref_doctype, "modified"]) + " desc" + + if params.get("sort_by_next"): + order_by += ( + ", " + + Report._format(params.get("sort_by_next").split(".")) + + " " + + params.get("sort_order_next") + ) + + group_by = None + if params.get("group_by"): + group_by_args = xhiveframework._dict(params["group_by"]) + group_by = group_by_args["group_by"] + order_by = "_aggregate_column desc" + + return order_by, group_by, group_by_args + + def build_standard_report_columns(self, columns, group_by_args): + _columns = [] + + for fieldname, doctype in columns: + meta = xhiveframework.get_meta(doctype) + + if meta.get_field(fieldname): + field = meta.get_field(fieldname) + else: + if fieldname == "_aggregate_column": + label = get_group_by_column_label(group_by_args, meta) + else: + label = meta.get_label(fieldname) + + field = xhiveframework._dict(fieldname=fieldname, label=label) + + # since name is the primary key for a document, it will always be a Link datatype + if fieldname == "name": + field.fieldtype = "Link" + field.options = doctype + + _columns.append(field) + return _columns + + def build_data_dict(self, result, columns): + data = [] + for row in result: + if isinstance(row, list | tuple): + _row = xhiveframework._dict() + for i, val in enumerate(row): + _row[columns[i].get("fieldname")] = val + elif isinstance(row, dict): + # no need to convert from dict to dict + _row = xhiveframework._dict(row) + data.append(_row) + + return data + + @xhiveframework.whitelist() + def toggle_disable(self, disable: bool): + if not self.has_permission("write"): + xhiveframework.throw(_("You are not allowed to edit the report.")) + + self.db_set("disabled", cint(disable)) + + +def is_prepared_report_enabled(report): + return cint(xhiveframework.db.get_value("Report", report, "prepared_report")) or 0 + + +def get_report_module_dotted_path(module, report_name): + return ( + xhiveframework.local.module_app[scrub(module)] + + "." + + scrub(module) + + ".report." + + scrub(report_name) + + "." + + scrub(report_name) + ) + + +def get_group_by_field(args, doctype): + if args["aggregate_function"] == "count": + group_by_field = "count(*) as _aggregate_column" + else: + group_by_field = f"{args.aggregate_function}({args.aggregate_on}) as _aggregate_column" + + return group_by_field + + +def get_group_by_column_label(args, meta): + if args["aggregate_function"] == "count": + label = "Count" + else: + sql_fn_map = {"avg": "Average", "sum": "Sum"} + aggregate_on_label = meta.get_label(args.aggregate_on) + label = _("{0} of {1}").format(_(sql_fn_map[args.aggregate_function]), _(aggregate_on_label)) + return label + + +def enable_prepared_report(report: str): + xhiveframework.db.set_value("Report", report, "prepared_report", 1) diff --git a/xhiveframework/core/doctype/report/test_records.json b/xhiveframework/core/doctype/report/test_records.json new file mode 100644 index 0000000..cee1a20 --- /dev/null +++ b/xhiveframework/core/doctype/report/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doctype": "Report", + "name": "_Test Report 1", + "report_name": "_Test Report 1", + "report_type": "Query Report", + "is_standard": "No", + "ref_doctype": "Event" + } +] diff --git a/xhiveframework/core/doctype/report/test_report.py b/xhiveframework/core/doctype/report/test_report.py new file mode 100644 index 0000000..826ef64 --- /dev/null +++ b/xhiveframework/core/doctype/report/test_report.py @@ -0,0 +1,433 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import os +import textwrap + +import xhiveframework +from xhiveframework.core.doctype.user_permission.test_user_permission import create_user +from xhiveframework.custom.doctype.customize_form.customize_form import reset_customization +from xhiveframework.desk.query_report import add_total_row, run, save_report +from xhiveframework.desk.reportview import delete_report +from xhiveframework.desk.reportview import save_report as _save_report +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Report") +test_dependencies = ["User"] + + +class TestReport(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls) -> None: + cls.enable_safe_exec() + return super().setUpClass() + + def test_report_builder(self): + if xhiveframework.db.exists("Report", "User Activity Report"): + xhiveframework.delete_doc("Report", "User Activity Report") + + with open(os.path.join(os.path.dirname(__file__), "user_activity_report.json")) as f: + xhiveframework.get_doc(json.loads(f.read())).insert() + + report = xhiveframework.get_doc("Report", "User Activity Report") + columns, data = report.get_data() + self.assertEqual(columns[0].get("label"), "ID") + self.assertEqual(columns[1].get("label"), "User Type") + self.assertTrue("Administrator" in [d[0] for d in data]) + + def test_query_report(self): + report = xhiveframework.get_doc("Report", "Permitted Documents For User") + columns, data = report.get_data(filters={"user": "Administrator", "doctype": "DocType"}) + self.assertEqual(columns[0].get("label"), "Name") + self.assertEqual(columns[1].get("label"), "Module") + self.assertTrue("User" in [d.get("name") for d in data]) + + def test_save_or_delete_report(self): + """Test for validations when editing / deleting report of type Report Builder""" + + try: + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": "Test Delete Report", + "report_type": "Report Builder", + "is_standard": "No", + } + ).insert() + + # Check for PermissionError + create_user("test_report_owner@example.com", "Website Manager") + xhiveframework.set_user("test_report_owner@example.com") + self.assertRaises(xhiveframework.PermissionError, delete_report, report.name) + + # Check for Report Type + xhiveframework.set_user("Administrator") + report.db_set("report_type", "Custom Report") + self.assertRaisesRegex( + xhiveframework.ValidationError, + "Only reports of type Report Builder can be deleted", + delete_report, + report.name, + ) + + # Check if creating and deleting works with proper validations + xhiveframework.set_user("test@example.com") + report_name = _save_report( + "Dummy Report", + "User", + json.dumps( + [ + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "insert_after_index": 0, + "link_field": "name", + "doctype": "User", + "options": "Email", + "width": 100, + "id": "email", + "name": "Email", + } + ] + ), + ) + + doc = xhiveframework.get_doc("Report", report_name) + delete_report(doc.name) + + finally: + xhiveframework.set_user("Administrator") + xhiveframework.db.rollback() + + def test_custom_report(self): + reset_customization("User") + custom_report_name = save_report( + "Permitted Documents For User", + "Permitted Documents For User Custom", + json.dumps( + [ + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "insert_after_index": 0, + "link_field": "name", + "doctype": "User", + "options": "Email", + "width": 100, + "id": "email", + "name": "Email", + } + ] + ), + json.dumps({"user": "Administrator", "doctype": "User"}), + ) + custom_report = xhiveframework.get_doc("Report", custom_report_name) + columns, result = custom_report.run_query_report(user=xhiveframework.session.user) + + self.assertListEqual(["email"], [column.get("fieldname") for column in columns]) + admin_dict = xhiveframework.core.utils.find(result, lambda d: d["name"] == "Administrator") + self.assertDictEqual( + { + "name": "Administrator", + "user_type": "System User", + "email": "admin@example.com", + }, + admin_dict, + ) + + def test_report_with_custom_column(self): + reset_customization("User") + response = run( + "Permitted Documents For User", + filters={"user": "Administrator", "doctype": "User"}, + custom_columns=[ + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "insert_after_index": 0, + "link_field": "name", + "doctype": "User", + "options": "Email", + "width": 100, + "id": "email", + "name": "Email", + } + ], + ) + result = response.get("result") + columns = response.get("columns") + self.assertListEqual( + ["name", "email", "user_type"], + [column.get("fieldname") for column in columns], + ) + admin_dict = xhiveframework.core.utils.find(result, lambda d: d["name"] == "Administrator") + self.assertDictEqual( + { + "name": "Administrator", + "user_type": "System User", + "email": "admin@example.com", + }, + admin_dict, + ) + + def test_report_permissions(self): + xhiveframework.set_user("test@example.com") + xhiveframework.db.delete("Has Role", {"parent": xhiveframework.session.user, "role": "Test Has Role"}) + xhiveframework.db.commit() + if not xhiveframework.db.exists("Role", "Test Has Role"): + xhiveframework.get_doc({"doctype": "Role", "role_name": "Test Has Role"}).insert(ignore_permissions=True) + + if not xhiveframework.db.exists("Report", "Test Report"): + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": "Test Report", + "report_type": "Query Report", + "is_standard": "No", + "roles": [{"role": "Test Has Role"}], + } + ).insert(ignore_permissions=True) + else: + report = xhiveframework.get_doc("Report", "Test Report") + + self.assertNotEqual(report.is_permitted(), True) + xhiveframework.set_user("Administrator") + + def test_report_custom_permissions(self): + xhiveframework.set_user("test@example.com") + xhiveframework.db.delete("Custom Role", {"report": "Test Custom Role Report"}) + xhiveframework.db.commit() # nosemgrep + if not xhiveframework.db.exists("Report", "Test Custom Role Report"): + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": "Test Custom Role Report", + "report_type": "Query Report", + "is_standard": "No", + "roles": [{"role": "_Test Role"}, {"role": "System Manager"}], + } + ).insert(ignore_permissions=True) + else: + report = xhiveframework.get_doc("Report", "Test Custom Role Report") + + self.assertEqual(report.is_permitted(), True) + + xhiveframework.get_doc( + { + "doctype": "Custom Role", + "report": "Test Custom Role Report", + "roles": [{"role": "_Test Role 2"}], + "ref_doctype": "User", + } + ).insert(ignore_permissions=True) + + self.assertNotEqual(report.is_permitted(), True) + xhiveframework.set_user("Administrator") + + # test for the `_format` method if report data doesn't have sort_by parameter + def test_format_method(self): + if xhiveframework.db.exists("Report", "User Activity Report Without Sort"): + xhiveframework.delete_doc("Report", "User Activity Report Without Sort") + with open(os.path.join(os.path.dirname(__file__), "user_activity_report_without_sort.json")) as f: + xhiveframework.get_doc(json.loads(f.read())).insert() + + report = xhiveframework.get_doc("Report", "User Activity Report Without Sort") + columns, data = report.get_data() + + self.assertEqual(columns[0].get("label"), "ID") + self.assertEqual(columns[1].get("label"), "User Type") + self.assertTrue("Administrator" in [d[0] for d in data]) + xhiveframework.delete_doc("Report", "User Activity Report Without Sort") + + def test_non_standard_script_report(self): + report_name = "Test Non Standard Script Report" + if not xhiveframework.db.exists("Report", report_name): + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": report_name, + "report_type": "Script Report", + "is_standard": "No", + } + ).insert(ignore_permissions=True) + else: + report = xhiveframework.get_doc("Report", report_name) + + report.report_script = """ +totals = {} +for user in xhiveframework.get_all('User', fields = ['name', 'user_type', 'creation']): + if not user.user_type in totals: + totals[user.user_type] = 0 + totals[user.user_type] = totals[user.user_type] + 1 + +data = [ + [ + {'fieldname': 'type', 'label': 'Type'}, + {'fieldname': 'value', 'label': 'Value'} + ], + [ + {"type":key, "value": value} for key, value in totals.items() + ] +] +""" + report.save() + data = report.get_data() + + # check columns + self.assertEqual(data[0][0]["label"], "Type") + + # check values + self.assertTrue("System User" in [d.get("type") for d in data[1]]) + + def test_script_report_with_columns(self): + report_name = "Test Script Report With Columns" + + if xhiveframework.db.exists("Report", report_name): + xhiveframework.delete_doc("Report", report_name) + + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": report_name, + "report_type": "Script Report", + "is_standard": "No", + "columns": [ + dict(fieldname="type", label="Type", fieldtype="Data"), + dict(fieldname="value", label="Value", fieldtype="Int"), + ], + } + ).insert(ignore_permissions=True) + + report.report_script = """ +totals = {} +for user in xhiveframework.get_all('User', fields = ['name', 'user_type', 'creation']): + if not user.user_type in totals: + totals[user.user_type] = 0 + totals[user.user_type] = totals[user.user_type] + 1 + +result = [ + {"type":key, "value": value} for key, value in totals.items() + ] +""" + + report.save() + data = report.get_data() + + # check columns + self.assertEqual(data[0][0]["label"], "Type") + + # check values + self.assertTrue("System User" in [d.get("type") for d in data[1]]) + + def test_toggle_disabled(self): + """Make sure that authorization is respected.""" + # Assuming that there will be reports in the system. + reports = xhiveframework.get_all(doctype="Report", limit=1) + report_name = reports[0]["name"] + doc = xhiveframework.get_doc("Report", report_name) + status = doc.disabled + + # User has write permission on reports and should pass through + xhiveframework.set_user("test@example.com") + doc.toggle_disable(not status) + doc.reload() + self.assertNotEqual(status, doc.disabled) + + # User has no write permission on reports, permission error is expected. + xhiveframework.set_user("test1@example.com") + doc = xhiveframework.get_doc("Report", report_name) + with self.assertRaises(xhiveframework.exceptions.ValidationError): + doc.toggle_disable(1) + + # Set user back to administrator + xhiveframework.set_user("Administrator") + + def test_add_total_row_for_tree_reports(self): + report_settings = {"tree": True, "parent_field": "parent_value"} + + columns = [ + { + "fieldname": "parent_column", + "label": "Parent Column", + "fieldtype": "Data", + "width": 10, + }, + { + "fieldname": "column_1", + "label": "Column 1", + "fieldtype": "Float", + "width": 10, + }, + { + "fieldname": "column_2", + "label": "Column 2", + "fieldtype": "Float", + "width": 10, + }, + ] + + result = [ + {"parent_column": "Parent 1", "column_1": 200, "column_2": 150.50}, + { + "parent_column": "Child 1", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1", + }, + { + "parent_column": "Child 2", + "column_1": 100, + "column_2": 75.25, + "parent_value": "Parent 1", + }, + ] + + result = add_total_row( + result, + columns, + meta=None, + is_tree=report_settings["tree"], + parent_field=report_settings["parent_field"], + ) + self.assertEqual(result[-1][0], "Total") + self.assertEqual(result[-1][1], 200) + self.assertEqual(result[-1][2], 150.50) + + def test_cte_in_query_report(self): + cte_query = textwrap.dedent( + """ + with enabled_users as ( + select name + from `tabUser` + where enabled = 1 + ) + select * from enabled_users; + """ + ) + + report = xhiveframework.get_doc( + { + "doctype": "Report", + "ref_doctype": "User", + "report_name": "Enabled Users List", + "report_type": "Query Report", + "is_standard": "No", + "query": cte_query, + } + ).insert() + + if xhiveframework.db.db_type == "mariadb": + col, rows = report.execute_query_report(filters={}) + self.assertEqual(col[0], "name") + self.assertGreaterEqual(len(rows), 1) + elif xhiveframework.db.db_type == "postgres": + self.assertRaises(xhiveframework.PermissionError, report.execute_query_report, filters={}) diff --git a/xhiveframework/core/doctype/report/user_activity_report.json b/xhiveframework/core/doctype/report/user_activity_report.json new file mode 100644 index 0000000..5339bbe --- /dev/null +++ b/xhiveframework/core/doctype/report/user_activity_report.json @@ -0,0 +1,17 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "is_standard": "No", + "javascript": null, + "json": "{\"filters\":[],\"columns\":[[\"name\",\"User\"],[\"user_type\",\"User\"],[\"first_name\",\"User\"],[\"last_name\",\"User\"],[\"last_active\",\"User\"],[\"role\",\"Has Role\"]],\"sort_by\":\"User.modified\",\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}", + "modified": "2016-09-01 02:59:07.728890", + "module": "Core", + "name": "User Activity Report", + "query": null, + "ref_doctype": "User", + "report_name": "User Activity Report", + "report_type": "Report Builder" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/report/user_activity_report_without_sort.json b/xhiveframework/core/doctype/report/user_activity_report_without_sort.json new file mode 100644 index 0000000..bb520a2 --- /dev/null +++ b/xhiveframework/core/doctype/report/user_activity_report_without_sort.json @@ -0,0 +1,17 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "is_standard": "No", + "javascript": null, + "json": "{\"filters\":[],\"columns\":[[\"name\",\"User\"],[\"user_type\",\"User\"],[\"first_name\",\"User\"],[\"last_name\",\"User\"],[\"last_active\",\"User\"],[\"role\",\"Has Role\"]],\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}", + "modified": "2018-12-17 18:27:07.728890", + "module": "Core", + "name": "User Activity Report Without Sort", + "query": null, + "ref_doctype": "User", + "report_name": "User Activity Report Without Sort", + "report_type": "Report Builder" + } \ No newline at end of file diff --git a/xhiveframework/core/doctype/report_column/__init__.py b/xhiveframework/core/doctype/report_column/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/report_column/report_column.json b/xhiveframework/core/doctype/report_column/report_column.json new file mode 100644 index 0000000..2e6a22d --- /dev/null +++ b/xhiveframework/core/doctype/report_column/report_column.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "creation": "2020-01-14 11:28:37.583656", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "fieldname", + "label", + "fieldtype", + "options", + "width" + ], + "fields": [ + { + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fieldname", + "reqd": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Fieldtype", + "options": "Check\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nInt\nLink\nSelect\nTime", + "reqd": 1 + }, + { + "fieldname": "options", + "fieldtype": "Data", + "label": "Options" + }, + { + "fieldname": "width", + "fieldtype": "Int", + "label": "Width" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-03 10:52:03.895817", + "modified_by": "Administrator", + "module": "Core", + "name": "Report Column", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/report_column/report_column.py b/xhiveframework/core/doctype/report_column/report_column.py new file mode 100644 index 0000000..79d78ad --- /dev/null +++ b/xhiveframework/core/doctype/report_column/report_column.py @@ -0,0 +1,40 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class ReportColumn(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + fieldname: DF.Data + fieldtype: DF.Literal[ + "Check", + "Currency", + "Data", + "Date", + "Datetime", + "Duration", + "Dynamic Link", + "Float", + "Fold", + "Int", + "Link", + "Select", + "Time", + ] + label: DF.Data + options: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + width: DF.Int + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/report_filter/__init__.py b/xhiveframework/core/doctype/report_filter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/report_filter/report_filter.json b/xhiveframework/core/doctype/report_filter/report_filter.json new file mode 100644 index 0000000..f0800a0 --- /dev/null +++ b/xhiveframework/core/doctype/report_filter/report_filter.json @@ -0,0 +1,90 @@ +{ + "actions": [], + "creation": "2020-01-14 11:38:58.016498", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label", + "fieldtype", + "fieldname", + "mandatory", + "wildcard_filter", + "column_break_urrx", + "options", + "default" + ], + "fields": [ + { + "columns": 2, + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fieldname", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "columns": 2, + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Fieldtype", + "options": "Check\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nInt\nLink\nSelect\nTime", + "reqd": 1 + }, + { + "columns": 1, + "default": "0", + "fieldname": "mandatory", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Mandatory" + }, + { + "columns": 2, + "fieldname": "options", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Options" + }, + { + "default": "0", + "description": "Will add \"%\" before and after the query", + "fieldname": "wildcard_filter", + "fieldtype": "Check", + "label": "Wildcard Filter" + }, + { + "fieldname": "default", + "fieldtype": "Small Text", + "label": "Default" + }, + { + "columns": 2, + "fieldname": "column_break_urrx", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-06-15 11:37:35.961036", + "modified_by": "Administrator", + "module": "Core", + "name": "Report Filter", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/report_filter/report_filter.py b/xhiveframework/core/doctype/report_filter/report_filter.py new file mode 100644 index 0000000..3053607 --- /dev/null +++ b/xhiveframework/core/doctype/report_filter/report_filter.py @@ -0,0 +1,41 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class ReportFilter(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + default: DF.SmallText | None + fieldname: DF.Data + fieldtype: DF.Literal[ + "Check", + "Currency", + "Data", + "Date", + "Datetime", + "Dynamic Link", + "Float", + "Fold", + "Int", + "Link", + "Select", + "Time", + ] + label: DF.Data + mandatory: DF.Check + options: DF.SmallText | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + wildcard_filter: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/role/README.md b/xhiveframework/core/doctype/role/README.md new file mode 100644 index 0000000..fe8643e --- /dev/null +++ b/xhiveframework/core/doctype/role/README.md @@ -0,0 +1 @@ +Roles are be assigned to users and permissions on DocTypes are defined on Roles. Standard roles are "Administrator" (developer), "Guest" and "System Manager" (system, user, permission administrator) \ No newline at end of file diff --git a/xhiveframework/core/doctype/role/__init__.py b/xhiveframework/core/doctype/role/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/doctype/role/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/doctype/role/patches/v13_set_default_desk_properties.py b/xhiveframework/core/doctype/role/patches/v13_set_default_desk_properties.py new file mode 100644 index 0000000..66f8340 --- /dev/null +++ b/xhiveframework/core/doctype/role/patches/v13_set_default_desk_properties.py @@ -0,0 +1,11 @@ +import xhiveframework + +from ..role import desk_properties + + +def execute(): + for role in xhiveframework.get_all("Role", ["name", "desk_access"]): + role_doc = xhiveframework.get_doc("Role", role.name) + for key in desk_properties: + role_doc.set(key, role_doc.desk_access) + role_doc.save() diff --git a/xhiveframework/core/doctype/role/role.js b/xhiveframework/core/doctype/role/role.js new file mode 100644 index 0000000..5436428 --- /dev/null +++ b/xhiveframework/core/doctype/role/role.js @@ -0,0 +1,29 @@ +// Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See LICENSE + +xhiveframework.ui.form.on("Role", { + refresh: function (frm) { + if (frm.doc.name === "All") { + frm.dashboard.add_comment( + __("Role 'All' will be given to all system + website users."), + "yellow" + ); + } else if (frm.doc.name === "Desk User") { + frm.dashboard.add_comment( + __("Role 'Desk User' will be given to all system users."), + "yellow" + ); + } + + frm.set_df_property("is_custom", "read_only", xhiveframework.session.user !== "Administrator"); + + frm.add_custom_button("Role Permissions Manager", function () { + xhiveframework.route_options = { role: frm.doc.name }; + xhiveframework.set_route("permission-manager"); + }); + frm.add_custom_button("Show Users", function () { + xhiveframework.route_options = { role: frm.doc.name }; + xhiveframework.set_route("List", "User", "Report"); + }); + }, +}); diff --git a/xhiveframework/core/doctype/role/role.json b/xhiveframework/core/doctype/role/role.json new file mode 100644 index 0000000..c963bad --- /dev/null +++ b/xhiveframework/core/doctype/role/role.json @@ -0,0 +1,176 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:role_name", + "creation": "2013-01-08 15:50:01", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "role_name", + "home_page", + "restrict_to_domain", + "column_break_4", + "disabled", + "is_custom", + "desk_access", + "two_factor_auth", + "navigation_settings_section", + "search_bar", + "notifications", + "list_settings_section", + "list_sidebar", + "bulk_actions", + "view_switcher", + "form_settings_section", + "form_sidebar", + "timeline", + "dashboard" + ], + "fields": [ + { + "fieldname": "role_name", + "fieldtype": "Data", + "label": "Role Name", + "oldfieldname": "role_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "description": "If disabled, this role will be removed from all users.", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "default": "1", + "fieldname": "desk_access", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Desk Access" + }, + { + "default": "0", + "fieldname": "two_factor_auth", + "fieldtype": "Check", + "label": "Two Factor Authentication" + }, + { + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict To Domain", + "options": "Domain" + }, + { + "description": "Route: Example \"/app\"", + "fieldname": "home_page", + "fieldtype": "Data", + "label": "Home Page" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "navigation_settings_section", + "fieldtype": "Section Break", + "label": "Navigation Settings" + }, + { + "default": "1", + "fieldname": "search_bar", + "fieldtype": "Check", + "label": "Search Bar" + }, + { + "fieldname": "list_settings_section", + "fieldtype": "Section Break", + "label": "List Settings" + }, + { + "default": "1", + "fieldname": "list_sidebar", + "fieldtype": "Check", + "label": "Sidebar" + }, + { + "default": "1", + "fieldname": "bulk_actions", + "fieldtype": "Check", + "label": "Bulk Actions" + }, + { + "fieldname": "form_settings_section", + "fieldtype": "Section Break", + "label": "Form Settings" + }, + { + "default": "1", + "fieldname": "form_sidebar", + "fieldtype": "Check", + "label": "Sidebar" + }, + { + "default": "1", + "fieldname": "timeline", + "fieldtype": "Check", + "label": "Timeline" + }, + { + "default": "1", + "fieldname": "dashboard", + "fieldtype": "Check", + "label": "Dashboard" + }, + { + "default": "1", + "fieldname": "view_switcher", + "fieldtype": "Check", + "label": "View Switcher" + }, + { + "default": "1", + "fieldname": "notifications", + "fieldtype": "Check", + "label": "Notifications" + }, + { + "default": "0", + "fieldname": "is_custom", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Custom" + } + ], + "icon": "fa fa-bookmark", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-03-13 20:59:37.875253", + "modified_by": "Administrator", + "module": "Core", + "name": "Role", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/role/role.py b/xhiveframework/core/doctype/role/role.py new file mode 100644 index 0000000..794fb4f --- /dev/null +++ b/xhiveframework/core/doctype/role/role.py @@ -0,0 +1,153 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.website.path_resolver import validate_path + +desk_properties = ( + "search_bar", + "notifications", + "list_sidebar", + "bulk_actions", + "view_switcher", + "form_sidebar", + "timeline", + "dashboard", +) +from xhiveframework.website.router import clear_routing_cache + +STANDARD_ROLES = ("Administrator", "System Manager", "Script Manager", "All", "Guest") + + +class Role(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + bulk_actions: DF.Check + dashboard: DF.Check + desk_access: DF.Check + disabled: DF.Check + form_sidebar: DF.Check + home_page: DF.Data | None + is_custom: DF.Check + list_sidebar: DF.Check + notifications: DF.Check + restrict_to_domain: DF.Link | None + role_name: DF.Data + search_bar: DF.Check + timeline: DF.Check + two_factor_auth: DF.Check + view_switcher: DF.Check + + # end: auto-generated types + def before_rename(self, old, new, merge=False): + if old in STANDARD_ROLES: + xhiveframework.throw(xhiveframework._("Standard roles cannot be renamed")) + + def after_insert(self): + xhiveframework.cache.hdel("roles", "Administrator") + + def validate(self): + if self.disabled: + self.disable_role() + else: + self.set_desk_properties() + self.validate_homepage() + + def disable_role(self): + if self.name in STANDARD_ROLES: + xhiveframework.throw(xhiveframework._("Standard roles cannot be disabled")) + else: + self.remove_roles() + + def validate_homepage(self): + if xhiveframework.request and self.home_page: + validate_path(self.home_page) + + if self.has_value_changed("home_page"): + clear_routing_cache() + + def set_desk_properties(self): + # set if desk_access is not allowed, unset all desk properties + if self.name == "Guest": + self.desk_access = 0 + + if not self.desk_access: + for key in desk_properties: + self.set(key, 0) + + def remove_roles(self): + xhiveframework.db.delete("Has Role", {"role": self.name}) + xhiveframework.clear_cache() + + def on_update(self): + """update system user desk access if this has changed in this update""" + if xhiveframework.flags.in_install: + return + if self.has_value_changed("desk_access"): + self.update_user_type_on_change() + + def update_user_type_on_change(self): + """When desk access changes, all the users that have this role need to be re-evaluated""" + + users_with_role = get_users(self.name) + + # perf: Do not re-evaluate users who already have same desk access that this role permits. + role_user_type = "System User" if self.desk_access else "Website User" + users_with_same_user_type = xhiveframework.get_all("User", {"user_type": role_user_type}, pluck="name") + + for user_name in set(users_with_role) - set(users_with_same_user_type): + user = xhiveframework.get_doc("User", user_name) + user_type = user.user_type + user.set_system_user() + if user_type != user.user_type: + user.save() + + +def get_info_based_on_role(role, field="email", ignore_permissions=False): + """Get information of all users that have been assigned this role""" + users = xhiveframework.get_list( + "Has Role", + filters={"role": role, "parenttype": "User"}, + parent_doctype="User", + fields=["parent as user_name"], + ignore_permissions=ignore_permissions, + ) + + return get_user_info(users, field) + + +def get_user_info(users, field="email"): + """Fetch details about users for the specified field""" + info_list = [] + for user in users: + user_info, enabled = xhiveframework.db.get_value("User", user.get("user_name"), [field, "enabled"]) + if enabled and user_info not in ["admin@example.com", "guest@example.com"]: + info_list.append(user_info) + return info_list + + +def get_users(role): + return [ + d.parent + for d in xhiveframework.get_all("Has Role", filters={"role": role, "parenttype": "User"}, fields=["parent"]) + ] + + +# searches for active employees +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def role_query(doctype, txt, searchfield, start, page_len, filters): + report_filters = [["Role", "name", "like", f"%{txt}%"], ["Role", "is_custom", "=", 0]] + if filters and isinstance(filters, list): + report_filters.extend(filters) + + return xhiveframework.get_all( + "Role", limit_start=start, limit_page_length=page_len, filters=report_filters, as_list=1 + ) diff --git a/xhiveframework/core/doctype/role/test_records.json b/xhiveframework/core/doctype/role/test_records.json new file mode 100644 index 0000000..49442e6 --- /dev/null +++ b/xhiveframework/core/doctype/role/test_records.json @@ -0,0 +1,22 @@ +[ + { + "doctype": "Role", + "role_name": "_Test Role", + "desk_access": 1 + }, + { + "doctype": "Role", + "role_name": "_Test Role 2", + "desk_access": 1 + }, + { + "doctype": "Role", + "role_name": "_Test Role 3", + "desk_access": 1 + }, + { + "doctype": "Role", + "role_name": "_Test Role 4", + "desk_access": 0 + } +] \ No newline at end of file diff --git a/xhiveframework/core/doctype/role/test_role.py b/xhiveframework/core/doctype/role/test_role.py new file mode 100644 index 0000000..611fcc8 --- /dev/null +++ b/xhiveframework/core/doctype/role/test_role.py @@ -0,0 +1,53 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.core.doctype.role.role import get_info_based_on_role +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Role") + + +class TestUser(XhiveFrameworkTestCase): + def test_disable_role(self): + xhiveframework.get_doc("User", "test@example.com").add_roles("_Test Role 3") + + role = xhiveframework.get_doc("Role", "_Test Role 3") + role.disabled = 1 + role.save() + + self.assertTrue("_Test Role 3" not in xhiveframework.get_roles("test@example.com")) + + role = xhiveframework.get_doc("Role", "_Test Role 3") + role.disabled = 0 + role.save() + + xhiveframework.get_doc("User", "test@example.com").add_roles("_Test Role 3") + self.assertTrue("_Test Role 3" in xhiveframework.get_roles("test@example.com")) + + def test_change_desk_access(self): + """if we change desk acecss from role, remove from user""" + xhiveframework.delete_doc_if_exists("User", "test-user-for-desk-access@example.com") + xhiveframework.delete_doc_if_exists("Role", "desk-access-test") + user = xhiveframework.get_doc( + dict(doctype="User", email="test-user-for-desk-access@example.com", first_name="test") + ).insert() + role = xhiveframework.get_doc(dict(doctype="Role", role_name="desk-access-test", desk_access=0)).insert() + user.add_roles(role.name) + user.save() + self.assertTrue(user.user_type == "Website User") + role.desk_access = 1 + role.save() + user.reload() + self.assertTrue(user.user_type == "System User") + role.desk_access = 0 + role.save() + user.reload() + self.assertTrue(user.user_type == "Website User") + + def test_get_users_by_role(self): + role = "System Manager" + sys_managers = get_info_based_on_role(role, field="name") + + for user in sys_managers: + self.assertIn(role, xhiveframework.get_roles(user)) diff --git a/xhiveframework/core/doctype/role_permission_for_page_and_report/__init__.py b/xhiveframework/core/doctype/role_permission_for_page_and_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.js b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.js new file mode 100644 index 0000000..895ae34 --- /dev/null +++ b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.js @@ -0,0 +1,127 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Role Permission for Page and Report", { + setup: function (frm) { + frm.trigger("set_queries"); + }, + + refresh: function (frm) { + frm.disable_save(); + frm.role_area.hide(); + frm.events.setup_buttons(frm); + }, + + setup_buttons: function (frm) { + frm.clear_custom_buttons(); + frm.page.clear_actions(); + if (frm.doc.set_role_for && frm.doc[xhiveframework.model.scrub(frm.doc.set_role_for)]) { + frm.add_custom_button(__("Reset to defaults"), function () { + frm.trigger("reset_roles"); + }); + + frm.page.set_primary_action(__("Update"), () => { + frm.trigger("update_report_page_data"); + }); + } + }, + + onload: function (frm) { + if (!frm.roles_editor) { + frm.role_area = $(frm.fields_dict.roles_html.wrapper); + frm.roles_editor = new xhiveframework.RoleEditor(frm.role_area, frm); + } + }, + + set_queries: function (frm) { + frm.set_query("page", function () { + return { + filters: { + system_page: 0, + }, + }; + }); + }, + + set_role_for: function (frm) { + frm.trigger("clear_fields"); + frm.toggle_display("roles_html", false); + }, + + clear_fields: function (frm) { + var field = frm.doc.set_role_for == "Report" ? "page" : "report"; + frm.set_value(field, ""); + }, + + page: function (frm) { + frm.events.setup_buttons(frm); + if (frm.doc.page) { + frm.trigger("set_report_page_data"); + } else { + frm.trigger("set_role_for"); + } + }, + + report: function (frm) { + frm.events.setup_buttons(frm); + if (frm.doc.report) { + frm.trigger("set_report_page_data"); + } else { + frm.trigger("set_role_for"); + } + }, + + set_report_page_data: function (frm) { + frm.toggle_display("roles_html", true); + frm.role_area.show(); + + return frm.call({ + method: "set_report_page_data", + doc: frm.doc, + callback: function (r) { + refresh_field("roles"); + frm.roles_editor.show(); + }, + }); + }, + + update_report_page_data: function (frm) { + frm.trigger("validate_mandatory_fields"); + if (frm.roles_editor) { + frm.roles_editor.set_roles_in_table(); + } + + return frm.call({ + method: "update_report_page_data", + doc: frm.doc, + callback: function (r) { + refresh_field("roles"); + frm.roles_editor.show(); + xhiveframework.msgprint(__("Successfully Updated")); + }, + }); + }, + + reset_roles: function (frm) { + frm.trigger("validate_mandatory_fields"); + return frm.call({ + method: "reset_roles", + doc: frm.doc, + callback: function (r) { + refresh_field("roles"); + frm.roles_editor.show(); + xhiveframework.msgprint(__("Successfully Updated")); + }, + }); + }, + + validate_mandatory_fields: function (frm) { + if (!frm.doc.set_role_for) { + xhiveframework.throw(__("Mandatory field: set role for")); + } + + if (frm.doc.set_role_for && !frm.doc[frm.doc.set_role_for.toLocaleLowerCase()]) { + xhiveframework.throw(__("Mandatory field: {0}", [frm.doc.set_role_for])); + } + }, +}); diff --git a/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.json b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.json new file mode 100644 index 0000000..52ecc5d --- /dev/null +++ b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.json @@ -0,0 +1,95 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2017-02-13 17:33:25.157332", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "set_role_for", + "page", + "report", + "column_break_4", + "enable_prepared_report", + "roles_permission", + "roles_html", + "roles" + ], + "fields": [ + { + "fieldname": "set_role_for", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Set Role For", + "options": "\nPage\nReport", + "reqd": 1 + }, + { + "depends_on": "eval:doc.set_role_for == 'Page'", + "fieldname": "page", + "fieldtype": "Link", + "label": "Page", + "options": "Page" + }, + { + "depends_on": "eval:doc.set_role_for == 'Report'", + "fieldname": "report", + "fieldtype": "Link", + "label": "Report", + "options": "Report" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "roles_permission", + "fieldtype": "Section Break", + "label": "Allow Roles" + }, + { + "fieldname": "roles_html", + "fieldtype": "HTML", + "label": "Roles Html" + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "hidden": 1, + "label": "Roles", + "options": "Has Role", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "report", + "fieldname": "enable_prepared_report", + "fieldtype": "Check", + "label": "Enable Prepared Report" + } + ], + "hide_toolbar": 1, + "issingle": 1, + "links": [], + "modified": "2022-11-23 12:39:05.750386", + "modified_by": "Administrator", + "module": "Core", + "name": "Role Permission for Page and Report", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py new file mode 100644 index 0000000..840dbe6 --- /dev/null +++ b/xhiveframework/core/doctype/role_permission_for_page_and_report/role_permission_for_page_and_report.py @@ -0,0 +1,107 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.core.doctype.report.report import is_prepared_report_enabled +from xhiveframework.model.document import Document +from xhiveframework.permissions import ALL_USER_ROLE +from xhiveframework.utils import cint + + +class RolePermissionforPageandReport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.types import DF + + enable_prepared_report: DF.Check + page: DF.Link | None + report: DF.Link | None + roles: DF.Table[HasRole] + set_role_for: DF.Literal["", "Page", "Report"] + + # end: auto-generated types + @xhiveframework.whitelist() + def set_report_page_data(self): + self.set_custom_roles() + self.check_prepared_report_disabled() + + def set_custom_roles(self): + args = self.get_args() + self.set("roles", []) + + name = xhiveframework.db.get_value("Custom Role", args, "name") + if name: + doc = xhiveframework.get_doc("Custom Role", name) + roles = doc.roles + else: + roles = self.get_standard_roles() + + self.set("roles", roles) + + def check_prepared_report_disabled(self): + if self.report: + self.enable_prepared_report = is_prepared_report_enabled(self.report) + + def get_standard_roles(self): + doctype = self.set_role_for + docname = self.page if self.set_role_for == "Page" else self.report + doc = xhiveframework.get_doc(doctype, docname) + return doc.roles + + @xhiveframework.whitelist() + def reset_roles(self): + roles = self.get_standard_roles() + self.set("roles", roles) + self.update_custom_roles() + self.update_disable_prepared_report() + + @xhiveframework.whitelist() + def update_report_page_data(self): + self.update_custom_roles() + self.update_disable_prepared_report() + + def update_custom_roles(self): + args = self.get_args() + name = xhiveframework.db.get_value("Custom Role", args, "name") + + args.update({"doctype": "Custom Role", "roles": self.get_roles()}) + + if self.report: + args.update({"ref_doctype": xhiveframework.db.get_value("Report", self.report, "ref_doctype")}) + + if name: + custom_role = xhiveframework.get_doc("Custom Role", name) + custom_role.set("roles", self.get_roles()) + custom_role.save() + else: + xhiveframework.get_doc(args).insert() + + def update_disable_prepared_report(self): + if self.report: + # intentionally written update query in xhiveframework.db.sql instead of xhiveframework.db.set_value + xhiveframework.db.sql( + """update `tabReport` set prepared_report = %s + where name = %s""", + (self.enable_prepared_report, self.report), + ) + + def get_args(self, row=None): + name = self.page if self.set_role_for == "Page" else self.report + check_for_field = self.set_role_for.replace(" ", "_").lower() + + return {check_for_field: name} + + def get_roles(self): + return [ + {"role": data.role, "parenttype": "Custom Role"} + for data in self.roles + if data.role != ALL_USER_ROLE + ] + + def update_status(self): + return xhiveframework.render_template diff --git a/xhiveframework/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py b/xhiveframework/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py new file mode 100644 index 0000000..c3ec850 --- /dev/null +++ b/xhiveframework/core/doctype/role_permission_for_page_and_report/test_role_permission_for_page_and_report.py @@ -0,0 +1,9 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestRolePermissionforPageandReport(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/role_profile/__init__.py b/xhiveframework/core/doctype/role_profile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/role_profile/role_profile.js b/xhiveframework/core/doctype/role_profile/role_profile.js new file mode 100644 index 0000000..0c21908 --- /dev/null +++ b/xhiveframework/core/doctype/role_profile/role_profile.js @@ -0,0 +1,20 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Role Profile", { + refresh: function (frm) { + if (has_common(xhiveframework.user_roles, ["Administrator", "System Manager"])) { + if (!frm.roles_editor) { + const role_area = $(frm.fields_dict.roles_html.wrapper); + frm.roles_editor = new xhiveframework.RoleEditor(role_area, frm); + } + frm.roles_editor.show(); + } + }, + + validate: function (frm) { + if (frm.roles_editor) { + frm.roles_editor.set_roles_in_table(); + } + }, +}); diff --git a/xhiveframework/core/doctype/role_profile/role_profile.json b/xhiveframework/core/doctype/role_profile/role_profile.json new file mode 100644 index 0000000..7cd60a1 --- /dev/null +++ b/xhiveframework/core/doctype/role_profile/role_profile.json @@ -0,0 +1,80 @@ +{ + "actions": [], + "autoname": "role_profile", + "creation": "2017-08-31 04:16:38.764465", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "role_profile", + "roles_html", + "roles" + ], + "fields": [ + { + "fieldname": "role_profile", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Role Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "roles_html", + "fieldtype": "HTML", + "label": "Roles HTML", + "read_only": 1 + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "hidden": 1, + "label": "Roles Assigned", + "options": "Has Role", + "permlevel": 1, + "print_hide": 1, + "read_only": 1 + } + ], + "links": [ + { + "link_doctype": "User", + "link_fieldname": "role_profile_name" + } + ], + "modified": "2021-12-03 15:45:45.270963", + "modified_by": "Administrator", + "module": "Core", + "name": "Role Profile", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "role_profile", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/role_profile/role_profile.py b/xhiveframework/core/doctype/role_profile/role_profile.py new file mode 100644 index 0000000..a6f4629 --- /dev/null +++ b/xhiveframework/core/doctype/role_profile/role_profile.py @@ -0,0 +1,57 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from collections import defaultdict + +import xhiveframework +from xhiveframework.model.document import Document + + +class RoleProfile(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.types import DF + + role_profile: DF.Data + roles: DF.Table[HasRole] + + # end: auto-generated types + def autoname(self): + """set name as Role Profile name""" + self.name = self.role_profile + + def on_update(self): + self.queue_action( + "update_all_users", + now=xhiveframework.flags.in_test or xhiveframework.flags.in_install, + enqueue_after_commit=True, + ) + + def update_all_users(self): + """Changes in role_profile reflected across all its user""" + has_role = xhiveframework.qb.DocType("Has Role") + user = xhiveframework.qb.DocType("User") + + all_current_roles = ( + xhiveframework.qb.from_(user) + .join(has_role) + .on(user.name == has_role.parent) + .where(user.role_profile_name == self.name) + .select(user.name, has_role.role) + ).run() + + user_roles = defaultdict(set) + for user, role in all_current_roles: + user_roles[user].add(role) + + role_profile_roles = {role.role for role in self.roles} + for user, roles in user_roles.items(): + if roles != role_profile_roles: + user = xhiveframework.get_doc("User", user) + user.roles = [] + user.add_roles(*role_profile_roles) diff --git a/xhiveframework/core/doctype/role_profile/test_role_profile.py b/xhiveframework/core/doctype/role_profile/test_role_profile.py new file mode 100644 index 0000000..8cfb85d --- /dev/null +++ b/xhiveframework/core/doctype/role_profile/test_role_profile.py @@ -0,0 +1,46 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["Role"] + + +class TestRoleProfile(XhiveFrameworkTestCase): + def test_make_new_role_profile(self): + xhiveframework.delete_doc_if_exists("Role Profile", "Test 1", force=1) + new_role_profile = xhiveframework.get_doc(dict(doctype="Role Profile", role_profile="Test 1")).insert() + + self.assertEqual(new_role_profile.role_profile, "Test 1") + + # add role + new_role_profile.append("roles", {"role": "_Test Role 2"}) + new_role_profile.save() + self.assertEqual(new_role_profile.roles[0].role, "_Test Role 2") + + # user with a role profile + random_user = xhiveframework.mock("email") + random_user_name = xhiveframework.mock("name") + + random_user = xhiveframework.get_doc( + { + "doctype": "User", + "email": random_user, + "enabled": 1, + "first_name": random_user_name, + "new_password": "Eastern_43A1W", + "role_profile_name": "Test 1", + } + ).insert(ignore_permissions=True, ignore_if_duplicate=True) + self.assertListEqual( + [role.role for role in random_user.roles], [role.role for role in new_role_profile.roles] + ) + + # clear roles + new_role_profile.roles = [] + new_role_profile.save() + self.assertEqual(new_role_profile.roles, []) + + # user roles with the role profile should also be updated + random_user.reload() + self.assertListEqual(random_user.roles, []) diff --git a/xhiveframework/core/doctype/rq_job/__init__.py b/xhiveframework/core/doctype/rq_job/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/rq_job/rq_job.js b/xhiveframework/core/doctype/rq_job/rq_job.js new file mode 100644 index 0000000..0e05964 --- /dev/null +++ b/xhiveframework/core/doctype/rq_job/rq_job.js @@ -0,0 +1,32 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("RQ Job", { + refresh: function (frm) { + // Nothing in this form is supposed to be editable. + frm.disable_form(); + frm.dashboard.set_headline_alert( + __("This is a virtual doctype and data is cleared periodically.") + ); + + if (["started", "queued"].includes(frm.doc.status)) { + frm.add_custom_button(__("Force Stop job"), () => { + xhiveframework.confirm( + __( + "This will terminate the job immediately and might be dangerous, are you sure? " + ), + () => { + xhiveframework + .xcall("xhiveframework.core.doctype.rq_job.rq_job.stop_job", { + job_id: frm.doc.name, + }) + .then((r) => { + xhiveframework.show_alert(__("Job Stopped Successfully")); + frm.reload_doc(); + }); + } + ); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/rq_job/rq_job.json b/xhiveframework/core/doctype/rq_job/rq_job.json new file mode 100644 index 0000000..6a39467 --- /dev/null +++ b/xhiveframework/core/doctype/rq_job/rq_job.json @@ -0,0 +1,163 @@ +{ + "actions": [], + "allow_copy": 1, + "autoname": "field:job_id", + "creation": "2023-03-22 20:05:22.962044", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "job_info_section", + "job_id", + "job_name", + "queue", + "timeout", + "column_break_5", + "arguments", + "job_status_section", + "status", + "time_taken", + "column_break_11", + "started_at", + "ended_at", + "exception_section", + "exc_info" + ], + "fields": [ + { + "fieldname": "queue", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Queue", + "options": "default\nshort\nlong" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "queued\nstarted\nfinished\nfailed\ndeferred\nscheduled\ncanceled" + }, + { + "fieldname": "job_id", + "fieldtype": "Data", + "label": "Job ID", + "unique": 1 + }, + { + "fieldname": "exc_info", + "fieldtype": "Code", + "label": "Exception" + }, + { + "fieldname": "job_name", + "fieldtype": "Data", + "label": "Job Name" + }, + { + "fieldname": "arguments", + "fieldtype": "Code", + "label": "Arguments" + }, + { + "fieldname": "timeout", + "fieldtype": "Duration", + "label": "Timeout" + }, + { + "fieldname": "time_taken", + "fieldtype": "Duration", + "label": "Time Taken" + }, + { + "fieldname": "started_at", + "fieldtype": "Datetime", + "label": "Started At" + }, + { + "fieldname": "ended_at", + "fieldtype": "Datetime", + "label": "Ended At" + }, + { + "fieldname": "job_info_section", + "fieldtype": "Section Break", + "label": "Job Info" + }, + { + "fieldname": "job_status_section", + "fieldtype": "Section Break", + "label": "Job Status" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "exception_section", + "fieldtype": "Section Break" + } + ], + "hide_toolbar": 1, + "in_create": 1, + "is_virtual": 1, + "links": [], + "modified": "2024-01-13 10:38:40.230972", + "modified_by": "Administrator", + "module": "Core", + "name": "RQ Job", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [ + { + "color": "Yellow", + "title": "queued" + }, + { + "color": "Blue", + "title": "started" + }, + { + "color": "Red", + "title": "failed" + }, + { + "color": "Green", + "title": "finished" + }, + { + "color": "Orange", + "title": "cancelled" + } + ], + "title_field": "job_name" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/rq_job/rq_job.py b/xhiveframework/core/doctype/rq_job/rq_job.py new file mode 100644 index 0000000..8c4f410 --- /dev/null +++ b/xhiveframework/core/doctype/rq_job/rq_job.py @@ -0,0 +1,230 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import functools +import re + +from rq.command import send_stop_job_command +from rq.exceptions import InvalidJobOperation, NoSuchJobError +from rq.job import Job +from rq.queue import Queue + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import ( + cint, + compare, + convert_utc_to_system_timezone, + create_batch, + make_filter_dict, +) +from xhiveframework.utils.background_jobs import get_queues, get_redis_conn + +QUEUES = ["default", "long", "short"] +JOB_STATUSES = ["queued", "started", "failed", "finished", "deferred", "scheduled", "canceled"] + + +def check_permissions(method): + @functools.wraps(method) + def wrapper(*args, **kwargs): + xhiveframework.only_for("System Manager") + job = args[0].job + if not for_current_site(job): + raise xhiveframework.PermissionError + + return method(*args, **kwargs) + + return wrapper + + +class RQJob(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + arguments: DF.Code | None + ended_at: DF.Datetime | None + exc_info: DF.Code | None + job_id: DF.Data | None + job_name: DF.Data | None + queue: DF.Literal["default", "short", "long"] + started_at: DF.Datetime | None + status: DF.Literal["queued", "started", "finished", "failed", "deferred", "scheduled", "canceled"] + time_taken: DF.Duration | None + timeout: DF.Duration | None + # end: auto-generated types + + def load_from_db(self): + try: + job = Job.fetch(self.name, connection=get_redis_conn()) + except NoSuchJobError: + raise xhiveframework.DoesNotExistError + + if not for_current_site(job): + raise xhiveframework.PermissionError + + super(Document, self).__init__(serialize_job(job)) + self._job_obj = job + + @property + def job(self): + return self._job_obj + + @staticmethod + def get_list(args): + start = cint(args.get("start")) or 0 + page_length = cint(args.get("page_length")) or 20 + + order_desc = "desc" in args.get("order_by", "") + + matched_job_ids = RQJob.get_matching_job_ids(args)[start : start + page_length] + + conn = get_redis_conn() + jobs = [serialize_job(job) for job in Job.fetch_many(job_ids=matched_job_ids, connection=conn) if job] + + return sorted(jobs, key=lambda j: j.modified, reverse=order_desc) + + @staticmethod + def get_matching_job_ids(args) -> list[str]: + filters = make_filter_dict(args.get("filters")) + + queues = _eval_filters(filters.get("queue"), QUEUES) + statuses = _eval_filters(filters.get("status"), JOB_STATUSES) + + matched_job_ids = [] + for queue in get_queues(): + if not queue.name.endswith(tuple(queues)): + continue + for status in statuses: + matched_job_ids.extend(fetch_job_ids(queue, status)) + + return filter_current_site_jobs(matched_job_ids) + + @check_permissions + def delete(self): + self.job.delete() + + @check_permissions + def stop_job(self): + try: + send_stop_job_command(connection=get_redis_conn(), job_id=self.job_id) + except InvalidJobOperation: + xhiveframework.msgprint(_("Job is not running."), title=_("Invalid Operation")) + + @staticmethod + def get_count(args) -> int: + return len(RQJob.get_matching_job_ids(args)) + + # None of these methods apply to virtual job doctype, overriden for sanity. + @staticmethod + def get_stats(args): + return {} + + def db_insert(self, *args, **kwargs): + pass + + def db_update(self, *args, **kwargs): + pass + + +def serialize_job(job: Job) -> xhiveframework._dict: + modified = job.last_heartbeat or job.ended_at or job.started_at or job.created_at + job_kwargs = job.kwargs.get("kwargs", {}) + job_name = job_kwargs.get("job_type") or str(job.kwargs.get("job_name")) + if job_name == "xhiveframework.utils.background_jobs.run_doc_method": + doctype = job_kwargs.get("doctype") + doc_method = job_kwargs.get("doc_method") + if doctype and doc_method: + job_name = f"{doctype}.{doc_method}" + + # function objects have this repr: '' + # This regex just removes unnecessary things around it. + if matches := re.match(r".*) at 0x.*>", job_name): + job_name = matches.group("func_name") + + return xhiveframework._dict( + name=job.id, + job_id=job.id, + queue=job.origin.rsplit(":", 1)[1], + job_name=job_name, + status=job.get_status(), + started_at=convert_utc_to_system_timezone(job.started_at) if job.started_at else "", + ended_at=convert_utc_to_system_timezone(job.ended_at) if job.ended_at else "", + time_taken=(job.ended_at - job.started_at).total_seconds() if job.ended_at else "", + exc_info=job.exc_info, + arguments=xhiveframework.as_json(job.kwargs), + timeout=job.timeout, + creation=convert_utc_to_system_timezone(job.created_at), + modified=convert_utc_to_system_timezone(modified), + _comment_count=0, + owner=job.kwargs.get("user"), + modified_by=job.kwargs.get("user"), + ) + + +def for_current_site(job: Job) -> bool: + return job.kwargs.get("site") == xhiveframework.local.site + + +def filter_current_site_jobs(job_ids: list[str]) -> list[str]: + site = xhiveframework.local.site + + return [j for j in job_ids if j.startswith(site)] + + +def _eval_filters(filter, values: list[str]) -> list[str]: + if filter: + operator, operand = filter + return [val for val in values if compare(val, operator, operand)] + return values + + +def fetch_job_ids(queue: Queue, status: str) -> list[str]: + registry_map = { + "queued": queue, # self + "started": queue.started_job_registry, + "finished": queue.finished_job_registry, + "failed": queue.failed_job_registry, + "deferred": queue.deferred_job_registry, + "scheduled": queue.scheduled_job_registry, + "canceled": queue.canceled_job_registry, + } + + registry = registry_map.get(status) + if registry is not None: + job_ids = registry.get_job_ids() + return [j for j in job_ids if j] + + return [] + + +@xhiveframework.whitelist() +def remove_failed_jobs(): + xhiveframework.only_for("System Manager") + for queue in get_queues(): + fail_registry = queue.failed_job_registry + failed_jobs = filter_current_site_jobs(fail_registry.get_job_ids()) + + # Delete in batches to avoid loading too many things in memory + conn = get_redis_conn() + for job_ids in create_batch(failed_jobs, 100): + for job in Job.fetch_many(job_ids=job_ids, connection=conn): + job and fail_registry.remove(job, delete_job=True) + + +def get_all_queued_jobs(): + jobs = [] + for q in get_queues(): + jobs.extend(q.get_jobs()) + + return [job for job in jobs if for_current_site(job)] + + +@xhiveframework.whitelist() +def stop_job(job_id): + xhiveframework.get_doc("RQ Job", job_id).stop_job() diff --git a/xhiveframework/core/doctype/rq_job/rq_job_list.js b/xhiveframework/core/doctype/rq_job/rq_job_list.js new file mode 100644 index 0000000..ea1dae2 --- /dev/null +++ b/xhiveframework/core/doctype/rq_job/rq_job_list.js @@ -0,0 +1,57 @@ +xhiveframework.listview_settings["RQ Job"] = { + hide_name_column: true, + + onload(listview) { + if (!has_common(xhiveframework.user_roles, ["Administrator", "System Manager"])) return; + + listview.page.add_inner_button( + __("Remove Failed Jobs"), + () => { + xhiveframework.confirm(__("Are you sure you want to remove all failed jobs?"), () => { + xhiveframework.xcall("xhiveframework.core.doctype.rq_job.rq_job.remove_failed_jobs"); + }); + }, + __("Actions") + ); + + xhiveframework.xcall("xhiveframework.utils.scheduler.get_scheduler_status").then(({ status }) => { + if (status === "active") { + listview.page.set_indicator(__("Scheduler: Active"), "green"); + } else { + listview.page.set_indicator(__("Scheduler: Inactive"), "red"); + listview.page.add_inner_button( + __("Enable Scheduler"), + () => { + xhiveframework.confirm(__("Are you sure you want to re-enable scheduler?"), () => { + xhiveframework + .xcall("xhiveframework.utils.scheduler.activate_scheduler") + .then(() => { + xhiveframework.show_alert(__("Enabled Scheduler")); + }) + .catch((e) => { + xhiveframework.show_alert({ + message: __("Failed to enable scheduler: {0}", e), + indicator: "error", + }); + }); + }); + }, + __("Actions") + ); + } + }); + + setInterval(() => { + if (listview.list_view_settings.disable_auto_refresh) { + return; + } + + const route = xhiveframework.get_route() || []; + if (route[0] != "List" || "RQ Job" != route[1]) { + return; + } + + listview.refresh(); + }, 15000); + }, +}; diff --git a/xhiveframework/core/doctype/rq_job/test_rq_job.py b/xhiveframework/core/doctype/rq_job/test_rq_job.py new file mode 100644 index 0000000..68a3bb4 --- /dev/null +++ b/xhiveframework/core/doctype/rq_job/test_rq_job.py @@ -0,0 +1,183 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors + +# See license.txt + +import time + +from rq import exceptions as rq_exc +from rq.job import Job + +import xhiveframework +from xhiveframework.core.doctype.rq_job.rq_job import RQJob, remove_failed_jobs, stop_job +from xhiveframework.installer import update_site_config +from xhiveframework.tests.utils import XhiveFrameworkTestCase, timeout +from xhiveframework.utils import cstr, execute_in_shell +from xhiveframework.utils.background_jobs import get_job_status, is_job_enqueued + + +@timeout(seconds=60) +def wait_for_completion(job: Job): + while True: + if not (job.is_queued or job.is_started): + break + time.sleep(0.2) + + +class TestRQJob(XhiveFrameworkTestCase): + BG_JOB = "xhiveframework.core.doctype.rq_job.test_rq_job.test_func" + + def check_status(self, job: Job, status, wait=True): + if wait: + wait_for_completion(job) + self.assertEqual(xhiveframework.get_doc("RQ Job", job.id).status, status) + + def test_serialization(self): + job = xhiveframework.enqueue(method=self.BG_JOB, queue="short") + rq_job = xhiveframework.get_doc("RQ Job", job.id) + + self.assertEqual(job, rq_job.job) + self.assertDocumentEqual( + { + "name": job.id, + "queue": "short", + "job_name": self.BG_JOB, + "exc_info": None, + }, + rq_job, + ) + self.check_status(job, "finished") + + def test_configurable_ttl(self): + xhiveframework.conf.rq_job_failure_ttl = 600 + job = xhiveframework.enqueue(method=self.BG_JOB, queue="short") + + self.assertEqual(job.failure_ttl, 600) + + def test_func_obj_serialization(self): + job = xhiveframework.enqueue(method=test_func, queue="short") + rq_job = xhiveframework.get_doc("RQ Job", job.id) + self.assertEqual(rq_job.job_name, "test_func") + + @timeout + def test_get_list_filtering(self): + # Check failed job clearning and filtering + remove_failed_jobs() + jobs = RQJob.get_list({"filters": [["RQ Job", "status", "=", "failed"]]}) + self.assertEqual(jobs, []) + + # Fail a job + job = xhiveframework.enqueue(method=self.BG_JOB, queue="short", fail=True) + self.check_status(job, "failed") + jobs = RQJob.get_list({"filters": [["RQ Job", "status", "=", "failed"]]}) + self.assertEqual(len(jobs), 1) + self.assertTrue(jobs[0].exc_info) + + # Assert that non-failed job still exists + non_failed_jobs = RQJob.get_list({"filters": [["RQ Job", "status", "!=", "failed"]]}) + self.assertGreaterEqual(len(non_failed_jobs), 1) + + # Create a slow job and check if it's stuck in "Started" + job = xhiveframework.enqueue(method=self.BG_JOB, queue="short", sleep=10) + time.sleep(3) + self.check_status(job, "started", wait=False) + stop_job(job_id=job.id) + self.check_status(job, "stopped") + + def test_delete_doc(self): + job = xhiveframework.enqueue(method=self.BG_JOB, queue="short") + xhiveframework.get_doc("RQ Job", job.id).delete() + + with self.assertRaises(rq_exc.NoSuchJobError): + job.refresh() + + @timeout + def test_multi_queue_burst_consumption(self): + for _ in range(3): + for q in ["default", "short"]: + xhiveframework.enqueue(self.BG_JOB, sleep=1, queue=q) + + _, stderr = execute_in_shell("bench worker --queue short,default --burst", check_exit_code=True) + self.assertIn("quitting", cstr(stderr)) + + @timeout + def test_multi_queue_burst_consumption_worker_pool(self): + for _ in range(3): + for q in ["default", "short"]: + xhiveframework.enqueue(self.BG_JOB, sleep=1, queue=q) + + _, stderr = execute_in_shell( + "bench worker-pool --queue short,default --burst --num-workers=4", check_exit_code=True + ) + self.assertIn("quitting", cstr(stderr)) + + def test_job_id_manual_dedup(self): + job_id = "test_dedup" + job = xhiveframework.enqueue(self.BG_JOB, sleep=5, job_id=job_id) + self.assertTrue(is_job_enqueued(job_id)) + self.check_status(job, "finished") + self.assertFalse(is_job_enqueued(job_id)) + + def test_auto_job_dedup(self): + job_id = "test_dedup" + job1 = xhiveframework.enqueue(self.BG_JOB, sleep=2, job_id=job_id, deduplicate=True) + job2 = xhiveframework.enqueue(self.BG_JOB, sleep=5, job_id=job_id, deduplicate=True) + self.assertIsNone(job2) + self.check_status(job1, "finished") # wait + + # Failed jobs last longer, subsequent job should still pass with same ID. + job3 = xhiveframework.enqueue(self.BG_JOB, fail=True, job_id=job_id, deduplicate=True) + self.check_status(job3, "failed") + job4 = xhiveframework.enqueue(self.BG_JOB, sleep=1, job_id=job_id, deduplicate=True) + self.check_status(job4, "finished") + + @timeout + def test_enqueue_after_commit(self): + job_id = xhiveframework.generate_hash() + + xhiveframework.enqueue(self.BG_JOB, enqueue_after_commit=True, job_id=job_id) + self.assertIsNone(get_job_status(job_id)) + + xhiveframework.db.commit() + self.assertIsNotNone(get_job_status(job_id)) + + job_id = xhiveframework.generate_hash() + xhiveframework.enqueue(self.BG_JOB, enqueue_after_commit=True, job_id=job_id) + self.assertIsNone(get_job_status(job_id)) + + xhiveframework.db.rollback() + self.assertIsNone(get_job_status(job_id)) + + xhiveframework.db.commit() + self.assertIsNone(get_job_status(job_id)) + + def test_memory_usage(self): + if xhiveframework.db.db_type != "mariadb": + return + job = xhiveframework.enqueue("xhiveframework.utils.data._get_rss_memory_usage") + self.check_status(job, "finished") + + rss = job.latest_result().return_value + msg = """Memory usage of simple background job increased. Potential root cause can be a newly added python module import. Check and move them to approriate file/function to avoid loading the module by default.""" + + # If this starts failing analyze memory usage using memray or some equivalent tool to find + # offending imports/function calls. + # Refer this PR: https://lab.membtech.com/xhiveframework/xhiveframework15/pull/21467 + LAST_MEASURED_USAGE = 41 + self.assertLessEqual(rss, LAST_MEASURED_USAGE * 1.05, msg) + + def test_clear_failed_jobs(self): + limit = 10 + update_site_config("rq_failed_jobs_limit", limit) + + jobs = [xhiveframework.enqueue(method=self.BG_JOB, queue="short", fail=True) for _ in range(limit * 2)] + self.check_status(jobs[-1], "failed") + self.assertLessEqual(RQJob.get_count({"filters": [["RQ Job", "status", "=", "failed"]]}), limit * 1.1) + + +def test_func(fail=False, sleep=0): + if fail: + 42 / 0 + if sleep: + time.sleep(sleep) + + return True diff --git a/xhiveframework/core/doctype/rq_worker/__init__.py b/xhiveframework/core/doctype/rq_worker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/rq_worker/rq_worker.js b/xhiveframework/core/doctype/rq_worker/rq_worker.js new file mode 100644 index 0000000..e3d79a4 --- /dev/null +++ b/xhiveframework/core/doctype/rq_worker/rq_worker.js @@ -0,0 +1,9 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("RQ Worker", { + refresh: function (frm) { + // Nothing in this form is supposed to be editable. + frm.disable_form(); + }, +}); diff --git a/xhiveframework/core/doctype/rq_worker/rq_worker.json b/xhiveframework/core/doctype/rq_worker/rq_worker.json new file mode 100644 index 0000000..cc301be --- /dev/null +++ b/xhiveframework/core/doctype/rq_worker/rq_worker.json @@ -0,0 +1,146 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2022-09-10 14:54:57.342170", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "worker_information_section", + "queue", + "queue_type", + "column_break_4", + "worker_name", + "statistics_section", + "status", + "pid", + "current_job_id", + "successful_job_count", + "failed_job_count", + "column_break_12", + "birth_date", + "last_heartbeat", + "total_working_time", + "utilization_percent" + ], + "fields": [ + { + "fieldname": "worker_name", + "fieldtype": "Data", + "label": "Worker Name", + "unique": 1 + }, + { + "fieldname": "status", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Status" + }, + { + "fieldname": "current_job_id", + "fieldtype": "Link", + "label": "Current Job ID", + "options": "RQ Job" + }, + { + "fieldname": "pid", + "fieldtype": "Data", + "label": "PID" + }, + { + "fieldname": "last_heartbeat", + "fieldtype": "Datetime", + "label": "Last Heartbeat" + }, + { + "fieldname": "birth_date", + "fieldtype": "Datetime", + "label": "Start Time" + }, + { + "fieldname": "successful_job_count", + "fieldtype": "Int", + "label": "Successful Job Count" + }, + { + "fieldname": "failed_job_count", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Failed Job Count" + }, + { + "fieldname": "total_working_time", + "fieldtype": "Duration", + "label": "Total Working Time" + }, + { + "fieldname": "queue", + "fieldtype": "Data", + "label": "Queue(s)" + }, + { + "fieldname": "queue_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Queue Type(s)", + "options": "default\nlong\nshort" + }, + { + "fieldname": "worker_information_section", + "fieldtype": "Section Break", + "label": "Worker Information" + }, + { + "fieldname": "statistics_section", + "fieldtype": "Section Break", + "label": "Statistics" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "utilization_percent", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Utilization %" + } + ], + "hide_toolbar": 1, + "in_create": 1, + "is_virtual": 1, + "links": [], + "modified": "2024-02-29 19:31:08.502527", + "modified_by": "Administrator", + "module": "Core", + "name": "RQ Worker", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [ + { + "color": "Blue", + "title": "idle" + }, + { + "color": "Yellow", + "title": "busy" + } + ], + "title_field": "pid" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/rq_worker/rq_worker.py b/xhiveframework/core/doctype/rq_worker/rq_worker.py new file mode 100644 index 0000000..f386cb9 --- /dev/null +++ b/xhiveframework/core/doctype/rq_worker/rq_worker.py @@ -0,0 +1,109 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import datetime +from contextlib import suppress + +from rq import Worker + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, convert_utc_to_system_timezone +from xhiveframework.utils.background_jobs import get_workers + + +class RQWorker(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + birth_date: DF.Datetime | None + current_job_id: DF.Link | None + failed_job_count: DF.Int + last_heartbeat: DF.Datetime | None + pid: DF.Data | None + queue: DF.Data | None + queue_type: DF.Literal["default", "long", "short"] + status: DF.Data | None + successful_job_count: DF.Int + total_working_time: DF.Duration | None + utilization_percent: DF.Percent + worker_name: DF.Data | None + # end: auto-generated types + + def load_from_db(self): + all_workers = get_workers() + workers = [w for w in all_workers if w.name == self.name] + if not workers: + raise xhiveframework.DoesNotExistError + d = serialize_worker(workers[0]) + + super(Document, self).__init__(d) + + @staticmethod + def get_list(args): + start = cint(args.get("start")) or 0 + page_length = cint(args.get("page_length")) or 20 + + workers = get_workers() + + valid_workers = [w for w in workers if w.pid][start : start + page_length] + return [serialize_worker(worker) for worker in valid_workers] + + @staticmethod + def get_count(args) -> int: + return len(get_workers()) + + # None of these methods apply to virtual workers, overriden for sanity. + @staticmethod + def get_stats(args): + return {} + + def db_insert(self, *args, **kwargs): + pass + + def db_update(self, *args, **kwargs): + pass + + def delete(self): + pass + + +def serialize_worker(worker: Worker) -> xhiveframework._dict: + queue_names = worker.queue_names() + + queue = ", ".join(queue_names) + queue_types = ",".join(q.rsplit(":", 1)[1] for q in queue_names) + + current_job = worker.get_current_job_id() + if current_job and not current_job.startswith(xhiveframework.local.site): + current_job = None + + return xhiveframework._dict( + name=worker.name, + queue=queue, + queue_type=queue_types, + worker_name=worker.name, + status=worker.get_state(), + pid=worker.pid, + current_job_id=current_job, + last_heartbeat=convert_utc_to_system_timezone(worker.last_heartbeat), + birth_date=convert_utc_to_system_timezone(worker.birth_date), + successful_job_count=worker.successful_job_count, + failed_job_count=worker.failed_job_count, + total_working_time=worker.total_working_time, + _comment_count=0, + modified=convert_utc_to_system_timezone(worker.last_heartbeat), + creation=convert_utc_to_system_timezone(worker.birth_date), + utilization_percent=compute_utilization(worker), + ) + + +def compute_utilization(worker: Worker) -> float: + with suppress(Exception): + total_time = (datetime.datetime.utcnow() - worker.birth_date).total_seconds() + return worker.total_working_time / total_time * 100 diff --git a/xhiveframework/core/doctype/rq_worker/rq_worker_list.js b/xhiveframework/core/doctype/rq_worker/rq_worker_list.js new file mode 100644 index 0000000..4547e0d --- /dev/null +++ b/xhiveframework/core/doctype/rq_worker/rq_worker_list.js @@ -0,0 +1,9 @@ +xhiveframework.listview_settings["RQ Worker"] = { + refresh(listview) { + listview.$no_result.html(` +
    + ${__("No RQ Workers connected. Try restarting the bench.")} +
    + `); + }, +}; diff --git a/xhiveframework/core/doctype/rq_worker/test_rq_worker.py b/xhiveframework/core/doctype/rq_worker/test_rq_worker.py new file mode 100644 index 0000000..67a92c2 --- /dev/null +++ b/xhiveframework/core/doctype/rq_worker/test_rq_worker.py @@ -0,0 +1,17 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# See license.txt + +import xhiveframework +from xhiveframework.core.doctype.rq_worker.rq_worker import RQWorker +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestRQWorker(XhiveFrameworkTestCase): + def test_get_worker_list(self): + workers = RQWorker.get_list({}) + self.assertGreaterEqual(len(workers), 1) + self.assertTrue(any("short" in w.queue_type for w in workers)) + + def test_worker_serialization(self): + workers = RQWorker.get_list({}) + xhiveframework.get_doc("RQ Worker", workers[0].name) diff --git a/xhiveframework/core/doctype/scheduled_job_log/__init__.py b/xhiveframework/core/doctype/scheduled_job_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.js b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.js new file mode 100644 index 0000000..7971d2e --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Scheduled Job Log", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.json b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.json new file mode 100644 index 0000000..451c410 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.json @@ -0,0 +1,65 @@ +{ + "actions": [], + "creation": "2019-09-23 14:36:36.935869", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status", + "scheduled_job_type", + "details" + ], + "fields": [ + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Scheduled\nComplete\nFailed", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "details", + "fieldtype": "Code", + "label": "Details", + "read_only": 1 + }, + { + "fieldname": "scheduled_job_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Scheduled Job", + "options": "Scheduled Job Type", + "read_only": 1, + "reqd": 1 + } + ], + "links": [], + "modified": "2022-06-13 05:41:21.090972", + "modified_by": "Administrator", + "module": "Core", + "name": "Scheduled Job Log", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "scheduled_job_type" +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.py b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.py new file mode 100644 index 0000000..59b3548 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log.py @@ -0,0 +1,27 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.query_builder import Interval +from xhiveframework.query_builder.functions import Now + + +class ScheduledJobLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + details: DF.Code | None + scheduled_job_type: DF.Link + status: DF.Literal["Scheduled", "Complete", "Failed"] + + # end: auto-generated types + @staticmethod + def clear_old_logs(days=90): + table = xhiveframework.qb.DocType("Scheduled Job Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) diff --git a/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log_list.js b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log_list.js new file mode 100644 index 0000000..d8c66f3 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_log/scheduled_job_log_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Scheduled Job Log"] = { + onload: function (listview) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(cur_list.doctype); + }); + }, +}; diff --git a/xhiveframework/core/doctype/scheduled_job_log/test_scheduled_job_log.py b/xhiveframework/core/doctype/scheduled_job_log/test_scheduled_job_log.py new file mode 100644 index 0000000..11ff251 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_log/test_scheduled_job_log.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestScheduledJobLog(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/scheduled_job_type/__init__.py b/xhiveframework/core/doctype/scheduled_job_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.js b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.js new file mode 100644 index 0000000..e242705 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Scheduled Job Type", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.json b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.json new file mode 100644 index 0000000..ac25625 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.json @@ -0,0 +1,129 @@ +{ + "actions": [ + { + "action": "xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type.execute_event", + "action_type": "Server Action", + "label": "Execute" + } + ], + "creation": "2019-09-23 14:34:09.205368", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stopped", + "method", + "server_script", + "frequency", + "cron_format", + "create_log", + "status_section", + "last_execution", + "column_break_9", + "next_execution" + ], + "fields": [ + { + "fieldname": "method", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Method", + "read_only": 1, + "reqd": 1 + }, + { + "default": "0", + "fieldname": "stopped", + "fieldtype": "Check", + "label": "Stopped" + }, + { + "default": "0", + "depends_on": "eval:doc.frequency==='All'", + "fieldname": "create_log", + "fieldtype": "Check", + "label": "Create Log" + }, + { + "fieldname": "last_execution", + "fieldtype": "Datetime", + "label": "Last Execution", + "read_only": 1 + }, + { + "allow_in_quick_entry": 1, + "depends_on": "eval:doc.frequency==='Cron'", + "description": "
    *  *  *  *  *\n\u252c  \u252c  \u252c  \u252c  \u252c\n\u2502  \u2502  \u2502  \u2502  \u2502\n\u2502  \u2502  \u2502  \u2502  \u2514 day of week (0 - 6) (0 is Sunday)\n\u2502  \u2502  \u2502  \u2514\u2500\u2500\u2500\u2500\u2500 month (1 - 12)\n\u2502  \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 day of month (1 - 31)\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 hour (0 - 23)\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 minute (0 - 59)\n\n---\n\n* - Any value\n/ - Step values\n
    \n", + "fieldname": "cron_format", + "fieldtype": "Data", + "label": "Cron Format", + "read_only": 1 + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Frequency", + "options": "All\nHourly\nHourly Long\nDaily\nDaily Long\nWeekly\nWeekly Long\nMonthly\nMonthly Long\nCron\nYearly\nAnnual", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "server_script", + "fieldtype": "Link", + "label": "Server Script", + "options": "Server Script", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "next_execution", + "fieldtype": "Datetime", + "is_virtual": 1, + "label": "Next Execution", + "read_only": 1 + }, + { + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + } + ], + "in_create": 1, + "links": [ + { + "link_doctype": "Scheduled Job Log", + "link_fieldname": "scheduled_job_type" + } + ], + "modified": "2023-10-14 11:26:05.005930", + "modified_by": "Administrator", + "module": "Core", + "name": "Scheduled Job Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "method", + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.py b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.py new file mode 100644 index 0000000..5c600c8 --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -0,0 +1,261 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json +from datetime import datetime + +import click +from croniter import CroniterBadCronError, croniter + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import get_datetime, now_datetime +from xhiveframework.utils.background_jobs import enqueue, is_job_enqueued + + +class ScheduledJobType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + create_log: DF.Check + cron_format: DF.Data | None + frequency: DF.Literal[ + "All", + "Hourly", + "Hourly Long", + "Daily", + "Daily Long", + "Weekly", + "Weekly Long", + "Monthly", + "Monthly Long", + "Cron", + "Yearly", + "Annual", + ] + last_execution: DF.Datetime | None + method: DF.Data + next_execution: DF.Datetime | None + server_script: DF.Link | None + stopped: DF.Check + + # end: auto-generated types + def autoname(self): + self.name = ".".join(self.method.split(".")[-2:]) + + def validate(self): + if self.frequency != "All": + # force logging for all events other than continuous ones (ALL) + self.create_log = 1 + + if self.frequency == "Cron": + if not self.cron_format: + xhiveframework.throw(_("Cron format is required for job types with Cron frequency.")) + try: + croniter(self.cron_format) + except CroniterBadCronError: + xhiveframework.throw( + _("{0} is not a valid Cron expression.").format(f"{self.cron_format}"), + title=_("Bad Cron Expression"), + ) + + def enqueue(self, force=False) -> bool: + # enqueue event if last execution is done + if self.is_event_due() or force: + if not self.is_job_in_queue(): + enqueue( + "xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type.run_scheduled_job", + queue=self.get_queue_name(), + job_type=self.method, + job_id=self.rq_job_id, + ) + return True + else: + xhiveframework.logger("scheduler").error( + f"Skipped queueing {self.method} because it was found in queue for {xhiveframework.local.site}" + ) + + return False + + def is_event_due(self, current_time=None): + """Return true if event is due based on time lapsed since last execution""" + # if the next scheduled event is before NOW, then its due! + return self.get_next_execution() <= (current_time or now_datetime()) + + def is_job_in_queue(self) -> bool: + return is_job_enqueued(self.rq_job_id) + + @property + def rq_job_id(self): + """Unique ID created to deduplicate jobs with single RQ call.""" + return f"scheduled_job::{self.method}" + + @property + def next_execution(self): + return self.get_next_execution() + + def get_next_execution(self): + CRON_MAP = { + "Yearly": "0 0 1 1 *", + "Annual": "0 0 1 1 *", + "Monthly": "0 0 1 * *", + "Monthly Long": "0 0 1 * *", + "Weekly": "0 0 * * 0", + "Weekly Long": "0 0 * * 0", + "Daily": "0 0 * * *", + "Daily Long": "0 0 * * *", + "Hourly": "0 * * * *", + "Hourly Long": "0 * * * *", + "All": f"*/{(xhiveframework.get_conf().scheduler_interval or 240) // 60} * * * *", + } + + if not self.cron_format: + self.cron_format = CRON_MAP[self.frequency] + + # If this is a cold start then last_execution will not be set. + # Creation is set as fallback because if very old fallback is set job might trigger + # immediately, even when it's meant to be daily. + # A dynamic fallback like current time might miss the scheduler interval and job will never start. + last_execution = get_datetime(self.last_execution or self.creation) + return croniter(self.cron_format, last_execution).get_next(datetime) + + def execute(self): + self.scheduler_log = None + try: + self.log_status("Start") + if self.server_script: + script_name = xhiveframework.db.get_value("Server Script", self.server_script) + if script_name: + xhiveframework.get_doc("Server Script", script_name).execute_scheduled_method() + else: + xhiveframework.get_attr(self.method)() + xhiveframework.db.commit() + self.log_status("Complete") + except Exception: + xhiveframework.db.rollback() + self.log_status("Failed") + + def log_status(self, status): + # log file + xhiveframework.logger("scheduler").info(f"Scheduled Job {status}: {self.method} for {xhiveframework.local.site}") + self.update_scheduler_log(status) + + def update_scheduler_log(self, status): + if not self.create_log: + # self.get_next_execution will work properly iff self.last_execution is properly set + if self.frequency == "All" and status == "Start": + self.db_set("last_execution", now_datetime(), update_modified=False) + xhiveframework.db.commit() + return + if not self.scheduler_log: + self.scheduler_log = xhiveframework.get_doc( + dict(doctype="Scheduled Job Log", scheduled_job_type=self.name) + ).insert(ignore_permissions=True) + self.scheduler_log.db_set("status", status) + if status == "Failed": + self.scheduler_log.db_set("details", xhiveframework.get_traceback(with_context=True)) + if status == "Start": + self.db_set("last_execution", now_datetime(), update_modified=False) + xhiveframework.db.commit() + + def get_queue_name(self): + return "long" if ("Long" in self.frequency) else "default" + + def on_trash(self): + xhiveframework.db.delete("Scheduled Job Log", {"scheduled_job_type": self.name}) + + +@xhiveframework.whitelist() +def execute_event(doc: str): + xhiveframework.only_for("System Manager") + doc = json.loads(doc) + xhiveframework.get_doc("Scheduled Job Type", doc.get("name")).enqueue(force=True) + return doc + + +def run_scheduled_job(job_type: str): + """This is a wrapper function that runs a hooks.scheduler_events method""" + try: + xhiveframework.get_doc("Scheduled Job Type", dict(method=job_type)).execute() + except Exception: + print(xhiveframework.get_traceback()) + + +def sync_jobs(hooks: dict | None = None): + xhiveframework.reload_doc("core", "doctype", "scheduled_job_type") + scheduler_events = hooks or xhiveframework.get_hooks("scheduler_events") + all_events = insert_events(scheduler_events) + clear_events(all_events) + + +def insert_events(scheduler_events: dict) -> list: + cron_jobs, event_jobs = [], [] + for event_type in scheduler_events: + events = scheduler_events.get(event_type) + if isinstance(events, dict): + cron_jobs += insert_cron_jobs(events) + else: + # hourly, daily etc + event_jobs += insert_event_jobs(events, event_type) + return cron_jobs + event_jobs + + +def insert_cron_jobs(events: dict) -> list: + cron_jobs = [] + for cron_format in events: + for event in events.get(cron_format): + cron_jobs.append(event) + insert_single_event("Cron", event, cron_format) + return cron_jobs + + +def insert_event_jobs(events: list, event_type: str) -> list: + event_jobs = [] + for event in events: + event_jobs.append(event) + frequency = event_type.replace("_", " ").title() + insert_single_event(frequency, event) + return event_jobs + + +def insert_single_event(frequency: str, event: str, cron_format: str | None = None): + cron_expr = {"cron_format": cron_format} if cron_format else {} + + try: + xhiveframework.get_attr(event) + except Exception as e: + click.secho(f"{event} is not a valid method: {e}", fg="yellow") + + doc = xhiveframework.get_doc( + { + "doctype": "Scheduled Job Type", + "method": event, + "cron_format": cron_format, + "frequency": frequency, + } + ) + + if not xhiveframework.db.exists("Scheduled Job Type", {"method": event, "frequency": frequency, **cron_expr}): + savepoint = "scheduled_job_type_creation" + try: + xhiveframework.db.savepoint(savepoint) + doc.insert() + except xhiveframework.DuplicateEntryError: + xhiveframework.db.rollback(save_point=savepoint) + doc.delete() + doc.insert() + + +def clear_events(all_events: list): + for event in xhiveframework.get_all("Scheduled Job Type", fields=["name", "method", "server_script"]): + is_server_script = event.server_script + is_defined_in_hooks = event.method in all_events + + if not (is_defined_in_hooks or is_server_script): + xhiveframework.delete_doc("Scheduled Job Type", event.name) diff --git a/xhiveframework/core/doctype/scheduled_job_type/test_scheduled_job_type.py b/xhiveframework/core/doctype/scheduled_job_type/test_scheduled_job_type.py new file mode 100644 index 0000000..53ced4b --- /dev/null +++ b/xhiveframework/core/doctype/scheduled_job_type/test_scheduled_job_type.py @@ -0,0 +1,90 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from datetime import timedelta + +import xhiveframework +from xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import get_datetime +from xhiveframework.utils.data import add_to_date, now_datetime + + +class TestScheduledJobType(XhiveFrameworkTestCase): + def setUp(self): + xhiveframework.db.rollback() + xhiveframework.db.truncate("Scheduled Job Type") + sync_jobs() + xhiveframework.db.commit() + + def test_sync_jobs(self): + all_job = xhiveframework.get_doc("Scheduled Job Type", dict(method="xhiveframework.email.queue.flush")) + self.assertEqual(all_job.frequency, "All") + + daily_job = xhiveframework.get_doc( + "Scheduled Job Type", dict(method="xhiveframework.desk.notifications.clear_notifications") + ) + self.assertEqual(daily_job.frequency, "Daily") + + # check if cron jobs are synced + cron_job = xhiveframework.get_doc("Scheduled Job Type", dict(method="xhiveframework.oauth.delete_oauth2_data")) + self.assertEqual(cron_job.frequency, "Cron") + self.assertEqual(cron_job.cron_format, "0/15 * * * *") + + # check if jobs are synced after change in hooks + updated_scheduler_events = {"hourly": ["xhiveframework.email.queue.flush"]} + sync_jobs(updated_scheduler_events) + updated_scheduled_job = xhiveframework.get_doc("Scheduled Job Type", {"method": "xhiveframework.email.queue.flush"}) + self.assertEqual(updated_scheduled_job.frequency, "Hourly") + + def test_daily_job(self): + job = xhiveframework.get_doc( + "Scheduled Job Type", dict(method="xhiveframework.desk.notifications.clear_notifications") + ) + job.db_set("last_execution", "2019-01-01 00:00:00") + self.assertTrue(job.is_event_due(get_datetime("2019-01-02 00:00:06"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-01 00:00:06"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-01 23:59:59"))) + + def test_weekly_job(self): + job = xhiveframework.get_doc( + "Scheduled Job Type", + dict(method="xhiveframework.social.doctype.energy_point_log.energy_point_log.send_weekly_summary"), + ) + job.db_set("last_execution", "2019-01-01 00:00:00") + self.assertTrue(job.is_event_due(get_datetime("2019-01-06 00:00:01"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-02 00:00:06"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-05 23:59:59"))) + + def test_monthly_job(self): + job = xhiveframework.get_doc( + "Scheduled Job Type", + dict(method="xhiveframework.email.doctype.auto_email_report.auto_email_report.send_monthly"), + ) + job.db_set("last_execution", "2019-01-01 00:00:00") + self.assertTrue(job.is_event_due(get_datetime("2019-02-01 00:00:01"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-15 00:00:06"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-31 23:59:59"))) + + def test_cron_job(self): + # runs every 15 mins + job = xhiveframework.get_doc("Scheduled Job Type", dict(method="xhiveframework.oauth.delete_oauth2_data")) + job.db_set("last_execution", "2019-01-01 00:00:00") + self.assertEqual(job.next_execution, get_datetime("2019-01-01 00:15:00")) + self.assertTrue(job.is_event_due(get_datetime("2019-01-01 00:15:01"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-01 00:05:06"))) + self.assertFalse(job.is_event_due(get_datetime("2019-01-01 00:14:59"))) + + def test_cold_start(self): + now = now_datetime() + just_before_12_am = now.replace(hour=11, minute=59, second=30) + just_after_12_am = now.replace(hour=0, minute=0, second=30) + timedelta(days=1) + + job = xhiveframework.new_doc("Scheduled Job Type") + job.frequency = "Daily" + job.set_user_and_timestamp() + + with self.freeze_time(just_before_12_am): + self.assertFalse(job.is_event_due()) + + with self.freeze_time(just_after_12_am): + self.assertTrue(job.is_event_due()) diff --git a/xhiveframework/core/doctype/server_script/__init__.py b/xhiveframework/core/doctype/server_script/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/server_script/server_script.js b/xhiveframework/core/doctype/server_script/server_script.js new file mode 100644 index 0000000..b2c925a --- /dev/null +++ b/xhiveframework/core/doctype/server_script/server_script.js @@ -0,0 +1,97 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Server Script", { + setup: function (frm) { + frm.trigger("setup_help"); + }, + refresh: function (frm) { + if (frm.doc.script_type != "Scheduler Event") { + frm.dashboard.hide(); + } + + if (!frm.is_new()) { + frm.add_custom_button(__("Compare Versions"), () => { + new xhiveframework.ui.DiffView("Server Script", "script", frm.doc.name); + }); + } + + frm.call("get_autocompletion_items") + .then((r) => r.message) + .then((items) => { + frm.set_df_property("script", "autocompletions", items); + }); + + frm.trigger("check_safe_exec"); + }, + + check_safe_exec(frm) { + xhiveframework.xcall("xhiveframework.core.doctype.server_script.server_script.enabled").then((enabled) => { + if (enabled === false) { + let docs_link = + "https://xhiveframework.com/docs/user/en/desk/scripting/server-script"; + let docs = `${__("Official Documentation")}`; + + frm.dashboard.clear_comment(); + let msg = __("Server Scripts feature is not available on this site.") + " "; + msg += __("To enable server scripts, read the {0}.", [docs]); + frm.dashboard.add_comment(msg, "yellow", true); + } + }); + }, + + setup_help(frm) { + frm.get_field("help_html").html(` +

    DocType Event

    +

    Add logic for standard doctype events like Before Insert, After Submit, etc.

    +
    +	
    +# set property
    +if "test" in doc.description:
    +	doc.status = 'Closed'
    +
    +
    +# validate
    +if "validate" in doc.description:
    +	raise xhiveframework.ValidationError
    +
    +# auto create another document
    +if doc.allocated_to:
    +	xhiveframework.get_doc(dict(
    +		doctype = 'ToDo'
    +		owner = doc.allocated_to,
    +		description = doc.subject
    +	)).insert()
    +
    +
    + +
    + +

    API Call

    +

    Respond to /api/method/<method-name> calls, just like whitelisted methods

    +
    
    +# respond to API
    +
    +if xhiveframework.form_dict.message == "ping":
    +	xhiveframework.response['message'] = "pong"
    +else:
    +	xhiveframework.response['message'] = "ok"
    +
    + +
    + +

    Permission Query

    +

    Add conditions to the where clause of list queries.

    +
    
    +# generate dynamic conditions and set it in the conditions variable
    +tenant_id = xhiveframework.db.get_value(...)
    +conditions = f'tenant_id = {tenant_id}'
    +
    +# resulting select query
    +select name from \`tabPerson\`
    +where tenant_id = 2
    +order by creation desc
    +
    +`); + }, +}); diff --git a/xhiveframework/core/doctype/server_script/server_script.json b/xhiveframework/core/doctype/server_script/server_script.json new file mode 100644 index 0000000..9f072c1 --- /dev/null +++ b/xhiveframework/core/doctype/server_script/server_script.json @@ -0,0 +1,178 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2019-09-30 11:56:57.943241", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "script_type", + "reference_doctype", + "event_frequency", + "cron_format", + "doctype_event", + "api_method", + "allow_guest", + "column_break_3", + "module", + "disabled", + "section_break_8", + "script", + "rate_limiting_section", + "enable_rate_limit", + "rate_limit_count", + "rate_limit_seconds", + "help_section", + "help_html" + ], + "fields": [ + { + "fieldname": "script_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Script Type", + "options": "DocType Event\nScheduler Event\nPermission Query\nAPI", + "reqd": 1 + }, + { + "fieldname": "script", + "fieldtype": "Code", + "label": "Script", + "options": "Python", + "reqd": 1 + }, + { + "depends_on": "eval:['DocType Event', 'Permission Query'].includes(doc.script_type)", + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Document Type", + "options": "DocType", + "search_index": 1 + }, + { + "depends_on": "eval:doc.script_type==='DocType Event'", + "fieldname": "doctype_event", + "fieldtype": "Select", + "label": "DocType Event", + "options": "Before Insert\nBefore Validate\nBefore Save\nAfter Insert\nAfter Save\nBefore Rename\nAfter Rename\nBefore Submit\nAfter Submit\nBefore Cancel\nAfter Cancel\nBefore Delete\nAfter Delete\nBefore Save (Submitted Document)\nAfter Save (Submitted Document)\nOn Payment Authorization" + }, + { + "depends_on": "eval:doc.script_type==='API'", + "fieldname": "api_method", + "fieldtype": "Data", + "label": "API Method" + }, + { + "default": "0", + "depends_on": "eval:doc.script_type==='API'", + "fieldname": "allow_guest", + "fieldtype": "Check", + "label": "Allow Guest" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "help_section", + "fieldtype": "Section Break", + "label": "Help" + }, + { + "fieldname": "help_html", + "fieldtype": "HTML" + }, + { + "depends_on": "eval:doc.script_type == \"Scheduler Event\"", + "fieldname": "event_frequency", + "fieldtype": "Select", + "label": "Event Frequency", + "mandatory_depends_on": "eval:doc.script_type == \"Scheduler Event\"", + "options": "All\nHourly\nDaily\nWeekly\nMonthly\nYearly\nHourly Long\nDaily Long\nWeekly Long\nMonthly Long\nCron" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module (for export)", + "options": "Module Def", + "search_index": 1 + }, + { + "depends_on": "eval:doc.script_type==='API'", + "fieldname": "rate_limiting_section", + "fieldtype": "Section Break", + "label": "Rate Limiting" + }, + { + "default": "0", + "fieldname": "enable_rate_limit", + "fieldtype": "Check", + "label": "Enable Rate Limit" + }, + { + "default": "5", + "depends_on": "enable_rate_limit", + "fieldname": "rate_limit_count", + "fieldtype": "Int", + "label": "Request Limit" + }, + { + "default": "86400", + "depends_on": "enable_rate_limit", + "fieldname": "rate_limit_seconds", + "fieldtype": "Int", + "label": "Time Window (Seconds)" + }, + { + "depends_on": "eval:doc.event_frequency==='Cron'", + "description": "
    *  *  *  *  *\n\u252c  \u252c  \u252c  \u252c  \u252c\n\u2502  \u2502  \u2502  \u2502  \u2502\n\u2502  \u2502  \u2502  \u2502  \u2514 day of week (0 - 6) (0 is Sunday)\n\u2502  \u2502  \u2502  \u2514\u2500\u2500\u2500\u2500\u2500 month (1 - 12)\n\u2502  \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 day of month (1 - 31)\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 hour (0 - 23)\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 minute (0 - 59)\n\n---\n\n* - Any value\n/ - Step values\n
    \n", + "fieldname": "cron_format", + "fieldtype": "Data", + "label": "Cron Format" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "link_doctype": "Scheduled Job Type", + "link_fieldname": "server_script" + } + ], + "modified": "2024-02-27 11:44:46.397495", + "modified_by": "Administrator", + "module": "Core", + "name": "Server Script", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Script Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/server_script/server_script.py b/xhiveframework/core/doctype/server_script/server_script.py new file mode 100644 index 0000000..92be90a --- /dev/null +++ b/xhiveframework/core/doctype/server_script/server_script.py @@ -0,0 +1,304 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from functools import partial +from types import FunctionType, MethodType, ModuleType + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.rate_limiter import rate_limit +from xhiveframework.utils.safe_exec import ( + XhiveFrameworkTransformer, + NamespaceDict, + get_safe_globals, + is_safe_exec_enabled, + safe_exec, +) + + +class ServerScript(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow_guest: DF.Check + api_method: DF.Data | None + cron_format: DF.Data | None + disabled: DF.Check + doctype_event: DF.Literal[ + "Before Insert", + "Before Validate", + "Before Save", + "After Insert", + "After Save", + "Before Rename", + "After Rename", + "Before Submit", + "After Submit", + "Before Cancel", + "After Cancel", + "Before Delete", + "After Delete", + "Before Save (Submitted Document)", + "After Save (Submitted Document)", + "On Payment Authorization", + ] + enable_rate_limit: DF.Check + event_frequency: DF.Literal[ + "All", + "Hourly", + "Daily", + "Weekly", + "Monthly", + "Yearly", + "Hourly Long", + "Daily Long", + "Weekly Long", + "Monthly Long", + "Cron", + ] + module: DF.Link | None + rate_limit_count: DF.Int + rate_limit_seconds: DF.Int + reference_doctype: DF.Link | None + script: DF.Code + script_type: DF.Literal["DocType Event", "Scheduler Event", "Permission Query", "API"] + + # end: auto-generated types + def validate(self): + xhiveframework.only_for("Script Manager", True) + self.sync_scheduled_jobs() + self.clear_scheduled_events() + self.check_if_compilable_in_restricted_context() + + def on_update(self): + self.sync_scheduler_events() + + def clear_cache(self): + xhiveframework.cache.delete_value("server_script_map") + return super().clear_cache() + + def on_trash(self): + xhiveframework.cache.delete_value("server_script_map") + if self.script_type == "Scheduler Event": + for job in self.scheduled_jobs: + xhiveframework.delete_doc("Scheduled Job Type", job.name) + + def get_code_fields(self): + return {"script": "py"} + + @property + def scheduled_jobs(self) -> list[dict[str, str]]: + return xhiveframework.get_all( + "Scheduled Job Type", + filters={"server_script": self.name}, + fields=["name", "stopped"], + ) + + def sync_scheduled_jobs(self): + """Sync Scheduled Job Type statuses if Server Script's disabled status is changed""" + if self.script_type != "Scheduler Event" or not self.has_value_changed("disabled"): + return + + for scheduled_job in self.scheduled_jobs: + if bool(scheduled_job.stopped) != bool(self.disabled): + job = xhiveframework.get_doc("Scheduled Job Type", scheduled_job.name) + job.stopped = self.disabled + job.save() + + def sync_scheduler_events(self): + """Create or update Scheduled Job Type documents for Scheduler Event Server Scripts""" + if not self.disabled and self.event_frequency and self.script_type == "Scheduler Event": + cron_format = self.cron_format if self.event_frequency == "Cron" else None + setup_scheduler_events( + script_name=self.name, frequency=self.event_frequency, cron_format=cron_format + ) + + def clear_scheduled_events(self): + """Deletes existing scheduled jobs by Server Script if self.event_frequency or self.cron_format has changed""" + if ( + self.script_type == "Scheduler Event" + and (self.has_value_changed("event_frequency") or self.has_value_changed("cron_format")) + ) or (self.has_value_changed("script_type") and self.script_type != "Scheduler Event"): + for scheduled_job in self.scheduled_jobs: + xhiveframework.delete_doc("Scheduled Job Type", scheduled_job.name, delete_permanently=1) + + def check_if_compilable_in_restricted_context(self): + """Check compilation errors and send them back as warnings.""" + from RestrictedPython import compile_restricted + + try: + compile_restricted(self.script, policy=XhiveFrameworkTransformer) + except Exception as e: + xhiveframework.msgprint(str(e), title=_("Compilation warning")) + + def execute_method(self) -> dict: + """Specific to API endpoint Server Scripts + + Raises: + xhiveframework.DoesNotExistError: If self.script_type is not API + xhiveframework.PermissionError: If self.allow_guest is unset for API accessed by Guest user + + Returns: + dict: Evaluates self.script with xhiveframework.utils.safe_exec.safe_exec and returns the flags set in it's safe globals + """ + + if self.enable_rate_limit: + # Wrap in rate limiter, required for specifying custom limits for each script + # Note that rate limiter works on `cmd` which is script name + limit = self.rate_limit_count or 5 + seconds = self.rate_limit_seconds or 24 * 60 * 60 + + _fn = partial(execute_api_server_script, script=self) + return rate_limit(limit=limit, seconds=seconds)(_fn)() + else: + return execute_api_server_script(self) + + def execute_doc(self, doc: Document): + """Specific to Document Event triggered Server Scripts + + Args: + doc (Document): Executes script with for a certain document's events + """ + safe_exec( + self.script, + _locals={"doc": doc}, + restrict_commit_rollback=True, + script_filename=self.name, + ) + + def execute_scheduled_method(self): + """Specific to Scheduled Jobs via Server Scripts + + Raises: + xhiveframework.DoesNotExistError: If script type is not a scheduler event + """ + if self.script_type != "Scheduler Event": + raise xhiveframework.DoesNotExistError + + safe_exec(self.script, script_filename=self.name) + + def get_permission_query_conditions(self, user: str) -> list[str]: + """Specific to Permission Query Server Scripts + + Args: + user (str): Takes user email to execute script and return list of conditions + + Returns: + list: Returns list of conditions defined by rules in self.script + """ + locals = {"user": user, "conditions": ""} + safe_exec(self.script, None, locals, script_filename=self.name) + if locals["conditions"]: + return locals["conditions"] + + @xhiveframework.whitelist() + def get_autocompletion_items(self): + """Generates a list of a autocompletion strings from the context dict + that is used while executing a Server Script. + + Returns: + list: Returns list of autocompletion items. + For e.g., ["xhiveframework.utils.cint", "xhiveframework.get_all", ...] + """ + + def get_keys(obj): + out = [] + for key in obj: + if key.startswith("_"): + continue + value = obj[key] + if isinstance(value, NamespaceDict | dict) and value: + if key == "form_dict": + out.append(["form_dict", 7]) + continue + for subkey, score in get_keys(value): + fullkey = f"{key}.{subkey}" + out.append([fullkey, score]) + else: + if isinstance(value, type) and issubclass(value, Exception): + score = 0 + elif isinstance(value, ModuleType): + score = 10 + elif isinstance(value, FunctionType | MethodType): + score = 9 + elif isinstance(value, type): + score = 8 + elif isinstance(value, dict): + score = 7 + else: + score = 6 + out.append([key, score]) + return out + + items = xhiveframework.cache.get_value("server_script_autocompletion_items") + if not items: + items = get_keys(get_safe_globals()) + items = [{"value": d[0], "score": d[1]} for d in items] + xhiveframework.cache.set_value("server_script_autocompletion_items", items) + return items + + +def setup_scheduler_events(script_name: str, frequency: str, cron_format: str | None = None): + """Creates or Updates Scheduled Job Type documents based on the specified script name and frequency + + Args: + script_name (str): Name of the Server Script document + frequency (str): Event label compatible with the XhiveFramework scheduler + """ + method = xhiveframework.scrub(f"{script_name}-{frequency}") + scheduled_script = xhiveframework.db.get_value("Scheduled Job Type", {"method": method}) + + if not scheduled_script: + xhiveframework.get_doc( + { + "doctype": "Scheduled Job Type", + "method": method, + "frequency": frequency, + "server_script": script_name, + "cron_format": cron_format, + } + ).insert() + + xhiveframework.msgprint(_("Enabled scheduled execution for script {0}").format(script_name)) + + else: + doc = xhiveframework.get_doc("Scheduled Job Type", scheduled_script) + + if doc.frequency == frequency: + return + + doc.frequency = frequency + doc.cron_format = cron_format + doc.save() + + xhiveframework.msgprint(_("Scheduled execution for script {0} has updated").format(script_name)) + + +def execute_api_server_script(script=None, *args, **kwargs): + # These are only added for compatibility with rate limiter. + del args + del kwargs + + if script.script_type != "API": + raise xhiveframework.DoesNotExistError + + # validate if guest is allowed + if xhiveframework.session.user == "Guest" and not script.allow_guest: + raise xhiveframework.PermissionError + + # output can be stored in flags + _globals, _locals = safe_exec(script.script, script_filename=script.name) + + return _globals.xhiveframework.flags + + +@xhiveframework.whitelist() +def enabled() -> bool | None: + if xhiveframework.has_permission("Server Script"): + return is_safe_exec_enabled() diff --git a/xhiveframework/core/doctype/server_script/server_script_list.js b/xhiveframework/core/doctype/server_script/server_script_list.js new file mode 100644 index 0000000..62433fe --- /dev/null +++ b/xhiveframework/core/doctype/server_script/server_script_list.js @@ -0,0 +1,38 @@ +xhiveframework.listview_settings["Server Script"] = { + onload: function (listview) { + add_github_star_cta(listview); + }, +}; + +function add_github_star_cta(listview) { + try { + const key = "show_github_star_banner"; + if (localStorage.getItem(key) == "false") { + return; + } + + if (listview.github_star_banner) { + listview.github_star_banner.remove(); + } + + const message = "Loving Xhive Framework?"; + const link = "https://lab.membtech.com/xhiveframework/xhiveframework15"; + const cta = "Star us on GitHub"; + + listview.github_star_banner = $(` +
    +
    + ${message}
    ${cta} → +
    +
    + + + +
    +
    + `).appendTo(listview.page.sidebar); + } catch (error) { + console.error(error); + } +} diff --git a/xhiveframework/core/doctype/server_script/server_script_utils.py b/xhiveframework/core/doctype/server_script/server_script_utils.py new file mode 100644 index 0000000..b0f4685 --- /dev/null +++ b/xhiveframework/core/doctype/server_script/server_script_utils.py @@ -0,0 +1,80 @@ +import xhiveframework + +# this is a separate file since it is imported in xhiveframework.model.document +# to avoid circular imports + +EVENT_MAP = { + "before_insert": "Before Insert", + "after_insert": "After Insert", + "before_validate": "Before Validate", + "validate": "Before Save", + "on_update": "After Save", + "before_rename": "Before Rename", + "after_rename": "After Rename", + "before_submit": "Before Submit", + "on_submit": "After Submit", + "before_cancel": "Before Cancel", + "on_cancel": "After Cancel", + "on_trash": "Before Delete", + "after_delete": "After Delete", + "before_update_after_submit": "Before Save (Submitted Document)", + "on_update_after_submit": "After Save (Submitted Document)", + "on_payment_authorized": "On Payment Authorization", +} + + +def run_server_script_for_doc_event(doc, event): + # run document event method + if event not in EVENT_MAP: + return + + if xhiveframework.flags.in_install: + return + + if xhiveframework.flags.in_migrate: + return + + scripts = get_server_script_map().get(doc.doctype, {}).get(EVENT_MAP[event], None) + if scripts: + # run all scripts for this doctype + event + for script_name in scripts: + xhiveframework.get_doc("Server Script", script_name).execute_doc(doc) + + +def get_server_script_map(): + # fetch cached server script methods + # { + # '[doctype]': { + # 'Before Insert': ['[server script 1]', '[server script 2]'] + # }, + # '_api': { + # '[path]': '[server script]' + # }, + # 'permission_query': { + # 'DocType': '[server script]' + # } + # } + if xhiveframework.flags.in_patch and not xhiveframework.db.table_exists("Server Script"): + return {} + + script_map = xhiveframework.cache.get_value("server_script_map") + if script_map is None: + script_map = {"permission_query": {}} + enabled_server_scripts = xhiveframework.get_all( + "Server Script", + fields=("name", "reference_doctype", "doctype_event", "api_method", "script_type"), + filters={"disabled": 0}, + ) + for script in enabled_server_scripts: + if script.script_type == "DocType Event": + script_map.setdefault(script.reference_doctype, {}).setdefault( + script.doctype_event, [] + ).append(script.name) + elif script.script_type == "Permission Query": + script_map["permission_query"][script.reference_doctype] = script.name + else: + script_map.setdefault("_api", {})[script.api_method] = script.name + + xhiveframework.cache.set_value("server_script_map", script_map) + + return script_map diff --git a/xhiveframework/core/doctype/server_script/test_server_script.py b/xhiveframework/core/doctype/server_script/test_server_script.py new file mode 100644 index 0000000..a02f024 --- /dev/null +++ b/xhiveframework/core/doctype/server_script/test_server_script.py @@ -0,0 +1,342 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import requests + +import xhiveframework +from xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs +from xhiveframework.xhiveframeworkclient import XhiveFrameworkClient, XhiveFrameworkException +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import get_site_url + +scripts = [ + dict( + name="test_todo", + script_type="DocType Event", + doctype_event="Before Insert", + reference_doctype="ToDo", + script=""" +if "test" in doc.description: + doc.status = 'Closed' +""", + ), + dict( + name="test_todo_validate", + script_type="DocType Event", + doctype_event="Before Insert", + reference_doctype="ToDo", + script=""" +if "validate" in doc.description: + raise xhiveframework.ValidationError +""", + ), + dict( + name="test_api", + script_type="API", + api_method="test_server_script", + allow_guest=1, + script=""" +xhiveframework.response['message'] = 'hello' +""", + ), + dict( + name="test_return_value", + script_type="API", + api_method="test_return_value", + allow_guest=1, + script=""" +xhiveframework.flags = 'hello' +""", + ), + dict( + name="test_permission_query", + script_type="Permission Query", + reference_doctype="ToDo", + script=""" +conditions = '1 = 1' +""", + ), + dict( + name="test_invalid_namespace_method", + script_type="DocType Event", + doctype_event="Before Insert", + reference_doctype="Note", + script=""" +xhiveframework.method_that_doesnt_exist("do some magic") +""", + ), + dict( + name="test_todo_commit", + script_type="DocType Event", + doctype_event="Before Save", + reference_doctype="ToDo", + disabled=1, + script=""" +xhiveframework.db.commit() +""", + ), + dict( + name="test_add_index", + script_type="DocType Event", + doctype_event="Before Save", + reference_doctype="ToDo", + disabled=1, + script=""" +xhiveframework.db.add_index("Todo", ["color", "date"]) +""", + ), + dict( + name="test_before_rename", + script_type="DocType Event", + doctype_event="After Rename", + reference_doctype="Role", + script=""" +doc.desk_access =0 +doc.save() +""", + ), + dict( + name="test_after_rename", + script_type="DocType Event", + doctype_event="After Rename", + reference_doctype="Role", + script=""" +doc.disabled =1 +doc.save() +""", + ), +] + + +class TestServerScript(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + xhiveframework.db.truncate("Server Script") + xhiveframework.get_doc("User", "Administrator").add_roles("Script Manager") + for script in scripts: + script_doc = xhiveframework.get_doc(doctype="Server Script") + script_doc.update(script) + script_doc.insert() + cls.enable_safe_exec() + xhiveframework.db.commit() + return super().setUpClass() + + @classmethod + def tearDownClass(cls): + xhiveframework.db.commit() + xhiveframework.db.truncate("Server Script") + xhiveframework.cache.delete_value("server_script_map") + + def setUp(self): + xhiveframework.cache.delete_value("server_script_map") + + def test_doctype_event(self): + todo = xhiveframework.get_doc(dict(doctype="ToDo", description="hello")).insert() + self.assertEqual(todo.status, "Open") + + todo = xhiveframework.get_doc(dict(doctype="ToDo", description="test todo")).insert() + self.assertEqual(todo.status, "Closed") + + self.assertRaises( + xhiveframework.ValidationError, xhiveframework.get_doc(dict(doctype="ToDo", description="validate me")).insert + ) + + role = xhiveframework.get_doc(doctype="Role", role_name="_Test Role 9").insert(ignore_if_duplicate=True) + role.rename("_Test Role 10") + role.reload() + self.assertEqual(role.disabled, 1) + self.assertEqual(role.desk_access, 0) + + def test_api(self): + response = requests.post(get_site_url(xhiveframework.local.site) + "/api/method/test_server_script") + self.assertEqual(response.status_code, 200) + self.assertEqual("hello", response.json()["message"]) + + def test_api_return(self): + self.assertEqual(xhiveframework.get_doc("Server Script", "test_return_value").execute_method(), "hello") + + def test_permission_query(self): + if xhiveframework.conf.db_type == "mariadb": + self.assertTrue("where (1 = 1)" in xhiveframework.db.get_list("ToDo", run=False)) + else: + self.assertTrue("where (1 = '1')" in xhiveframework.db.get_list("ToDo", run=False)) + self.assertTrue(isinstance(xhiveframework.db.get_list("ToDo"), list)) + + def test_attribute_error(self): + """Raise AttributeError if method not found in Namespace""" + note = xhiveframework.get_doc({"doctype": "Note", "title": "Test Note: Server Script"}) + self.assertRaises(AttributeError, note.insert) + + def test_syntax_validation(self): + server_script = scripts[0] + server_script["script"] = "js || code.?" + + with self.assertRaises(xhiveframework.ValidationError) as se: + xhiveframework.get_doc(doctype="Server Script", **server_script).insert() + + self.assertTrue( + "invalid python code" in str(se.exception).lower(), msg="Python code validation not working" + ) + + def test_commit_in_doctype_event(self): + server_script = xhiveframework.get_doc("Server Script", "test_todo_commit") + server_script.disabled = 0 + server_script.save() + + self.assertRaises(AttributeError, xhiveframework.get_doc(dict(doctype="ToDo", description="test me")).insert) + + server_script.disabled = 1 + server_script.save() + + def test_add_index_in_doctype_event(self): + server_script = xhiveframework.get_doc("Server Script", "test_add_index") + server_script.disabled = 0 + server_script.save() + + self.assertRaises(AttributeError, xhiveframework.get_doc(dict(doctype="ToDo", description="test me")).insert) + + server_script.disabled = 1 + server_script.save() + + def test_restricted_qb(self): + todo = xhiveframework.get_doc(doctype="ToDo", description="QbScriptTestNote") + todo.insert() + + script = xhiveframework.get_doc( + doctype="Server Script", + name="test_qb_restrictions", + script_type="API", + api_method="test_qb_restrictions", + allow_guest=1, + # whitelisted update + script=f""" +xhiveframework.db.set_value("ToDo", "{todo.name}", "description", "safe") +""", + ) + script.insert() + script.execute_method() + + todo.reload() + self.assertEqual(todo.description, "safe") + + # unsafe update + script.script = f""" +todo = xhiveframework.qb.DocType("ToDo") +xhiveframework.qb.update(todo).set(todo.description, "unsafe").where(todo.name == "{todo.name}").run() +""" + script.save() + self.assertRaises(xhiveframework.PermissionError, script.execute_method) + todo.reload() + self.assertEqual(todo.description, "safe") + + # safe select + script.script = f""" +todo = xhiveframework.qb.DocType("ToDo") +xhiveframework.qb.from_(todo).select(todo.name).where(todo.name == "{todo.name}").run() +""" + script.save() + script.execute_method() + + def test_scripts_all_the_way_down(self): + # why not + script = xhiveframework.get_doc( + doctype="Server Script", + name="test_nested_scripts_1", + script_type="API", + api_method="test_nested_scripts_1", + script="""log("nothing")""", + ) + script.insert() + script.execute_method() + + script = xhiveframework.get_doc( + doctype="Server Script", + name="test_nested_scripts_2", + script_type="API", + api_method="test_nested_scripts_2", + script="""xhiveframework.call("test_nested_scripts_1")""", + ) + script.insert() + script.execute_method() + + def test_server_script_rate_limiting(self): + script1 = xhiveframework.get_doc( + doctype="Server Script", + name="rate_limited_server_script", + script_type="API", + enable_rate_limit=1, + allow_guest=1, + rate_limit_count=5, + api_method="rate_limited_endpoint", + script="""xhiveframework.flags = {"test": True}""", + ) + + script1.insert() + + script2 = xhiveframework.get_doc( + doctype="Server Script", + name="rate_limited_server_script2", + script_type="API", + enable_rate_limit=1, + allow_guest=1, + rate_limit_count=5, + api_method="rate_limited_endpoint2", + script="""xhiveframework.flags = {"test": False}""", + ) + + script2.insert() + + xhiveframework.db.commit() + + site = xhiveframework.utils.get_site_url(xhiveframework.local.site) + client = XhiveFrameworkClient(site) + + # Exhaust rate limit + for _ in range(5): + client.get_api(script1.api_method) + + self.assertRaises(XhiveFrameworkException, client.get_api, script1.api_method) + + # Exhaust rate limit + for _ in range(5): + client.get_api(script2.api_method) + + self.assertRaises(XhiveFrameworkException, client.get_api, script2.api_method) + + script1.delete() + script2.delete() + xhiveframework.db.commit() + + def test_server_script_scheduled(self): + scheduled_script = xhiveframework.get_doc( + doctype="Server Script", + name="scheduled_script_wo_cron", + script_type="Scheduler Event", + script="""xhiveframework.flags = {"test": True}""", + event_frequency="Hourly", + ).insert() + + cron_script = xhiveframework.get_doc( + doctype="Server Script", + name="scheduled_script_w_cron", + script_type="Scheduler Event", + script="""xhiveframework.flags = {"test": True}""", + event_frequency="Cron", + cron_format="0 0 1 1 *", # 1st january + ).insert() + + # Ensure that jobs remain in DB after migrate + sync_jobs() + self.assertTrue(xhiveframework.db.exists("Scheduled Job Type", {"server_script": scheduled_script.name})) + + cron_job_name = xhiveframework.db.get_value("Scheduled Job Type", {"server_script": cron_script.name}) + self.assertTrue(cron_job_name) + + cron_job = xhiveframework.get_doc("Scheduled Job Type", cron_job_name) + self.assertEqual(cron_job.next_execution.day, 1) + self.assertEqual(cron_job.next_execution.month, 1) + + cron_script.cron_format = "0 0 2 1 *" # 2nd january + cron_script.save() + cron_job.reload() + self.assertEqual(cron_job.next_execution.day, 2) diff --git a/xhiveframework/core/doctype/session_default/__init__.py b/xhiveframework/core/doctype/session_default/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/session_default/session_default.json b/xhiveframework/core/doctype/session_default/session_default.json new file mode 100644 index 0000000..d382e12 --- /dev/null +++ b/xhiveframework/core/doctype/session_default/session_default.json @@ -0,0 +1,29 @@ +{ + "creation": "2019-07-17 16:21:33.546379", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType" + } + ], + "istable": 1, + "modified": "2019-07-21 13:22:25.752553", + "modified_by": "Administrator", + "module": "Core", + "name": "Session Default", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/session_default/session_default.py b/xhiveframework/core/doctype/session_default/session_default.py new file mode 100644 index 0000000..3fd07ce --- /dev/null +++ b/xhiveframework/core/doctype/session_default/session_default.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class SessionDefault(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + ref_doctype: DF.Link | None + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/session_default_settings/__init__.py b/xhiveframework/core/doctype/session_default_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/session_default_settings/session_default_settings.js b/xhiveframework/core/doctype/session_default_settings/session_default_settings.js new file mode 100644 index 0000000..9af9d8e --- /dev/null +++ b/xhiveframework/core/doctype/session_default_settings/session_default_settings.js @@ -0,0 +1,15 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.ui.form.on("Session Default Settings", { + refresh: function (frm) { + frm.set_query("ref_doctype", "session_defaults", function () { + return { + filters: { + issingle: 0, + istable: 0, + }, + }; + }); + }, +}); diff --git a/xhiveframework/core/doctype/session_default_settings/session_default_settings.json b/xhiveframework/core/doctype/session_default_settings/session_default_settings.json new file mode 100644 index 0000000..3eeb3f4 --- /dev/null +++ b/xhiveframework/core/doctype/session_default_settings/session_default_settings.json @@ -0,0 +1,39 @@ +{ + "creation": "2019-07-17 16:22:31.300991", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "session_defaults" + ], + "fields": [ + { + "fieldname": "session_defaults", + "fieldtype": "Table", + "label": "Session Defaults", + "options": "Session Default" + } + ], + "issingle": 1, + "modified": "2019-07-19 16:04:33.971089", + "modified_by": "Administrator", + "module": "Core", + "name": "Session Default Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/session_default_settings/session_default_settings.py b/xhiveframework/core/doctype/session_default_settings/session_default_settings.py new file mode 100644 index 0000000..bf091c7 --- /dev/null +++ b/xhiveframework/core/doctype/session_default_settings/session_default_settings.py @@ -0,0 +1,59 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class SessionDefaultSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.session_default.session_default import SessionDefault + from xhiveframework.types import DF + + session_defaults: DF.Table[SessionDefault] + # end: auto-generated types + pass + + +@xhiveframework.whitelist() +def get_session_default_values(): + settings = xhiveframework.get_single("Session Default Settings") + fields = [] + for default_values in settings.session_defaults: + reference_doctype = xhiveframework.scrub(default_values.ref_doctype) + fields.append( + { + "fieldname": reference_doctype, + "fieldtype": "Link", + "options": default_values.ref_doctype, + "label": _("Default {0}").format(_(default_values.ref_doctype)), + "default": xhiveframework.defaults.get_user_default(reference_doctype), + } + ) + return json.dumps(fields) + + +@xhiveframework.whitelist() +def set_session_default_values(default_values): + default_values = xhiveframework.parse_json(default_values) + for entry in default_values: + try: + xhiveframework.defaults.set_user_default(entry, default_values.get(entry)) + except Exception: + return + return "success" + + +# called on hook 'on_logout' to clear defaults for the session +def clear_session_defaults(): + settings = xhiveframework.get_single("Session Default Settings").session_defaults + for entry in settings: + xhiveframework.defaults.clear_user_default(xhiveframework.scrub(entry.ref_doctype)) diff --git a/xhiveframework/core/doctype/session_default_settings/test_session_default_settings.py b/xhiveframework/core/doctype/session_default_settings/test_session_default_settings.py new file mode 100644 index 0000000..0b2f483 --- /dev/null +++ b/xhiveframework/core/doctype/session_default_settings/test_session_default_settings.py @@ -0,0 +1,31 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.session_default_settings.session_default_settings import ( + clear_session_defaults, + set_session_default_values, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSessionDefaultSettings(XhiveFrameworkTestCase): + def test_set_session_default_settings(self): + xhiveframework.set_user("Administrator") + settings = xhiveframework.get_single("Session Default Settings") + settings.session_defaults = [] + settings.append("session_defaults", {"ref_doctype": "Role"}) + settings.save() + + set_session_default_values({"role": "Website Manager"}) + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test session defaults set", assigned_by="Administrator") + ).insert() + self.assertEqual(todo.role, "Website Manager") + + def test_clear_session_defaults(self): + clear_session_defaults() + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test session defaults cleared", assigned_by="Administrator") + ).insert() + self.assertNotEqual(todo.role, "Website Manager") diff --git a/xhiveframework/core/doctype/sms_parameter/README.md b/xhiveframework/core/doctype/sms_parameter/README.md new file mode 100644 index 0000000..5935a39 --- /dev/null +++ b/xhiveframework/core/doctype/sms_parameter/README.md @@ -0,0 +1 @@ +SMS query parameter for SMS Settings. \ No newline at end of file diff --git a/xhiveframework/core/doctype/sms_parameter/__init__.py b/xhiveframework/core/doctype/sms_parameter/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/sms_parameter/sms_parameter.json b/xhiveframework/core/doctype/sms_parameter/sms_parameter.json new file mode 100755 index 0000000..98972f9 --- /dev/null +++ b/xhiveframework/core/doctype/sms_parameter/sms_parameter.json @@ -0,0 +1,51 @@ +{ + "actions": [], + "creation": "2013-02-22 01:27:58", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parameter", + "value", + "header" + ], + "fields": [ + { + "fieldname": "parameter", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Value", + "print_width": "150px", + "reqd": 1, + "width": "150px" + }, + { + "default": "0", + "fieldname": "header", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Header" + } + ], + "idx": 1, + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:53.129765", + "modified_by": "Administrator", + "module": "Core", + "name": "SMS Parameter", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/sms_parameter/sms_parameter.py b/xhiveframework/core/doctype/sms_parameter/sms_parameter.py new file mode 100644 index 0000000..c1ce7f8 --- /dev/null +++ b/xhiveframework/core/doctype/sms_parameter/sms_parameter.py @@ -0,0 +1,23 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class SMSParameter(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + header: DF.Check + parameter: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + value: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/sms_settings/README.md b/xhiveframework/core/doctype/sms_settings/README.md new file mode 100644 index 0000000..4fb4980 --- /dev/null +++ b/xhiveframework/core/doctype/sms_settings/README.md @@ -0,0 +1 @@ +Settings for automatically sending SMS from the system. \ No newline at end of file diff --git a/xhiveframework/core/doctype/sms_settings/__init__.py b/xhiveframework/core/doctype/sms_settings/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/sms_settings/sms_settings.js b/xhiveframework/core/doctype/sms_settings/sms_settings.js new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/sms_settings/sms_settings.json b/xhiveframework/core/doctype/sms_settings/sms_settings.json new file mode 100755 index 0000000..14ff324 --- /dev/null +++ b/xhiveframework/core/doctype/sms_settings/sms_settings.json @@ -0,0 +1,80 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2013-01-10 16:34:24", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "sms_gateway_url", + "message_parameter", + "receiver_parameter", + "static_parameters_section", + "parameters", + "use_post" + ], + "fields": [ + { + "description": "Eg. smsgateway.com/api/send_sms.cgi", + "fieldname": "sms_gateway_url", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "SMS Gateway URL", + "reqd": 1 + }, + { + "description": "Enter url parameter for message", + "fieldname": "message_parameter", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Message Parameter", + "reqd": 1 + }, + { + "description": "Enter url parameter for receiver nos", + "fieldname": "receiver_parameter", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Receiver Parameter", + "reqd": 1 + }, + { + "fieldname": "static_parameters_section", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "description": "Enter static url parameters here (Eg. sender=XhiveERP, username=XhiveERP, password=1234 etc.)", + "fieldname": "parameters", + "fieldtype": "Table", + "label": "Static Parameters", + "options": "SMS Parameter" + }, + { + "default": "0", + "fieldname": "use_post", + "fieldtype": "Check", + "label": "Use POST" + } + ], + "icon": "fa fa-cog", + "idx": 1, + "issingle": 1, + "links": [], + "modified": "2021-09-21 19:45:26.809793", + "modified_by": "Administrator", + "module": "Core", + "name": "SMS Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/sms_settings/sms_settings.py b/xhiveframework/core/doctype/sms_settings/sms_settings.py new file mode 100644 index 0000000..320e3c5 --- /dev/null +++ b/xhiveframework/core/doctype/sms_settings/sms_settings.py @@ -0,0 +1,157 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _, msgprint, throw +from xhiveframework.model.document import Document +from xhiveframework.utils import nowdate + + +class SMSSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.sms_parameter.sms_parameter import SMSParameter + from xhiveframework.types import DF + + message_parameter: DF.Data + parameters: DF.Table[SMSParameter] + receiver_parameter: DF.Data + sms_gateway_url: DF.SmallText + use_post: DF.Check + # end: auto-generated types + pass + + +def validate_receiver_nos(receiver_list): + validated_receiver_list = [] + for d in receiver_list: + if not d: + continue + + # remove invalid character + for x in [" ", "-", "(", ")"]: + d = d.replace(x, "") + + validated_receiver_list.append(d) + + if not validated_receiver_list: + throw(_("Please enter valid mobile nos")) + + return validated_receiver_list + + +@xhiveframework.whitelist() +def get_contact_number(contact_name, ref_doctype, ref_name): + "returns mobile number of the contact" + number = xhiveframework.db.sql( + """select mobile_no, phone from tabContact + where name=%s + and exists( + select name from `tabDynamic Link` where link_doctype=%s and link_name=%s + ) + """, + (contact_name, ref_doctype, ref_name), + ) + + return number and (number[0][0] or number[0][1]) or "" + + +@xhiveframework.whitelist() +def send_sms(receiver_list, msg, sender_name="", success_msg=True): + import json + + if isinstance(receiver_list, str): + receiver_list = json.loads(receiver_list) + if not isinstance(receiver_list, list): + receiver_list = [receiver_list] + + receiver_list = validate_receiver_nos(receiver_list) + + arg = { + "receiver_list": receiver_list, + "message": xhiveframework.safe_decode(msg).encode("utf-8"), + "success_msg": success_msg, + } + + if xhiveframework.db.get_single_value("SMS Settings", "sms_gateway_url"): + send_via_gateway(arg) + else: + msgprint(_("Please Update SMS Settings")) + + +def send_via_gateway(arg): + ss = xhiveframework.get_doc("SMS Settings", "SMS Settings") + headers = get_headers(ss) + use_json = headers.get("Content-Type") == "application/json" + + message = xhiveframework.safe_decode(arg.get("message")) + args = {ss.message_parameter: message} + for d in ss.get("parameters"): + if not d.header: + args[d.parameter] = d.value + + success_list = [] + for d in arg.get("receiver_list"): + args[ss.receiver_parameter] = d + status = send_request(ss.sms_gateway_url, args, headers, ss.use_post, use_json) + + if 200 <= status < 300: + success_list.append(d) + + if len(success_list) > 0: + args.update(arg) + create_sms_log(args, success_list) + if arg.get("success_msg"): + xhiveframework.msgprint(_("SMS sent to following numbers: {0}").format("\n" + "\n".join(success_list))) + + +def get_headers(sms_settings=None): + if not sms_settings: + sms_settings = xhiveframework.get_doc("SMS Settings", "SMS Settings") + + headers = {"Accept": "text/plain, text/html, */*"} + for d in sms_settings.get("parameters"): + if d.header == 1: + headers.update({d.parameter: d.value}) + + return headers + + +def send_request(gateway_url, params, headers=None, use_post=False, use_json=False): + import requests + + if not headers: + headers = get_headers() + kwargs = {"headers": headers} + + if use_json: + kwargs["json"] = params + elif use_post: + kwargs["data"] = params + else: + kwargs["params"] = params + + if use_post: + response = requests.post(gateway_url, **kwargs) + else: + response = requests.get(gateway_url, **kwargs) + response.raise_for_status() + return response.status_code + + +# Create SMS Log +# ========================================================= +def create_sms_log(args, sent_to): + sl = xhiveframework.new_doc("SMS Log") + sl.sent_on = nowdate() + sl.message = args["message"].decode("utf-8") + sl.no_of_requested_sms = len(args["receiver_list"]) + sl.requested_numbers = "\n".join(args["receiver_list"]) + sl.no_of_sent_sms = len(sent_to) + sl.sent_to = "\n".join(sent_to) + sl.flags.ignore_permissions = True + sl.save() diff --git a/xhiveframework/core/doctype/sms_settings/test_sms_settings.py b/xhiveframework/core/doctype/sms_settings/test_sms_settings.py new file mode 100644 index 0000000..c611589 --- /dev/null +++ b/xhiveframework/core/doctype/sms_settings/test_sms_settings.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSMSSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/submission_queue/__init__.py b/xhiveframework/core/doctype/submission_queue/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/submission_queue/submission_queue.js b/xhiveframework/core/doctype/submission_queue/submission_queue.js new file mode 100644 index 0000000..8314b6b --- /dev/null +++ b/xhiveframework/core/doctype/submission_queue/submission_queue.js @@ -0,0 +1,20 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Submission Queue", { + refresh: function (frm) { + if (frm.doc.status === "Queued" && xhiveframework.boot.user.roles.includes("System Manager")) { + frm.add_custom_button(__("Unlock Reference Document"), () => { + xhiveframework.confirm( + ` + Are you sure you want to go ahead with this action? + Doing this could unlock other submissions of this document which are in queue (if present) + and could lead to non-ideal conditions.`, + () => { + frm.call("unlock_doc"); + } + ); + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/submission_queue/submission_queue.json b/xhiveframework/core/doctype/submission_queue/submission_queue.json new file mode 100644 index 0000000..4e4abce --- /dev/null +++ b/xhiveframework/core/doctype/submission_queue/submission_queue.json @@ -0,0 +1,130 @@ +{ + "actions": [], + "autoname": "hash", + "beta": 1, + "creation": "2022-10-04 00:41:00.028163", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status", + "created_at", + "enqueued_by", + "job_id", + "column_break_5", + "ended_at", + "ref_doctype", + "ref_docname", + "section_break_8", + "exception" + ], + "fields": [ + { + "fieldname": "job_id", + "fieldtype": "Link", + "label": "Job Id", + "options": "RQ Job", + "read_only": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference DocType", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Docname", + "options": "ref_doctype", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Status", + "options": "Queued\nFinished\nFailed", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "enqueued_by", + "fieldtype": "Data", + "is_virtual": 1, + "label": "Enqueued By", + "read_only": 1 + }, + { + "fieldname": "ended_at", + "fieldtype": "Datetime", + "label": "Ended At", + "read_only": 1 + }, + { + "fieldname": "created_at", + "fieldtype": "Datetime", + "is_virtual": 1, + "label": "Created At", + "read_only": 1 + }, + { + "fieldname": "exception", + "fieldtype": "Long Text", + "label": "Exception", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-29 12:27:54.754054", + "modified_by": "Administrator", + "module": "Core", + "name": "Submission Queue", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, + { + "if_owner": 1, + "read": 1, + "role": "Desk User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [ + { + "color": "Blue", + "title": "Queued" + }, + { + "color": "Red", + "title": "Failed" + }, + { + "color": "Green", + "title": "Finished" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/submission_queue/submission_queue.py b/xhiveframework/core/doctype/submission_queue/submission_queue.py new file mode 100644 index 0000000..6c888c9 --- /dev/null +++ b/xhiveframework/core/doctype/submission_queue/submission_queue.py @@ -0,0 +1,208 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +from urllib.parse import quote + +from rq import get_current_job + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.doctype.notification_log.notification_log import enqueue_create_notification +from xhiveframework.model.document import Document +from xhiveframework.monitor import add_data_to_monitor +from xhiveframework.utils import now, time_diff_in_seconds +from xhiveframework.utils.data import cint + + +class SubmissionQueue(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + created_at: DF.Datetime | None + ended_at: DF.Datetime | None + enqueued_by: DF.Data | None + exception: DF.LongText | None + job_id: DF.Link | None + ref_docname: DF.DynamicLink | None + ref_doctype: DF.Link | None + status: DF.Literal["Queued", "Finished", "Failed"] + + # end: auto-generated types + @property + def created_at(self): + return self.creation + + @property + def enqueued_by(self): + return self.owner + + @property + def queued_doc(self): + return getattr(self, "to_be_queued_doc", xhiveframework.get_doc(self.ref_doctype, self.ref_docname)) + + @staticmethod + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Submission Queue") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + def insert(self, to_be_queued_doc: Document, action: str): + self.status = "Queued" + self.to_be_queued_doc = to_be_queued_doc + self.action_for_queuing = action + super().insert(ignore_permissions=True) + + def lock(self): + self.queued_doc.lock() + + def unlock(self): + self.queued_doc.unlock() + + def update_job_id(self, job_id): + xhiveframework.db.set_value( + self.doctype, + self.name, + {"job_id": job_id}, + update_modified=False, + ) + xhiveframework.db.commit() + + def after_insert(self): + self.queue_action( + "background_submission", + to_be_queued_doc=self.queued_doc, + action_for_queuing=self.action_for_queuing, + timeout=600, + enqueue_after_commit=True, + ) + + def background_submission(self, to_be_queued_doc: Document, action_for_queuing: str): + # Set the job id for that submission doctype + self.update_job_id(get_current_job().id) + + _action = action_for_queuing.lower() + if _action == "update": + _action = "submit" + + try: + getattr(to_be_queued_doc, _action)() + add_data_to_monitor( + doctype=to_be_queued_doc.doctype, + docname=to_be_queued_doc.name, + action=_action, + execution_time=time_diff_in_seconds(now(), self.created_at), + enqueued_by=self.enqueued_by, + ) + values = {"status": "Finished"} + except Exception: + values = {"status": "Failed", "exception": xhiveframework.get_traceback(with_context=True)} + xhiveframework.db.rollback() + + values["ended_at"] = now() + xhiveframework.db.set_value(self.doctype, self.name, values, update_modified=False) + self.notify(values["status"], action_for_queuing) + + def notify(self, submission_status: str, action: str): + if submission_status == "Failed": + doctype = self.doctype + docname = self.name + message = _("Action {0} failed on {1} {2}. View it {3}") + else: + doctype = self.ref_doctype + docname = self.ref_docname + message = _("Action {0} completed successfully on {1} {2}. View it {3}") + + message_replacements = ( + xhiveframework.bold(action), + xhiveframework.bold(str(self.ref_doctype)), + xhiveframework.bold(str(self.ref_docname)), + ) + + time_diff = time_diff_in_seconds(now(), self.created_at) + if cint(time_diff) <= 60: + xhiveframework.publish_realtime( + "msgprint", + { + "message": message.format( + *message_replacements, + f"here", + ), + "alert": True, + "indicator": "red" if submission_status == "Failed" else "green", + }, + user=self.enqueued_by, + ) + else: + notification_doc = { + "type": "Alert", + "document_type": doctype, + "document_name": docname, + "subject": message.format(*message_replacements, "here"), + } + + notify_to = xhiveframework.db.get_value("User", self.enqueued_by, fieldname="email") + enqueue_create_notification([notify_to], notification_doc) + + @xhiveframework.whitelist() + def unlock_doc(self): + # NOTE: this can lead to some weird unlocking/locking behaviours. + # for example: hitting unlock on a submission could lead to unlocking of another submission + # of the same reference document. + + if self.status != "Queued": + return + + self.queued_doc.unlock() + xhiveframework.msgprint(_("Document Unlocked")) + + +def queue_submission(doc: Document, action: str, alert: bool = True): + queue = xhiveframework.new_doc("Submission Queue") + queue.ref_doctype = doc.doctype + queue.ref_docname = doc.name + queue.insert(doc, action) + + if alert: + xhiveframework.msgprint( + _("Queued for Submission. You can track the progress over {0}.").format( + f"here" + ), + indicator="green", + alert=True, + ) + + +@xhiveframework.whitelist() +def get_latest_submissions(doctype, docname): + # NOTE: not used creation as orderby intentianlly as we have used update_modified=False everywhere + # hence assuming modified will be equal to creation for submission queue documents + + latest_submission = xhiveframework.db.get_value( + "Submission Queue", + filters={"ref_doctype": doctype, "ref_docname": docname}, + fieldname=["name", "exception", "status"], + ) + + out = None + if latest_submission: + out = { + "latest_submission": latest_submission[0], + "exc": format_tb(latest_submission[1]), + "status": latest_submission[2], + } + + return out + + +def format_tb(traceback: str | None = None): + if not traceback: + return + + return traceback.strip().split("\n")[-1] diff --git a/xhiveframework/core/doctype/submission_queue/test_submission_queue.py b/xhiveframework/core/doctype/submission_queue/test_submission_queue.py new file mode 100644 index 0000000..8616903 --- /dev/null +++ b/xhiveframework/core/doctype/submission_queue/test_submission_queue.py @@ -0,0 +1,51 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# See license.txt + +import time +import typing + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase, timeout +from xhiveframework.utils.background_jobs import get_queue + +if typing.TYPE_CHECKING: + from rq.job import Job + + +class TestSubmissionQueue(XhiveFrameworkTestCase): + queue = get_queue(qtype="default") + + @timeout(seconds=20) + def check_status(self, job: "Job", status, wait=True): + if wait: + while True: + if job.is_queued or job.is_started: + time.sleep(0.2) + else: + break + self.assertEqual(xhiveframework.get_doc("RQ Job", job.id).status, status) + + def test_queue_operation(self): + from xhiveframework.core.doctype.doctype.test_doctype import new_doctype + from xhiveframework.core.doctype.submission_queue.submission_queue import queue_submission + + if not xhiveframework.db.table_exists("Test Submission Queue", cached=False): + doc = new_doctype("Test Submission Queue", is_submittable=True, queue_in_background=True) + doc.insert() + + d = xhiveframework.new_doc("Test Submission Queue") + d.update({"some_fieldname": "Random"}) + d.insert() + + xhiveframework.db.commit() + queue_submission(d, "submit") + xhiveframework.db.commit() + + # Waiting for execution + time.sleep(4) + submission_queue = xhiveframework.get_last_doc("Submission Queue") + + # Test queueing / starting + job = self.queue.fetch_job(submission_queue.job_id) + # Test completion + self.check_status(job, status="finished") diff --git a/xhiveframework/core/doctype/success_action/__init__.py b/xhiveframework/core/doctype/success_action/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/success_action/success_action.js b/xhiveframework/core/doctype/success_action/success_action.js new file mode 100644 index 0000000..b350623 --- /dev/null +++ b/xhiveframework/core/doctype/success_action/success_action.js @@ -0,0 +1,60 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Success Action", { + on_load: (frm) => { + if (!frm.action_multicheck) { + frm.trigger("set_next_action_multicheck"); + } + }, + refresh: (frm) => { + if (!frm.action_multicheck) { + frm.trigger("set_next_action_multicheck"); + } + }, + validate: (frm) => { + const checked_actions = frm.action_multicheck.get_checked_options(); + if (checked_actions.length < 2) { + xhiveframework.msgprint(__("Select atleast 2 actions")); + } else { + return true; + } + }, + before_save: (frm) => { + const checked_actions = frm.action_multicheck.get_checked_options(); + frm.doc.next_actions = checked_actions.join("\n"); + }, + after_save: (frm) => { + xhiveframework.boot.success_action.push(frm.doc); + //TODO: update success action cache on record update and delete + }, + set_next_action_multicheck: (frm) => { + const next_actions_wrapper = frm.fields_dict.next_actions_html.$wrapper; + const checked_actions = frm.doc.next_actions ? frm.doc.next_actions.split("\n") : []; + const action_multicheck_options = get_default_next_actions().map((action) => { + return { + label: action.label, + value: action.value, + checked: checked_actions.length ? checked_actions.includes(action.value) : 1, + }; + }); + frm.action_multicheck = xhiveframework.ui.form.make_control({ + parent: next_actions_wrapper, + df: { + label: "Next Actions", + fieldname: "next_actions_multicheck", + fieldtype: "MultiCheck", + options: action_multicheck_options, + }, + }); + }, +}); + +const get_default_next_actions = () => { + return [ + { label: __("New"), value: "new" }, + { label: __("Print"), value: "print" }, + { label: __("Email"), value: "email" }, + { label: __("View All"), value: "list" }, + ]; +}; diff --git a/xhiveframework/core/doctype/success_action/success_action.json b/xhiveframework/core/doctype/success_action/success_action.json new file mode 100644 index 0000000..749fa67 --- /dev/null +++ b/xhiveframework/core/doctype/success_action/success_action.json @@ -0,0 +1,84 @@ +{ + "actions": [], + "autoname": "field:ref_doctype", + "creation": "2018-04-15 18:07:35.316870", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "first_success_message", + "message", + "next_actions_html", + "next_actions", + "action_timeout" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1, + "unique": 1 + }, + { + "default": "Congratulations on first creations", + "fieldname": "first_success_message", + "fieldtype": "Data", + "in_list_view": 1, + "label": "First Success Message", + "reqd": 1 + }, + { + "default": "Successfully created", + "fieldname": "message", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Message", + "reqd": 1 + }, + { + "fieldname": "next_actions_html", + "fieldtype": "HTML", + "label": "Next Actions HTML" + }, + { + "fieldname": "next_actions", + "fieldtype": "Data", + "hidden": 1 + }, + { + "default": "7", + "fieldname": "action_timeout", + "fieldtype": "Int", + "label": "Action Timeout (Seconds)" + } + ], + "hide_toolbar": 1, + "links": [], + "modified": "2022-08-03 12:20:54.532708", + "modified_by": "Administrator", + "module": "Core", + "name": "Success Action", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/success_action/success_action.py b/xhiveframework/core/doctype/success_action/success_action.py new file mode 100644 index 0000000..b6e846f --- /dev/null +++ b/xhiveframework/core/doctype/success_action/success_action.py @@ -0,0 +1,22 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class SuccessAction(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action_timeout: DF.Int + first_success_message: DF.Data + message: DF.Data + next_actions: DF.Data | None + ref_doctype: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/system_settings/__init__.py b/xhiveframework/core/doctype/system_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/system_settings/system_settings.js b/xhiveframework/core/doctype/system_settings/system_settings.js new file mode 100644 index 0000000..a4b6ee6 --- /dev/null +++ b/xhiveframework/core/doctype/system_settings/system_settings.js @@ -0,0 +1,89 @@ +xhiveframework.ui.form.on("System Settings", { + refresh: function (frm) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.system_settings.system_settings.load", + callback: function (data) { + xhiveframework.all_timezones = data.message.timezones; + frm.set_df_property("time_zone", "options", xhiveframework.all_timezones); + + $.each(data.message.defaults, function (key, val) { + frm.set_value(key, val, null, true); + xhiveframework.sys_defaults[key] = val; + }); + if (frm.re_setup_moment) { + xhiveframework.app.setup_moment(); + delete frm.re_setup_moment; + } + }, + }); + + frm.trigger("set_rounding_method_options"); + }, + enable_password_policy: function (frm) { + if (frm.doc.enable_password_policy == 0) { + frm.set_value("minimum_password_score", ""); + } else { + frm.set_value("minimum_password_score", "2"); + } + }, + enable_two_factor_auth: function (frm) { + if (frm.doc.enable_two_factor_auth == 0) { + frm.set_value("bypass_2fa_for_retricted_ip_users", 0); + frm.set_value("bypass_restrict_ip_check_if_2fa_enabled", 0); + } + }, + after_save: function (frm) { + /** + * Checks whether the effective value has changed. + * + * @param {Array.} - Tuple with new fallback, previous fallback and + * optionally an override value. + * @returns {boolean} - Whether the resulting value has effectively changed + */ + const has_effectively_changed = ([new_fallback, prev_fallback, override = undefined]) => + !override && prev_fallback !== new_fallback; + + const attr_tuples = [ + [frm.doc.language, xhiveframework.boot.sysdefaults.language, xhiveframework.boot.user.language], + [frm.doc.rounding_method, xhiveframework.boot.sysdefaults.rounding_method], // no user override. + ]; + + if (attr_tuples.some(has_effectively_changed)) { + xhiveframework.msgprint(__("Refreshing...")); + window.location.reload(); + } + }, + first_day_of_the_week(frm) { + frm.re_setup_moment = true; + }, + + rounding_method: function (frm) { + if (frm.doc.rounding_method == xhiveframework.boot.sysdefaults.rounding_method) return; + let msg = __( + "Changing rounding method on site with data can result in unexpected behaviour." + ); + msg += "
    "; + msg += __("Do you still want to proceed?"); + + xhiveframework.confirm( + msg, + () => {}, + () => { + frm.set_value("rounding_method", xhiveframework.boot.sysdefaults.rounding_method); + } + ); + }, + + set_rounding_method_options: function (frm) { + if (frm.doc.rounding_method != "Banker's Rounding (legacy)") { + let field = frm.fields_dict.rounding_method; + + field.df.options = field.df.options + .split("\n") + .filter((o) => o != "Banker's Rounding (legacy)") + .join("\n"); + + field.refresh(); + } + }, +}); diff --git a/xhiveframework/core/doctype/system_settings/system_settings.json b/xhiveframework/core/doctype/system_settings/system_settings.json new file mode 100644 index 0000000..ae12672 --- /dev/null +++ b/xhiveframework/core/doctype/system_settings/system_settings.json @@ -0,0 +1,683 @@ +{ + "actions": [], + "creation": "2022-01-06 03:18:16.326761", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "localization", + "app_name", + "country", + "language", + "column_break_3", + "time_zone", + "enable_onboarding", + "setup_complete", + "disable_document_sharing", + "date_and_number_format", + "date_format", + "time_format", + "number_format", + "first_day_of_the_week", + "column_break_7", + "float_precision", + "currency_precision", + "rounding_method", + "permissions", + "apply_strict_user_permissions", + "column_break_21", + "allow_older_web_view_links", + "security_tab", + "security", + "session_expiry", + "document_share_key_expiry", + "column_break_txqh", + "deny_multiple_sessions", + "disable_user_pass_login", + "login_methods_section", + "allow_login_using_mobile_number", + "allow_login_using_user_name", + "column_break_uhqk", + "login_with_email_link", + "login_with_email_link_expiry", + "brute_force_security", + "allow_consecutive_login_attempts", + "column_break_34", + "allow_login_after_fail", + "two_factor_authentication", + "enable_two_factor_auth", + "bypass_2fa_for_retricted_ip_users", + "bypass_restrict_ip_check_if_2fa_enabled", + "two_factor_method", + "lifespan_qrcode_image", + "otp_issuer_name", + "password_tab", + "password_settings", + "logout_on_password_reset", + "force_user_to_reset_password", + "reset_password_link_expiry_duration", + "password_reset_limit", + "column_break_31", + "enable_password_policy", + "minimum_password_score", + "email_tab", + "email", + "email_footer_address", + "email_retry_limit", + "column_break_18", + "disable_standard_email_footer", + "hide_footer_in_auto_email_reports", + "attach_view_link", + "store_attached_pdf_document", + "welcome_email_template", + "reset_password_template", + "files_tab", + "files_section", + "max_file_size", + "allow_guests_to_upload_files", + "force_web_capture_mode_for_uploads", + "strip_exif_metadata_from_uploaded_images", + "column_break_uqma", + "allowed_file_extensions", + "updates_tab", + "system_updates_section", + "disable_system_update_notification", + "disable_change_log_notification", + "backups_tab", + "sec_backup_limit", + "backup_limit", + "encrypt_backup", + "advanced_tab", + "prepared_report_section", + "max_auto_email_report_per_user", + "background_workers", + "enable_scheduler", + "dormant_days", + "telemetry_section", + "allow_error_traceback", + "enable_telemetry", + "search_section", + "link_field_results_limit" + ], + "fields": [ + { + "fieldname": "localization", + "fieldtype": "Section Break" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Language", + "options": "Language", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "time_zone", + "fieldtype": "Select", + "label": "Time Zone", + "read_only": 1, + "reqd": 1 + }, + { + "default": "0", + "fieldname": "setup_complete", + "fieldtype": "Check", + "hidden": 1, + "label": "Setup Complete", + "read_only": 1 + }, + { + "fieldname": "date_and_number_format", + "fieldtype": "Section Break", + "label": "Date and Number Format" + }, + { + "fieldname": "date_format", + "fieldtype": "Select", + "label": "Date Format", + "options": "yyyy-mm-dd\ndd-mm-yyyy\ndd/mm/yyyy\ndd.mm.yyyy\nmm/dd/yyyy\nmm-dd-yyyy", + "reqd": 1 + }, + { + "default": "HH:mm:ss", + "fieldname": "time_format", + "fieldtype": "Select", + "label": "Time Format", + "options": "HH:mm:ss\nHH:mm", + "reqd": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "number_format", + "fieldtype": "Select", + "label": "Number Format", + "options": "#,###.##\n#.###,##\n# ###.##\n# ###,##\n#'###.##\n#, ###.##\n#,##,###.##\n#,###.###\n#.###\n#,###", + "reqd": 1 + }, + { + "fieldname": "float_precision", + "fieldtype": "Select", + "label": "Float Precision", + "options": "\n2\n3\n4\n5\n6\n7\n8\n9" + }, + { + "description": "If not set, the currency precision will depend on number format", + "fieldname": "currency_precision", + "fieldtype": "Select", + "label": "Currency Precision", + "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" + }, + { + "fieldname": "sec_backup_limit", + "fieldtype": "Section Break" + }, + { + "default": "3", + "description": "Older backups will be automatically deleted", + "fieldname": "backup_limit", + "fieldtype": "Int", + "label": "Number of Backups" + }, + { + "fieldname": "background_workers", + "fieldtype": "Section Break", + "label": "Background Workers" + }, + { + "default": "0", + "description": "Run scheduled jobs only if checked", + "fieldname": "enable_scheduler", + "fieldtype": "Check", + "hidden": 1, + "label": "Enable Scheduled Jobs" + }, + { + "fieldname": "permissions", + "fieldtype": "Section Break", + "label": "Permissions" + }, + { + "default": "0", + "description": "If Apply Strict User Permission is checked and User Permission is defined for a DocType for a User, then all the documents where value of the link is blank, will not be shown to that User", + "fieldname": "apply_strict_user_permissions", + "fieldtype": "Check", + "label": "Apply Strict User Permissions" + }, + { + "fieldname": "security", + "fieldtype": "Section Break" + }, + { + "default": "170:00", + "description": "Example: Setting this to 24:00 will log out a user if they are not active for 24:00 hours.", + "fieldname": "session_expiry", + "fieldtype": "Data", + "label": "Session Expiry (idle timeout)" + }, + { + "default": "0", + "description": "Note: Multiple sessions will be allowed in case of mobile device", + "fieldname": "deny_multiple_sessions", + "fieldtype": "Check", + "label": "Allow only one session per user" + }, + { + "default": "0", + "description": "User can login using Email id or Mobile number", + "fieldname": "allow_login_using_mobile_number", + "fieldtype": "Check", + "label": "Allow Login using Mobile Number" + }, + { + "default": "0", + "description": "User can login using Email id or User Name", + "fieldname": "allow_login_using_user_name", + "fieldtype": "Check", + "label": "Allow Login using User Name" + }, + { + "default": "1", + "fieldname": "allow_error_traceback", + "fieldtype": "Check", + "label": "Show Full Error and Allow Reporting of Issues to the Developer" + }, + { + "fieldname": "password_settings", + "fieldtype": "Section Break", + "label": "Password" + }, + { + "description": "In Days", + "fieldname": "force_user_to_reset_password", + "fieldtype": "Int", + "label": "Force User to Reset Password" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "default": "1", + "description": "If enabled, the password strength will be enforced based on the Minimum Password Score value. A value of 2 being medium strong and 4 being very strong.", + "fieldname": "enable_password_policy", + "fieldtype": "Check", + "label": "Enable Password Policy" + }, + { + "default": "2", + "depends_on": "eval:doc.enable_password_policy==1", + "fieldname": "minimum_password_score", + "fieldtype": "Select", + "label": "Minimum Password Score", + "options": "2\n3\n4" + }, + { + "fieldname": "brute_force_security", + "fieldtype": "Section Break", + "label": "Brute Force Security" + }, + { + "default": "10", + "fieldname": "allow_consecutive_login_attempts", + "fieldtype": "Int", + "label": "Allow Consecutive Login Attempts " + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" + }, + { + "default": "60", + "description": "In seconds", + "fieldname": "allow_login_after_fail", + "fieldtype": "Int", + "label": "Allow Login After Fail" + }, + { + "fieldname": "two_factor_authentication", + "fieldtype": "Section Break", + "label": "Two Factor Authentication" + }, + { + "default": "0", + "fieldname": "enable_two_factor_auth", + "fieldtype": "Check", + "label": "Enable Two Factor Auth" + }, + { + "default": "0", + "depends_on": "enable_two_factor_auth", + "description": "If enabled, users who login from Restricted IP Address, won't be prompted for Two Factor Auth", + "fieldname": "bypass_2fa_for_retricted_ip_users", + "fieldtype": "Check", + "label": "Bypass Two Factor Auth for users who login from restricted IP Address" + }, + { + "default": "0", + "depends_on": "enable_two_factor_auth", + "description": "If enabled, all users can login from any IP Address using Two Factor Auth. This can also be set only for specific user(s) in User Page", + "fieldname": "bypass_restrict_ip_check_if_2fa_enabled", + "fieldtype": "Check", + "label": "Bypass restricted IP Address check If Two Factor Auth Enabled" + }, + { + "default": "OTP App", + "depends_on": "enable_two_factor_auth", + "description": "Choose authentication method to be used by all users", + "fieldname": "two_factor_method", + "fieldtype": "Select", + "label": "Two Factor Authentication method", + "options": "OTP App\nSMS\nEmail" + }, + { + "depends_on": "eval:doc.enable_two_factor_auth && doc.two_factor_method == \"OTP App\"", + "description": "Time in seconds to retain QR code image on server. Min:240", + "fieldname": "lifespan_qrcode_image", + "fieldtype": "Int", + "label": "Expiry time of QR Code Image Page" + }, + { + "default": "Xhive Framework", + "depends_on": "enable_two_factor_auth", + "fieldname": "otp_issuer_name", + "fieldtype": "Data", + "label": "OTP Issuer Name" + }, + { + "fieldname": "email", + "fieldtype": "Section Break" + }, + { + "description": "Your organization name and address for the email footer.", + "fieldname": "email_footer_address", + "fieldtype": "Small Text", + "label": "Email Footer Address" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "disable_standard_email_footer", + "fieldtype": "Check", + "label": "Disable Standard Email Footer" + }, + { + "default": "0", + "fieldname": "hide_footer_in_auto_email_reports", + "fieldtype": "Check", + "label": "Hide footer in auto email reports" + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "When enabled this will allow guests to upload files to your application, You can enable this if you wish to collect files from user without having them to log in, for example in job applications web form.", + "fieldname": "allow_guests_to_upload_files", + "fieldtype": "Check", + "label": "Allow Guests to Upload Files" + }, + { + "default": "4", + "description": "Will run scheduled jobs only once a day for inactive sites. Default 4 days if set to 0.", + "fieldname": "dormant_days", + "fieldtype": "Int", + "label": "Run Jobs only Daily if Inactive For (Days)" + }, + { + "default": "3", + "description": "Hourly rate limit for generating password reset links", + "fieldname": "password_reset_limit", + "fieldtype": "Int", + "label": "Password Reset Link Generation Limit" + }, + { + "default": "1", + "fieldname": "logout_on_password_reset", + "fieldtype": "Check", + "label": "Logout All Sessions on Password Reset" + }, + { + "default": "0", + "fieldname": "enable_onboarding", + "fieldtype": "Check", + "label": "Enable Onboarding" + }, + { + "default": "1", + "fieldname": "attach_view_link", + "fieldtype": "Check", + "label": "Include Web View Link in Email" + }, + { + "fieldname": "prepared_report_section", + "fieldtype": "Section Break", + "label": "Reports" + }, + { + "default": "XhiveFramework", + "description": "The application name will be used in the Login page.", + "fieldname": "app_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Application Name" + }, + { + "default": "1", + "fieldname": "strip_exif_metadata_from_uploaded_images", + "fieldtype": "Check", + "label": "Strip EXIF tags from uploaded images" + }, + { + "default": "0", + "fieldname": "encrypt_backup", + "fieldtype": "Check", + "label": "Encrypt Backups" + }, + { + "fieldname": "system_updates_section", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "disable_system_update_notification", + "fieldtype": "Check", + "label": "Disable System Update Notification" + }, + { + "default": "Sunday", + "fieldname": "first_day_of_the_week", + "fieldtype": "Select", + "label": "First Day of the Week", + "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday" + }, + { + "default": "30", + "description": "Number of days after which the document Web View link shared on email will be expired", + "fieldname": "document_share_key_expiry", + "fieldtype": "Int", + "label": "Document Share Key Expiry (in Days)" + }, + { + "default": "0", + "fieldname": "allow_older_web_view_links", + "fieldtype": "Check", + "label": "Allow Older Web View Links (Insecure)" + }, + { + "default": "20", + "fieldname": "max_auto_email_report_per_user", + "fieldtype": "Int", + "label": "Max auto email report per user" + }, + { + "default": "0", + "fieldname": "disable_change_log_notification", + "fieldtype": "Check", + "label": "Disable Change Log Notification" + }, + { + "default": "1200", + "fieldname": "reset_password_link_expiry_duration", + "fieldtype": "Duration", + "label": "Reset Password Link Expiry Duration", + "non_negative": 1 + }, + { + "default": "3", + "fieldname": "email_retry_limit", + "fieldtype": "Int", + "label": "Email Retry Limit" + }, + { + "default": "0", + "description": "Make sure to configure a Social Login Key before disabling to prevent lockout", + "fieldname": "disable_user_pass_login", + "fieldtype": "Check", + "label": "Disable Username/Password Login" + }, + { + "default": "1", + "description": "Allow users to log in without a password, using a login link sent to their email", + "fieldname": "login_with_email_link", + "fieldtype": "Check", + "label": "Login with email link" + }, + { + "default": "10", + "depends_on": "login_with_email_link", + "fieldname": "login_with_email_link_expiry", + "fieldtype": "Int", + "label": "Login with email link expiry (in minutes)" + }, + { + "default": "Banker's Rounding (legacy)", + "fieldname": "rounding_method", + "fieldtype": "Select", + "label": "Rounding Method", + "options": "Banker's Rounding (legacy)\nBanker's Rounding\nCommercial Rounding" + }, + { + "default": "0", + "fieldname": "disable_document_sharing", + "fieldtype": "Check", + "label": "Disable Document Sharing" + }, + { + "fieldname": "telemetry_section", + "fieldtype": "Section Break", + "label": "Telemetry" + }, + { + "default": "1", + "fieldname": "enable_telemetry", + "fieldtype": "Check", + "label": "Allow Sending Usage Data for Improving Applications" + }, + { + "fieldname": "welcome_email_template", + "fieldtype": "Link", + "label": "Welcome Email Template", + "options": "Email Template" + }, + { + "fieldname": "reset_password_template", + "fieldtype": "Link", + "label": "Reset Password Template", + "options": "Email Template" + }, + { + "default": "0", + "description": "When uploading files, force the use of the web-based image capture. If this is unchecked, the default behavior is to use the mobile native camera when use from a mobile is detected.", + "fieldname": "force_web_capture_mode_for_uploads", + "fieldtype": "Check", + "label": "Force Web Capture Mode for Uploads" + }, + { + "fieldname": "files_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "max_file_size", + "fieldtype": "Int", + "label": "Max File Size (MB)", + "non_negative": 1 + }, + { + "fieldname": "column_break_uqma", + "fieldtype": "Column Break" + }, + { + "description": "Provide a list of allowed file extensions for file uploads. Each line should contain one allowed file type. If unset, all file extensions are allowed. Example:
    CSV
    JPG
    PNG", + "fieldname": "allowed_file_extensions", + "fieldtype": "Small Text", + "label": "Allowed File Extensions" + }, + { + "fieldname": "security_tab", + "fieldtype": "Tab Break", + "label": "Login" + }, + { + "fieldname": "email_tab", + "fieldtype": "Tab Break", + "label": "Email" + }, + { + "fieldname": "files_tab", + "fieldtype": "Tab Break", + "label": "Files" + }, + { + "fieldname": "updates_tab", + "fieldtype": "Tab Break", + "label": "Updates" + }, + { + "fieldname": "backups_tab", + "fieldtype": "Tab Break", + "label": "Backups" + }, + { + "fieldname": "advanced_tab", + "fieldtype": "Tab Break", + "label": "Advanced" + }, + { + "fieldname": "password_tab", + "fieldtype": "Tab Break", + "label": "Password" + }, + { + "fieldname": "column_break_txqh", + "fieldtype": "Column Break" + }, + { + "fieldname": "login_methods_section", + "fieldtype": "Section Break", + "label": "Login Methods" + }, + { + "fieldname": "column_break_uhqk", + "fieldtype": "Column Break" + }, + { + "fieldname": "search_section", + "fieldtype": "Section Break", + "label": "Search" + }, + { + "default": "10", + "fieldname": "link_field_results_limit", + "fieldtype": "Int", + "label": "Link Field Results Limit", + "non_negative": 1 + }, + { + "default": "1", + "description": "When sending document using email, store the PDF on Communication. Warning: This can increase your storage usage.", + "fieldname": "store_attached_pdf_document", + "fieldtype": "Check", + "label": "Store Attached PDF Document" + } + ], + "icon": "fa fa-cog", + "issingle": 1, + "links": [], + "modified": "2024-03-14 15:18:01.465057", + "modified_by": "Administrator", + "module": "Core", + "name": "System Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/system_settings/system_settings.py b/xhiveframework/core/doctype/system_settings/system_settings.py new file mode 100644 index 0000000..6fd45fa --- /dev/null +++ b/xhiveframework/core/doctype/system_settings/system_settings.py @@ -0,0 +1,217 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import no_value_fields +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, today + + +class SystemSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow_consecutive_login_attempts: DF.Int + allow_error_traceback: DF.Check + allow_guests_to_upload_files: DF.Check + allow_login_after_fail: DF.Int + allow_login_using_mobile_number: DF.Check + allow_login_using_user_name: DF.Check + allow_older_web_view_links: DF.Check + allowed_file_extensions: DF.SmallText | None + app_name: DF.Data | None + apply_strict_user_permissions: DF.Check + attach_view_link: DF.Check + backup_limit: DF.Int + bypass_2fa_for_retricted_ip_users: DF.Check + bypass_restrict_ip_check_if_2fa_enabled: DF.Check + country: DF.Link | None + currency_precision: DF.Literal["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + date_format: DF.Literal[ + "yyyy-mm-dd", "dd-mm-yyyy", "dd/mm/yyyy", "dd.mm.yyyy", "mm/dd/yyyy", "mm-dd-yyyy" + ] + deny_multiple_sessions: DF.Check + disable_change_log_notification: DF.Check + disable_document_sharing: DF.Check + disable_standard_email_footer: DF.Check + disable_system_update_notification: DF.Check + disable_user_pass_login: DF.Check + document_share_key_expiry: DF.Int + dormant_days: DF.Int + email_footer_address: DF.SmallText | None + email_retry_limit: DF.Int + enable_onboarding: DF.Check + enable_password_policy: DF.Check + enable_scheduler: DF.Check + enable_telemetry: DF.Check + enable_two_factor_auth: DF.Check + encrypt_backup: DF.Check + first_day_of_the_week: DF.Literal[ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" + ] + float_precision: DF.Literal["", "2", "3", "4", "5", "6", "7", "8", "9"] + force_user_to_reset_password: DF.Int + force_web_capture_mode_for_uploads: DF.Check + hide_footer_in_auto_email_reports: DF.Check + language: DF.Link + lifespan_qrcode_image: DF.Int + link_field_results_limit: DF.Int + login_with_email_link: DF.Check + login_with_email_link_expiry: DF.Int + logout_on_password_reset: DF.Check + max_auto_email_report_per_user: DF.Int + max_file_size: DF.Int + minimum_password_score: DF.Literal["2", "3", "4"] + number_format: DF.Literal[ + "#,###.##", + "#.###,##", + "# ###.##", + "# ###,##", + "#'###.##", + "#, ###.##", + "#,##,###.##", + "#,###.###", + "#.###", + "#,###", + ] + otp_issuer_name: DF.Data | None + password_reset_limit: DF.Int + reset_password_link_expiry_duration: DF.Duration | None + reset_password_template: DF.Link | None + rounding_method: DF.Literal["Banker's Rounding (legacy)", "Banker's Rounding", "Commercial Rounding"] + session_expiry: DF.Data | None + setup_complete: DF.Check + store_attached_pdf_document: DF.Check + strip_exif_metadata_from_uploaded_images: DF.Check + time_format: DF.Literal["HH:mm:ss", "HH:mm"] + time_zone: DF.Literal[None] + two_factor_method: DF.Literal["OTP App", "SMS", "Email"] + welcome_email_template: DF.Link | None + # end: auto-generated types + + def validate(self): + from xhiveframework.twofactor import toggle_two_factor_auth + + enable_password_policy = cint(self.enable_password_policy) and True or False + minimum_password_score = cint(getattr(self, "minimum_password_score", 0)) or 0 + if enable_password_policy and minimum_password_score <= 0: + xhiveframework.throw(_("Please select Minimum Password Score")) + elif not enable_password_policy: + self.minimum_password_score = "" + + if self.session_expiry: + parts = self.session_expiry.split(":") + if len(parts) != 2 or not (cint(parts[0]) or cint(parts[1])): + xhiveframework.throw(_("Session Expiry must be in format {0}").format("hh:mm")) + + if self.enable_two_factor_auth: + if self.two_factor_method == "SMS": + if not xhiveframework.db.get_single_value("SMS Settings", "sms_gateway_url"): + xhiveframework.throw( + _("Please setup SMS before setting it as an authentication method, via SMS Settings") + ) + toggle_two_factor_auth(True, roles=["All"]) + else: + self.bypass_2fa_for_retricted_ip_users = 0 + self.bypass_restrict_ip_check_if_2fa_enabled = 0 + + xhiveframework.flags.update_last_reset_password_date = False + if self.force_user_to_reset_password and not cint( + xhiveframework.db.get_single_value("System Settings", "force_user_to_reset_password") + ): + xhiveframework.flags.update_last_reset_password_date = True + + self.validate_user_pass_login() + self.validate_backup_limit() + self.validate_file_extensions() + + if not self.link_field_results_limit: + self.link_field_results_limit = 10 + + if self.link_field_results_limit > 50: + self.link_field_results_limit = 50 + label = _(self.meta.get_label("link_field_results_limit")) + xhiveframework.msgprint( + _("{0} can not be more than {1}").format(label, 50), alert=True, indicator="yellow" + ) + + def validate_user_pass_login(self): + if not self.disable_user_pass_login: + return + + social_login_enabled = xhiveframework.db.exists("Social Login Key", {"enable_social_login": 1}) + ldap_enabled = xhiveframework.db.get_single_value("LDAP Settings", "enabled") + login_with_email_link_enabled = xhiveframework.db.get_single_value("System Settings", "login_with_email_link") + + if not (social_login_enabled or ldap_enabled or login_with_email_link_enabled): + xhiveframework.throw( + _( + "Please enable atleast one Social Login Key or LDAP or Login With Email Link before disabling username/password based login." + ) + ) + + def validate_backup_limit(self): + if not self.backup_limit or self.backup_limit < 1: + xhiveframework.msgprint(_("Number of backups must be greater than zero."), alert=True) + self.backup_limit = 1 + + def validate_file_extensions(self): + if not self.allowed_file_extensions: + return + + self.allowed_file_extensions = "\n".join( + ext.strip().upper() for ext in self.allowed_file_extensions.strip().splitlines() + ) + + def on_update(self): + self.set_defaults() + + xhiveframework.cache.delete_value("system_settings") + xhiveframework.cache.delete_value("time_zone") + + if xhiveframework.flags.update_last_reset_password_date: + update_last_reset_password_date() + + def set_defaults(self): + from xhiveframework.translate import set_default_language + + for df in self.meta.get("fields"): + if df.fieldtype not in no_value_fields and self.has_value_changed(df.fieldname): + xhiveframework.db.set_default(df.fieldname, self.get(df.fieldname)) + + if self.language: + set_default_language(self.language) + + +def update_last_reset_password_date(): + xhiveframework.db.sql( + """ UPDATE `tabUser` + SET + last_password_reset_date = %s + WHERE + last_password_reset_date is null""", + today(), + ) + + +@xhiveframework.whitelist() +def load(): + from xhiveframework.utils.momentjs import get_all_timezones + + if "System Manager" not in xhiveframework.get_roles(): + xhiveframework.throw(_("Not permitted"), xhiveframework.PermissionError) + + all_defaults = xhiveframework.db.get_defaults() + defaults = {} + + for df in xhiveframework.get_meta("System Settings").get("fields"): + if df.fieldtype in ("Select", "Data"): + defaults[df.fieldname] = all_defaults.get(df.fieldname) + + return {"timezones": get_all_timezones(), "defaults": defaults} diff --git a/xhiveframework/core/doctype/system_settings/test_system_settings.py b/xhiveframework/core/doctype/system_settings/test_system_settings.py new file mode 100644 index 0000000..e8aa14e --- /dev/null +++ b/xhiveframework/core/doctype/system_settings/test_system_settings.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSystemSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/transaction_log/__init__.py b/xhiveframework/core/doctype/transaction_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/transaction_log/readme.md b/xhiveframework/core/doctype/transaction_log/readme.md new file mode 100644 index 0000000..09162ef --- /dev/null +++ b/xhiveframework/core/doctype/transaction_log/readme.md @@ -0,0 +1,16 @@ +# Transaction Log Changelog + +## v1.0.0 +Initial version + +The line hash summarizes: +- The index of the row +- The timestamp +- The document raw data + +The chain hash summarizes: +- The previous line hash +- The current line hash + +## v1.0.1 +Modification of the timestamp fieldtype from "Time" to "Datetime" \ No newline at end of file diff --git a/xhiveframework/core/doctype/transaction_log/test_transaction_log.py b/xhiveframework/core/doctype/transaction_log/test_transaction_log.py new file mode 100644 index 0000000..41334a6 --- /dev/null +++ b/xhiveframework/core/doctype/transaction_log/test_transaction_log.py @@ -0,0 +1,46 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import hashlib + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = [] + + +class TestTransactionLog(XhiveFrameworkTestCase): + def test_validate_chaining(self): + xhiveframework.get_doc( + { + "doctype": "Transaction Log", + "reference_doctype": "Test Doctype", + "document_name": "Test Document 1", + "data": "first_data", + } + ).insert(ignore_permissions=True) + + second_log = xhiveframework.get_doc( + { + "doctype": "Transaction Log", + "reference_doctype": "Test Doctype", + "document_name": "Test Document 2", + "data": "second_data", + } + ).insert(ignore_permissions=True) + + third_log = xhiveframework.get_doc( + { + "doctype": "Transaction Log", + "reference_doctype": "Test Doctype", + "document_name": "Test Document 3", + "data": "third_data", + } + ).insert(ignore_permissions=True) + + sha = hashlib.sha256() + sha.update( + xhiveframework.safe_encode(str(third_log.transaction_hash)) + + xhiveframework.safe_encode(str(second_log.chaining_hash)) + ) + + self.assertEqual(sha.hexdigest(), third_log.chaining_hash) diff --git a/xhiveframework/core/doctype/transaction_log/transaction_log.js b/xhiveframework/core/doctype/transaction_log/transaction_log.js new file mode 100644 index 0000000..98a3634 --- /dev/null +++ b/xhiveframework/core/doctype/transaction_log/transaction_log.js @@ -0,0 +1,4 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Transaction Log", {}); diff --git a/xhiveframework/core/doctype/transaction_log/transaction_log.json b/xhiveframework/core/doctype/transaction_log/transaction_log.json new file mode 100644 index 0000000..2135976 --- /dev/null +++ b/xhiveframework/core/doctype/transaction_log/transaction_log.json @@ -0,0 +1,124 @@ +{ + "actions": [], + "creation": "2018-02-06 11:48:51.270524", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "row_index", + "section_break_2", + "reference_doctype", + "document_name", + "column_break_5", + "timestamp", + "checksum_version", + "section_break_8", + "previous_hash", + "transaction_hash", + "chaining_hash", + "data", + "amended_from" + ], + "fields": [ + { + "fieldname": "row_index", + "fieldtype": "Data", + "label": "Row Index", + "read_only": 1 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Data", + "label": "Reference Document Type", + "read_only": 1 + }, + { + "fieldname": "document_name", + "fieldtype": "Data", + "label": "Document Name", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "timestamp", + "fieldtype": "Datetime", + "label": "Timestamp", + "read_only": 1 + }, + { + "fieldname": "checksum_version", + "fieldtype": "Data", + "label": "Checksum Version", + "read_only": 1 + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "previous_hash", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Previous Hash", + "read_only": 1 + }, + { + "fieldname": "transaction_hash", + "fieldtype": "Small Text", + "label": "Transaction Hash", + "read_only": 1 + }, + { + "fieldname": "chaining_hash", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Chaining Hash", + "read_only": 1 + }, + { + "fieldname": "data", + "fieldtype": "Long Text", + "hidden": 1, + "label": "Data", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Transaction Log", + "print_hide": 1, + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2022-08-03 12:20:54.684305", + "modified_by": "Administrator", + "module": "Core", + "name": "Transaction Log", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/transaction_log/transaction_log.py b/xhiveframework/core/doctype/transaction_log/transaction_log.py new file mode 100644 index 0000000..ccb34a6 --- /dev/null +++ b/xhiveframework/core/doctype/transaction_log/transaction_log.py @@ -0,0 +1,86 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import hashlib + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.query_builder import DocType +from xhiveframework.utils import cint, now_datetime + + +class TransactionLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + amended_from: DF.Link | None + chaining_hash: DF.SmallText | None + checksum_version: DF.Data | None + data: DF.LongText | None + document_name: DF.Data | None + previous_hash: DF.SmallText | None + reference_doctype: DF.Data | None + row_index: DF.Data | None + timestamp: DF.Datetime | None + transaction_hash: DF.SmallText | None + + # end: auto-generated types + def before_insert(self): + index = get_current_index() + self.row_index = index + self.timestamp = now_datetime() + if index != 1: + prev_hash = xhiveframework.get_all( + "Transaction Log", filters={"row_index": str(index - 1)}, pluck="chaining_hash", limit=1 + ) + if prev_hash: + self.previous_hash = prev_hash[0] + else: + self.previous_hash = "Indexing broken" + else: + self.previous_hash = self.hash_line() + self.transaction_hash = self.hash_line() + self.chaining_hash = self.hash_chain() + self.checksum_version = "v1.0.1" + + def hash_line(self): + sha = hashlib.sha256() + sha.update( + xhiveframework.safe_encode(str(self.row_index)) + + xhiveframework.safe_encode(str(self.timestamp)) + + xhiveframework.safe_encode(str(self.data)) + ) + return sha.hexdigest() + + def hash_chain(self): + sha = hashlib.sha256() + sha.update( + xhiveframework.safe_encode(str(self.transaction_hash)) + xhiveframework.safe_encode(str(self.previous_hash)) + ) + return sha.hexdigest() + + +def get_current_index(): + series = DocType("Series") + current = ( + xhiveframework.qb.from_(series).where(series.name == "TRANSACTLOG").for_update().select("current") + ).run() + + if current and current[0][0] is not None: + current = current[0][0] + + xhiveframework.db.sql( + """UPDATE `tabSeries` + SET `current` = `current` + 1 + where `name` = 'TRANSACTLOG'""" + ) + current = cint(current) + 1 + else: + xhiveframework.db.sql("INSERT INTO `tabSeries` (name, current) VALUES ('TRANSACTLOG', 1)") + current = 1 + return current diff --git a/xhiveframework/core/doctype/translation/__init__.py b/xhiveframework/core/doctype/translation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/translation/test_translation.py b/xhiveframework/core/doctype/translation/test_translation.py new file mode 100644 index 0000000..e9a8694 --- /dev/null +++ b/xhiveframework/core/doctype/translation/test_translation.py @@ -0,0 +1,110 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework import _ +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.translate import clear_cache + + +class TestTranslation(XhiveFrameworkTestCase): + def setUp(self): + xhiveframework.db.delete("Translation") + + def tearDown(self): + xhiveframework.local.lang = "en" + clear_cache() + + def test_doctype(self): + translation_data = get_translation_data() + for key, val in translation_data.items(): + xhiveframework.local.lang = key + + translation = create_translation(key, val) + self.assertEqual(_(val[0]), val[1]) + + xhiveframework.delete_doc("Translation", translation.name) + self.assertEqual(_(val[0]), val[0]) + + def test_parent_language(self): + data = [ + ["es", ["Test Data", "datos de prueba"]], + ["es", ["Test Spanish", "prueba de español"]], + ["es-MX", ["Test Data", "pruebas de datos"]], + ] + + for key, val in data: + create_translation(key, val) + + xhiveframework.local.lang = "es" + + self.assertTrue(_(data[0][0]), data[0][1]) + + self.assertTrue(_(data[1][0]), data[1][1]) + + xhiveframework.local.lang = "es-MX" + + # different translation for es-MX + self.assertTrue(_(data[2][0]), data[2][1]) + + # from spanish (general) + self.assertTrue(_(data[1][0]), data[1][1]) + + def test_multi_language_translations(self): + source = "User" + self.assertNotEqual(_(source, lang="de"), _(source, lang="es")) + + def test_html_content_data_translation(self): + source = """ + MacBook Air lasts up to an incredible 12 hours between charges. So from your morning coffee to + your evening commute, you can work unplugged. When it’s time to kick back and relax, + you can get up to 12 hours of iTunes movie playback. And with up to 30 days of standby time, + you can go away for weeks and pick up where you left off.Whatever the task, + fifth-generation Intel Core i5 and i7 processors with Intel HD Graphics 6000 are up to it.
    + """ + + target = """ + MacBook Air dura hasta 12 horas increíbles entre cargas. Por lo tanto, + desde el café de la mañana hasta el viaje nocturno, puede trabajar desconectado. + Cuando es hora de descansar y relajarse, puede obtener hasta 12 horas de reproducción de películas de iTunes. + Y con hasta 30 días de tiempo de espera, puede irse por semanas y continuar donde lo dejó. Sea cual sea la tarea, + los procesadores Intel Core i5 e i7 de quinta generación con Intel HD Graphics 6000 son capaces de hacerlo. + """ + + create_translation("es", [source, target]) + + source = """ + MacBook Air lasts up to an incredible 12 hours between charges. So from your morning coffee to + your evening commute, you can work unplugged. When it’s time to kick back and relax, + you can get up to 12 hours of iTunes movie playback. And with up to 30 days of standby time, + you can go away for weeks and pick up where you left off.Whatever the task, + fifth-generation Intel Core i5 and i7 processors with Intel HD Graphics 6000 are up to it.
    + """ + + self.assertTrue(_(source), target) + + +def get_translation_data(): + html_source_data = """ + Test Data""" + html_translated_data = """ + testituloksia """ + + return { + "hr": ["Test data", "Testdaten"], + "ms": ["Test Data", "ujian Data"], + "et": ["Test Data", "testandmed"], + "es": ["Test Data", "datos de prueba"], + "en": ["Quotation", "Tax Invoice"], + "fi": [html_source_data, html_translated_data], + } + + +def create_translation(key, val): + translation = xhiveframework.new_doc("Translation") + translation.language = key + translation.source_text = val[0] + translation.translated_text = val[1] + translation.save() + return translation diff --git a/xhiveframework/core/doctype/translation/translation.js b/xhiveframework/core/doctype/translation/translation.js new file mode 100644 index 0000000..51c9c4f --- /dev/null +++ b/xhiveframework/core/doctype/translation/translation.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Translation", { + refresh: function () { + // + }, +}); diff --git a/xhiveframework/core/doctype/translation/translation.json b/xhiveframework/core/doctype/translation/translation.json new file mode 100644 index 0000000..68b83ed --- /dev/null +++ b/xhiveframework/core/doctype/translation/translation.json @@ -0,0 +1,111 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "hash", + "creation": "2016-02-17 12:21:16.175465", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "contributed", + "language", + "section_break_4", + "source_text", + "context", + "column_break_6", + "translated_text", + "section_break_6", + "contribution_status", + "contribution_docname" + ], + "fields": [ + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Language", + "options": "Language", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "context", + "fieldtype": "Data", + "label": "Context" + }, + { + "default": "0", + "fieldname": "contributed", + "fieldtype": "Check", + "hidden": 1, + "label": "Contributed", + "read_only": 1 + }, + { + "depends_on": "doc.contributed", + "fieldname": "contribution_status", + "fieldtype": "Select", + "label": "Contribution Status", + "options": "\nPending\nVerified\nRejected", + "read_only": 1 + }, + { + "fieldname": "contribution_docname", + "fieldtype": "Data", + "hidden": 1, + "label": "Contribution Document Name", + "read_only": 1 + }, + { + "description": "If your data is in HTML, please copy paste the exact HTML code with the tags.", + "fieldname": "source_text", + "fieldtype": "Code", + "label": "Source Text", + "reqd": 1 + }, + { + "fieldname": "translated_text", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Translated Text", + "reqd": 1 + } + ], + "links": [], + "modified": "2022-07-04 06:53:54.997004", + "modified_by": "Administrator", + "module": "Core", + "name": "Translation", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "source_text", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/translation/translation.py b/xhiveframework/core/doctype/translation/translation.py new file mode 100644 index 0000000..af9a2d0 --- /dev/null +++ b/xhiveframework/core/doctype/translation/translation.py @@ -0,0 +1,46 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.translate import MERGED_TRANSLATION_KEY, USER_TRANSLATION_KEY, get_translator_url +from xhiveframework.utils import is_html, strip_html_tags + + +class Translation(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + context: DF.Data | None + contributed: DF.Check + contribution_docname: DF.Data | None + contribution_status: DF.Literal["", "Pending", "Verified", "Rejected"] + language: DF.Link + source_text: DF.Code + translated_text: DF.Code + + # end: auto-generated types + def validate(self): + if is_html(self.source_text): + self.remove_html_from_source() + + def remove_html_from_source(self): + self.source_text = strip_html_tags(self.source_text).strip() + + def on_update(self): + clear_user_translation_cache(self.language) + + def on_trash(self): + clear_user_translation_cache(self.language) + + +def clear_user_translation_cache(lang): + xhiveframework.cache.hdel(USER_TRANSLATION_KEY, lang) + xhiveframework.cache.hdel(MERGED_TRANSLATION_KEY, lang) diff --git a/xhiveframework/core/doctype/user/__init__.py b/xhiveframework/core/doctype/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user/test_records.json b/xhiveframework/core/doctype/user/test_records.json new file mode 100644 index 0000000..9d1bf0e --- /dev/null +++ b/xhiveframework/core/doctype/user/test_records.json @@ -0,0 +1,95 @@ +[ + { + "doctype": "User", + "email": "test@example.com", + "enabled": 1, + "first_name": "_Test", + "new_password": "Eastern_43A1W", + "roles": [ + { + "doctype": "Has Role", + "parentfield": "roles", + "role": "_Test Role" + }, + { + "doctype": "Has Role", + "parentfield": "roles", + "role": "System Manager" + } + ] + }, + { + "doctype": "User", + "email": "test1@example.com", + "first_name": "_Test1", + "new_password": "Eastern_43A1W" + }, + { + "doctype": "User", + "email": "test2@example.com", + "first_name": "_Test2", + "new_password": "Eastern_43A1W", + "enabled": 1 + }, + { + "doctype": "User", + "email": "test3@example.com", + "first_name": "_Test3", + "new_password": "Eastern_43A1W", + "enabled": 1 + }, + { + "doctype": "User", + "email": "test4@example.com", + "first_name": "_Test4", + "new_password": "Eastern_43A1W", + "enabled": 1 + }, + { + "doctype": "User", + "email": "test'5@example.com", + "first_name": "_Test'5", + "new_password": "Eastern_43A1W", + "enabled": 1 + }, + { + "doctype": "User", + "email": "testperm@example.com", + "first_name": "_Test Perm", + "new_password": "Eastern_43A1W", + "enabled": 1 + }, + { + "doctype": "User", + "email": "testdelete@example.com", + "enabled": 1, + "first_name": "_Test", + "new_password": "Eastern_43A1W", + "roles": [ + { + "doctype": "Has Role", + "parentfield": "roles", + "role": "_Test Role 2" + }, + { + "doctype": "Has Role", + "parentfield": "roles", + "role": "System Manager" + } + ] + }, + { + "doctype": "User", + "email": "testpassword@example.com", + "enabled": 1, + "first_name": "_Test", + "new_password": "Eastern_43A1W", + "roles": [ + { + "doctype": "Has Role", + "parentfield": "roles", + "role": "System Manager" + } + ] + } +] diff --git a/xhiveframework/core/doctype/user/test_user.py b/xhiveframework/core/doctype/user/test_user.py new file mode 100644 index 0000000..9039907 --- /dev/null +++ b/xhiveframework/core/doctype/user/test_user.py @@ -0,0 +1,493 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import json +import time +from contextlib import contextmanager +from unittest.mock import patch +from urllib.parse import parse_qs, urlparse + +from werkzeug.http import parse_cookie + +import xhiveframework +import xhiveframework.exceptions +from xhiveframework.core.doctype.user.user import ( + handle_password_test_fail, + reset_password, + sign_up, + test_password_strength, + update_password, + verify_password, +) +from xhiveframework.desk.notifications import extract_mentions +from xhiveframework.xhiveframeworkclient import XhiveFrameworkClient +from xhiveframework.model.delete_doc import delete_doc +from xhiveframework.tests.test_api import XhiveFrameworkAPITestCase +from xhiveframework.tests.utils import XhiveFrameworkTestCase, change_settings +from xhiveframework.utils import get_url + +user_module = xhiveframework.core.doctype.user.user +test_records = xhiveframework.get_test_records("User") + + +class TestUser(XhiveFrameworkTestCase): + def tearDown(self): + # disable password strength test + xhiveframework.db.set_single_value("System Settings", "enable_password_policy", 0) + xhiveframework.db.set_single_value("System Settings", "minimum_password_score", "") + xhiveframework.db.set_single_value("System Settings", "password_reset_limit", 3) + xhiveframework.set_user("Administrator") + + @staticmethod + def reset_password(user) -> str: + link = user.reset_password() + return parse_qs(urlparse(link).query)["key"][0] + + def test_user_type(self): + user_id = xhiveframework.generate_hash() + "@example.com" + new_user = xhiveframework.get_doc(doctype="User", email=user_id, first_name="Tester").insert() + self.assertEqual(new_user.user_type, "Website User") + + # social login userid for xhiveframework + self.assertTrue(new_user.social_logins[0].userid) + self.assertEqual(new_user.social_logins[0].provider, "xhiveframework") + + # role with desk access + new_user.add_roles("_Test Role 2") + new_user.save() + self.assertEqual(new_user.user_type, "System User") + + # clear role + new_user.roles = [] + new_user.save() + self.assertEqual(new_user.user_type, "Website User") + + # role without desk access + new_user.add_roles("_Test Role 4") + new_user.save() + self.assertEqual(new_user.user_type, "Website User") + + delete_contact(new_user.name) + xhiveframework.delete_doc("User", new_user.name) + + def test_delete(self): + xhiveframework.get_doc("User", "test@example.com").add_roles("_Test Role 2") + self.assertRaises(xhiveframework.LinkExistsError, delete_doc, "Role", "_Test Role 2") + xhiveframework.db.delete("Has Role", {"role": "_Test Role 2"}) + delete_doc("Role", "_Test Role 2") + + if xhiveframework.db.exists("User", "_test@example.com"): + delete_contact("_test@example.com") + delete_doc("User", "_test@example.com") + + user = xhiveframework.copy_doc(test_records[1]) + user.email = "_test@example.com" + user.insert() + + xhiveframework.get_doc({"doctype": "ToDo", "description": "_Test"}).insert() + + delete_contact("_test@example.com") + delete_doc("User", "_test@example.com") + + self.assertTrue( + not xhiveframework.db.sql("""select * from `tabToDo` where allocated_to=%s""", ("_test@example.com",)) + ) + + from xhiveframework.core.doctype.role.test_role import test_records as role_records + + xhiveframework.copy_doc(role_records[1]).insert() + + def test_get_value(self): + self.assertEqual(xhiveframework.db.get_value("User", "test@example.com"), "test@example.com") + self.assertEqual(xhiveframework.db.get_value("User", {"email": "test@example.com"}), "test@example.com") + self.assertEqual( + xhiveframework.db.get_value("User", {"email": "test@example.com"}, "email"), "test@example.com" + ) + self.assertEqual( + xhiveframework.db.get_value("User", {"email": "test@example.com"}, ["first_name", "email"]), + ("_Test", "test@example.com"), + ) + self.assertEqual( + xhiveframework.db.get_value( + "User", {"email": "test@example.com", "first_name": "_Test"}, ["first_name", "email"] + ), + ("_Test", "test@example.com"), + ) + + test_user = xhiveframework.db.sql("select * from tabUser where name='test@example.com'", as_dict=True)[0] + self.assertEqual( + xhiveframework.db.get_value("User", {"email": "test@example.com"}, "*", as_dict=True), test_user + ) + + self.assertEqual(xhiveframework.db.get_value("User", "xxxtest@example.com"), None) + + xhiveframework.db.set_single_value("Website Settings", "_test", "_test_val") + self.assertEqual(xhiveframework.db.get_value("Website Settings", None, "_test"), "_test_val") + self.assertEqual(xhiveframework.db.get_value("Website Settings", "Website Settings", "_test"), "_test_val") + + def test_high_permlevel_validations(self): + user = xhiveframework.get_meta("User") + self.assertTrue("roles" in [d.fieldname for d in user.get_high_permlevel_fields()]) + + me = xhiveframework.get_doc("User", "testperm@example.com") + me.remove_roles("System Manager") + + xhiveframework.set_user("testperm@example.com") + + me = xhiveframework.get_doc("User", "testperm@example.com") + me.add_roles("System Manager") + + # system manager is not added (it is reset) + self.assertFalse("System Manager" in [d.role for d in me.roles]) + + # ignore permlevel using flags + me.flags.ignore_permlevel_for_fields = ["roles"] + me.add_roles("System Manager") + + # system manager now added due to flags + self.assertTrue("System Manager" in [d.role for d in me.get("roles")]) + + # reset flags + me.flags.ignore_permlevel_for_fields = None + + # change user + xhiveframework.set_user("Administrator") + + me = xhiveframework.get_doc("User", "testperm@example.com") + me.add_roles("System Manager") + + # system manager now added by Administrator + self.assertTrue("System Manager" in [d.role for d in me.get("roles")]) + + def test_delete_user(self): + new_user = xhiveframework.get_doc( + dict(doctype="User", email="test-for-delete@example.com", first_name="Tester Delete User") + ).insert(ignore_if_duplicate=True) + self.assertEqual(new_user.user_type, "Website User") + + # role with desk access + new_user.add_roles("_Test Role 2") + new_user.save() + self.assertEqual(new_user.user_type, "System User") + + comm = xhiveframework.get_doc( + { + "doctype": "Communication", + "subject": "To check user able to delete even if linked with communication", + "content": "To check user able to delete even if linked with communication", + "sent_or_received": "Sent", + "user": new_user.name, + } + ) + comm.insert(ignore_permissions=True) + + delete_contact(new_user.name) + xhiveframework.delete_doc("User", new_user.name) + self.assertFalse(xhiveframework.db.exists("User", new_user.name)) + + def test_password_strength(self): + # Test Password without Password Strength Policy + xhiveframework.db.set_single_value("System Settings", "enable_password_policy", 0) + + # password policy is disabled, test_password_strength should be ignored + result = test_password_strength("test_password") + self.assertFalse(result.get("feedback", None)) + + # Test Password with Password Strenth Policy Set + xhiveframework.db.set_single_value("System Settings", "enable_password_policy", 1) + xhiveframework.db.set_single_value("System Settings", "minimum_password_score", 2) + + # Score 1; should now fail + result = test_password_strength("bee2ve") + self.assertEqual(result["feedback"]["password_policy_validation_passed"], False) + self.assertRaises(xhiveframework.exceptions.ValidationError, handle_password_test_fail, result["feedback"]) + self.assertRaises( + xhiveframework.exceptions.ValidationError, handle_password_test_fail, result + ) # test backwards compatibility + + # Score 4; should pass + result = test_password_strength("Eastern_43A1W") + self.assertEqual(result["feedback"]["password_policy_validation_passed"], True) + + # test password strength while saving user with new password + user = xhiveframework.get_doc("User", "test@example.com") + xhiveframework.flags.in_test = False + user.new_password = "password" + self.assertRaises(xhiveframework.exceptions.ValidationError, user.save) + user.reload() + user.new_password = "Eastern_43A1W" + user.save() + xhiveframework.flags.in_test = True + + def test_comment_mentions(self): + comment = """ + + @Test + + """ + self.assertEqual(extract_mentions(comment)[0], "test.comment@example.com") + + comment = """ +
    + Testing comment, + + @Test + + please check +
    + """ + self.assertEqual(extract_mentions(comment)[0], "test.comment@example.com") + comment = """ +
    + Testing comment for + + @Test + + and + + @Test + + please check +
    + """ + self.assertEqual(extract_mentions(comment)[0], "test_user@example.com") + self.assertEqual(extract_mentions(comment)[1], "test.again@example1.com") + + xhiveframework.delete_doc("User Group", "Team") + doc = xhiveframework.get_doc( + { + "doctype": "User Group", + "name": "Team", + "user_group_members": [{"user": "test@example.com"}, {"user": "test1@example.com"}], + } + ) + + doc.insert() + + comment = """ +
    + Testing comment for + + @Team + and + + @Unknown Team + + please check +
    + """ + self.assertListEqual(extract_mentions(comment), ["test@example.com", "test1@example.com"]) + + @change_settings("System Settings", commit=True, password_reset_limit=1) + def test_rate_limiting_for_reset_password(self): + url = get_url() + data = {"cmd": "xhiveframework.core.doctype.user.user.reset_password", "user": "test@test.com"} + + # Clear rate limit tracker to start fresh + key = f"rl:{data['cmd']}:{data['user']}" + xhiveframework.cache.delete(key) + + c = XhiveFrameworkClient(url) + res1 = c.session.post(url, data=data, verify=c.verify, headers=c.headers) + res2 = c.session.post(url, data=data, verify=c.verify, headers=c.headers) + self.assertEqual(res1.status_code, 404) + self.assertEqual(res2.status_code, 417) + + def test_user_rename(self): + old_name = "test_user_rename@example.com" + new_name = "test_user_rename_new@example.com" + user = xhiveframework.get_doc( + { + "doctype": "User", + "email": old_name, + "enabled": 1, + "first_name": "_Test", + "new_password": "Eastern_43A1W", + "roles": [{"doctype": "Has Role", "parentfield": "roles", "role": "System Manager"}], + } + ).insert(ignore_permissions=True, ignore_if_duplicate=True) + + xhiveframework.rename_doc("User", user.name, new_name) + self.assertTrue(xhiveframework.db.exists("Notification Settings", new_name)) + + xhiveframework.delete_doc("User", new_name) + + def test_signup(self): + import xhiveframework.website.utils + + random_user = xhiveframework.mock("email") + random_user_name = xhiveframework.mock("name") + # disabled signup + with patch.object(user_module, "is_signup_disabled", return_value=True): + self.assertRaisesRegex( + xhiveframework.exceptions.ValidationError, + "Sign Up is disabled", + sign_up, + random_user, + random_user_name, + "/signup", + ) + + self.assertTupleEqual( + sign_up(random_user, random_user_name, "/welcome"), + (1, "Please check your email for verification"), + ) + self.assertEqual(xhiveframework.cache.hget("redirect_after_login", random_user), "/welcome") + + # re-register + self.assertTupleEqual(sign_up(random_user, random_user_name, "/welcome"), (0, "Already Registered")) + + # disabled user + user = xhiveframework.get_doc("User", random_user) + user.enabled = 0 + user.save() + + self.assertTupleEqual( + sign_up(random_user, random_user_name, "/welcome"), (0, "Registered but disabled") + ) + + # throttle user creation + with patch.object(user_module.xhiveframework.db, "get_creation_count", return_value=301): + self.assertRaisesRegex( + xhiveframework.exceptions.ValidationError, + "Throttled", + sign_up, + xhiveframework.mock("email"), + random_user_name, + "/signup", + ) + + @change_settings("System Settings", password_reset_limit=6) + def test_reset_password(self): + from xhiveframework.auth import CookieManager, LoginManager + from xhiveframework.utils import set_request + + old_password = "Eastern_43A1W" + new_password = "easy_password" + + set_request(path="/random") + xhiveframework.local.cookie_manager = CookieManager() + xhiveframework.local.login_manager = LoginManager() + # used by rate limiter when calling reset_password + xhiveframework.local.request_ip = "127.0.0.69" + + xhiveframework.set_user("testpassword@example.com") + test_user = xhiveframework.get_doc("User", "testpassword@example.com") + key = self.reset_password(test_user) + self.assertEqual(update_password(new_password, key=key), "/app") + self.assertEqual( + update_password(new_password, key="wrong_key"), + "The reset password link has either been used before or is invalid", + ) + + # password verification should fail with old password + self.assertRaises(xhiveframework.exceptions.AuthenticationError, verify_password, old_password) + verify_password(new_password) + + # reset password + update_password(old_password, old_password=new_password) + self.assertRaises(TypeError, update_password, "test", 1, ["like", "%"]) + + password_strength_response = { + "feedback": {"password_policy_validation_passed": False, "suggestions": ["Fix password"]} + } + + # password strength failure test + with patch.object(user_module, "test_password_strength", return_value=password_strength_response): + self.assertRaisesRegex( + xhiveframework.exceptions.ValidationError, + "Fix password", + update_password, + new_password, + 0, + test_user.reset_password_key, + ) + + # test redirect URL for website users + xhiveframework.set_user("test2@example.com") + self.assertEqual(update_password(new_password, old_password=old_password), "/") + # reset password + update_password(old_password, old_password=new_password) + + # test API endpoint + with patch.object(user_module.xhiveframework, "sendmail") as sendmail: + xhiveframework.clear_messages() + test_user = xhiveframework.get_doc("User", "test2@example.com") + self.assertEqual(reset_password(user="test2@example.com"), None) + test_user.reload() + link = sendmail.call_args_list[0].kwargs["args"]["link"] + key = parse_qs(urlparse(link).query)["key"][0] + self.assertEqual(update_password(new_password, key=key), "/") + update_password(old_password, old_password=new_password) + self.assertEqual( + xhiveframework.message_log[0].get("message"), + "Password reset instructions have been sent to your email", + ) + + sendmail.assert_called_once() + self.assertEqual(sendmail.call_args[1]["recipients"], "test2@example.com") + + self.assertEqual(reset_password(user="test2@example.com"), None) + self.assertEqual(reset_password(user="Administrator"), "not allowed") + self.assertEqual(reset_password(user="random"), "not found") + + def test_user_onload_modules(self): + from xhiveframework.config import get_modules_from_all_apps + from xhiveframework.desk.form.load import getdoc + + xhiveframework.response.docs = [] + getdoc("User", "Administrator") + doc = xhiveframework.response.docs[0] + self.assertListEqual( + sorted(doc.get("__onload").get("all_modules", [])), + sorted(m.get("module_name") for m in get_modules_from_all_apps()), + ) + + @change_settings("System Settings", reset_password_link_expiry_duration=1) + def test_reset_password_link_expiry(self): + new_password = "new_password" + xhiveframework.set_user("testpassword@example.com") + test_user = xhiveframework.get_doc("User", "testpassword@example.com") + key = self.reset_password(test_user) + time.sleep(1) + + self.assertEqual( + update_password(new_password, key=key), + "The reset password link has been expired", + ) + + +class TestImpersonation(XhiveFrameworkAPITestCase): + def test_impersonation(self): + with test_user(roles=["System Manager"]) as user: + self.post( + self.method_path("xhiveframework.core.doctype.user.user.impersonate"), + {"user": user.name, "reason": "test", "sid": self.sid}, + ) + resp = self.get(self.method_path("xhiveframework.auth.get_logged_user")) + self.assertEqual(resp.json["message"], user.name) + + +@contextmanager +def test_user(*, first_name: str | None = None, email: str | None = None, roles: list[str], **kwargs): + try: + first_name = first_name or xhiveframework.generate_hash() + email = email or (first_name + "@example.com") + user = xhiveframework.new_doc( + "User", + send_welcome_email=0, + email=email, + first_name=first_name, + **kwargs, + ) + user.append_roles(*roles) + user.insert() + yield user + finally: + user.delete(force=True, ignore_permissions=True) + xhiveframework.db.commit() + + +def delete_contact(user): + xhiveframework.db.delete("Contact", {"email_id": user}) + xhiveframework.db.delete("Contact Email", {"email_id": user}) diff --git a/xhiveframework/core/doctype/user/user.js b/xhiveframework/core/doctype/user/user.js new file mode 100644 index 0000000..68fc0f2 --- /dev/null +++ b/xhiveframework/core/doctype/user/user.js @@ -0,0 +1,413 @@ +xhiveframework.ui.form.on("User", { + before_load: function (frm) { + let update_tz_options = function () { + frm.fields_dict.time_zone.set_data(xhiveframework.all_timezones); + }; + + if (!xhiveframework.all_timezones) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.get_timezones", + callback: function (r) { + xhiveframework.all_timezones = r.message.timezones; + update_tz_options(); + }, + }); + } else { + update_tz_options(); + } + }, + + time_zone: function (frm) { + if (frm.doc.time_zone && frm.doc.time_zone.startsWith("Etc")) { + frm.set_df_property( + "time_zone", + "description", + __("Note: Etc timezones have their signs reversed.") + ); + } + }, + + role_profile_name: function (frm) { + if (frm.doc.role_profile_name) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.get_role_profile", + args: { + role_profile: frm.doc.role_profile_name, + }, + callback: function (data) { + frm.set_value("roles", []); + $.each(data.message || [], function (i, v) { + var d = frm.add_child("roles"); + d.role = v.role; + }); + frm.roles_editor.show(); + }, + }); + } + }, + + module_profile: function (frm) { + if (frm.doc.module_profile) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.get_module_profile", + args: { + module_profile: frm.doc.module_profile, + }, + callback: function (data) { + frm.set_value("block_modules", []); + $.each(data.message || [], function (i, v) { + let d = frm.add_child("block_modules"); + d.module = v.module; + }); + frm.module_editor && frm.module_editor.show(); + }, + }); + } + }, + + onload: function (frm) { + frm.can_edit_roles = has_access_to_edit_user(); + + if (frm.is_new() && frm.roles_editor) { + frm.roles_editor.reset(); + } + + if ( + frm.can_edit_roles && + !frm.is_new() && + ["System User", "Website User"].includes(frm.doc.user_type) + ) { + if (!frm.roles_editor) { + const role_area = $('
    ').appendTo( + frm.fields_dict.roles_html.wrapper + ); + + frm.roles_editor = new xhiveframework.RoleEditor( + role_area, + frm, + frm.doc.role_profile_name ? 1 : 0 + ); + + if (frm.doc.user_type == "System User") { + var module_area = $("
    ").appendTo(frm.fields_dict.modules_html.wrapper); + frm.module_editor = new xhiveframework.ModuleEditor(frm, module_area); + } + } else { + frm.roles_editor.show(); + } + } + }, + refresh: function (frm) { + let doc = frm.doc; + + if (frm.is_new()) { + frm.set_value("time_zone", xhiveframework.sys_defaults.time_zone); + } + + if ( + ["System User", "Website User"].includes(frm.doc.user_type) && + !frm.is_new() && + !frm.roles_editor && + frm.can_edit_roles + ) { + frm.reload_doc(); + return; + } + + frm.toggle_display(["sb1", "sb3", "modules_access"], false); + frm.trigger("setup_impersonation"); + + if (!frm.is_new()) { + if (has_access_to_edit_user()) { + frm.add_custom_button( + __("Set User Permissions"), + function () { + xhiveframework.route_options = { + user: doc.name, + }; + xhiveframework.set_route("List", "User Permission"); + }, + __("Permissions") + ); + + frm.add_custom_button( + __("View Permitted Documents"), + () => + xhiveframework.set_route("query-report", "Permitted Documents For User", { + user: frm.doc.name, + }), + __("Permissions") + ); + + frm.toggle_display(["sb1", "sb3", "modules_access"], true); + } + + frm.add_custom_button( + __("Reset Password"), + function () { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.reset_password", + args: { + user: frm.doc.name, + }, + }); + }, + __("Password") + ); + + if (xhiveframework.user.has_role("System Manager")) { + xhiveframework.db.get_single_value("LDAP Settings", "enabled").then((value) => { + if (value === 1 && frm.doc.name != "Administrator") { + frm.add_custom_button( + __("Reset LDAP Password"), + function () { + const d = new xhiveframework.ui.Dialog({ + title: __("Reset LDAP Password"), + fields: [ + { + label: __("New Password"), + fieldtype: "Password", + fieldname: "new_password", + reqd: 1, + }, + { + label: __("Confirm New Password"), + fieldtype: "Password", + fieldname: "confirm_password", + reqd: 1, + }, + { + label: __("Logout All Sessions"), + fieldtype: "Check", + fieldname: "logout_sessions", + }, + ], + primary_action: (values) => { + d.hide(); + if (values.new_password !== values.confirm_password) { + xhiveframework.throw(__("Passwords do not match!")); + } + xhiveframework.call( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.reset_password", + { + user: frm.doc.email, + password: values.new_password, + logout: values.logout_sessions, + } + ); + }, + }); + d.show(); + }, + __("Password") + ); + } + }); + } + + if ( + cint(xhiveframework.boot.sysdefaults.enable_two_factor_auth) && + (xhiveframework.session.user == doc.name || xhiveframework.user.has_role("System Manager")) + ) { + frm.add_custom_button( + __("Reset OTP Secret"), + function () { + xhiveframework.call({ + method: "xhiveframework.twofactor.reset_otp_secret", + args: { + user: frm.doc.name, + }, + }); + }, + __("Password") + ); + } + + frm.trigger("enabled"); + + if (frm.roles_editor && frm.can_edit_roles) { + frm.roles_editor.disable = frm.doc.role_profile_name ? 1 : 0; + frm.roles_editor.show(); + } + + frm.module_editor && frm.module_editor.show(); + + if (xhiveframework.session.user == doc.name) { + // update display settings + if (doc.user_image) { + xhiveframework.boot.user_info[xhiveframework.session.user].image = xhiveframework.utils.get_file_link( + doc.user_image + ); + } + } + } + if (frm.doc.user_emails && xhiveframework.model.can_create("Email Account")) { + var found = 0; + for (var i = 0; i < frm.doc.user_emails.length; i++) { + if (frm.doc.email == frm.doc.user_emails[i].email_id) { + found = 1; + } + } + if (!found) { + frm.add_custom_button(__("Create User Email"), function () { + frm.events.create_user_email(frm); + }); + } + } + + if (xhiveframework.route_flags.unsaved === 1) { + delete xhiveframework.route_flags.unsaved; + for (let i = 0; i < frm.doc.user_emails.length; i++) { + frm.doc.user_emails[i].idx = frm.doc.user_emails[i].idx + 1; + } + frm.dirty(); + } + frm.trigger("time_zone"); + }, + validate: function (frm) { + if (frm.roles_editor) { + frm.roles_editor.set_roles_in_table(); + } + }, + enabled: function (frm) { + var doc = frm.doc; + if (!frm.is_new() && has_access_to_edit_user()) { + frm.toggle_display(["sb1", "sb3", "modules_access"], doc.enabled); + frm.set_df_property("enabled", "read_only", 0); + } + + if (xhiveframework.session.user !== "Administrator") { + frm.toggle_enable("email", frm.is_new()); + } + }, + create_user_email: function (frm) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.has_email_account", + args: { + email: frm.doc.email, + }, + callback: function (r) { + if (!Array.isArray(r.message) || !r.message.length) { + xhiveframework.route_options = { + email_id: frm.doc.email, + awaiting_password: 1, + enable_incoming: 1, + }; + xhiveframework.model.with_doctype("Email Account", function (doc) { + doc = xhiveframework.model.get_new_doc("Email Account"); + xhiveframework.route_flags.linked_user = frm.doc.name; + xhiveframework.route_flags.delete_user_from_locals = true; + xhiveframework.set_route("Form", "Email Account", doc.name); + }); + } else { + xhiveframework.route_flags.create_user_account = frm.doc.name; + xhiveframework.set_route("Form", "Email Account", r.message[0]["name"]); + } + }, + }); + }, + generate_keys: function (frm) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.generate_keys", + args: { + user: frm.doc.name, + }, + callback: function (r) { + if (r.message) { + xhiveframework.msgprint(__("Save API Secret: {0}", [r.message.api_secret])); + frm.reload_doc(); + } + }, + }); + }, + after_save: function (frm) { + /** + * Checks whether the effective value has changed. + * + * @param {Array.} - Tuple with new override, previous override, + * and optionally fallback. + * @returns {boolean} - Whether the resulting value has effectively changed + */ + const has_effectively_changed = ([new_override, prev_override, fallback = undefined]) => { + const prev_effective = prev_override || fallback; + const new_effective = new_override || fallback; + return new_override !== undefined && prev_effective !== new_effective; + }; + + const doc = frm.doc; + const boot = xhiveframework.boot; + const attr_tuples = [ + [doc.language, boot.user.language, boot.sysdefaults.language], + [doc.time_zone, boot.time_zone.user, boot.time_zone.system], + [doc.desk_theme, boot.user.desk_theme], // No system default. + ]; + + if (doc.name === xhiveframework.session.user && attr_tuples.some(has_effectively_changed)) { + xhiveframework.msgprint(__("Refreshing...")); + window.location.reload(); + } + }, + setup_impersonation: function (frm) { + if (xhiveframework.session.user === "Administrator" && frm.doc.name != "Administrator") { + frm.add_custom_button(__("Impersonate"), () => { + if (frm.doc.restrict_ip) { + xhiveframework.msgprint({ + message: + "There's IP restriction for this user, you can not impersonate as this user.", + title: "IP restriction is enabled", + }); + return; + } + xhiveframework.prompt( + [ + { + fieldname: "reason", + fieldtype: "Small Text", + label: "Reason for impersonating", + description: __("Note: This will be shared with user."), + reqd: 1, + }, + ], + (values) => { + xhiveframework + .xcall("xhiveframework.core.doctype.user.user.impersonate", { + user: frm.doc.name, + reason: values.reason, + }) + .then(() => window.location.reload()); + }, + __("Impersonate as {0}", [frm.doc.name]), + __("Confirm") + ); + }); + } + }, +}); + +xhiveframework.ui.form.on("User Email", { + email_account(frm, cdt, cdn) { + let child_row = locals[cdt][cdn]; + xhiveframework.model.get_value( + "Email Account", + child_row.email_account, + "auth_method", + (value) => { + child_row.used_oauth = value.auth_method === "OAuth"; + frm.refresh_field("user_emails", cdn, "used_oauth"); + } + ); + }, +}); + +function has_access_to_edit_user() { + return has_common(xhiveframework.user_roles, get_roles_for_editing_user()); +} + +function get_roles_for_editing_user() { + return ( + xhiveframework + .get_meta("User") + .permissions.filter((perm) => perm.permlevel >= 1 && perm.write) + .map((perm) => perm.role) || ["System Manager"] + ); +} diff --git a/xhiveframework/core/doctype/user/user.json b/xhiveframework/core/doctype/user/user.json new file mode 100644 index 0000000..2db45c9 --- /dev/null +++ b/xhiveframework/core/doctype/user/user.json @@ -0,0 +1,806 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "creation": "2022-01-10 17:29:51.672911", + "description": "Represents a User in the system.", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "user_details_tab", + "enabled", + "section_break_3", + "email", + "first_name", + "middle_name", + "last_name", + "column_break0", + "full_name", + "username", + "column_break_11", + "language", + "time_zone", + "send_welcome_email", + "unsubscribed", + "user_image", + "roles_permissions_tab", + "sb1", + "role_profile_name", + "roles_html", + "roles", + "sb_allow_modules", + "module_profile", + "modules_html", + "block_modules", + "home_settings", + "short_bio", + "gender", + "birth_date", + "interest", + "column_break_26", + "phone", + "location", + "bio", + "column_break_22", + "mobile_no", + "settings_tab", + "desk_settings_section", + "mute_sounds", + "desk_theme", + "banner_image", + "change_password", + "new_password", + "logout_all_sessions", + "reset_password_key", + "last_reset_password_key_generated_on", + "last_password_reset_date", + "redirect_url", + "document_follow_notifications_section", + "document_follow_notify", + "document_follow_frequency", + "column_break_75", + "follow_created_documents", + "follow_commented_documents", + "follow_liked_documents", + "follow_assigned_documents", + "follow_shared_documents", + "email_settings", + "email_signature", + "thread_notify", + "send_me_a_copy", + "allowed_in_mentions", + "user_emails", + "sb2", + "defaults", + "sb3", + "simultaneous_sessions", + "restrict_ip", + "last_ip", + "column_break1", + "login_after", + "user_type", + "last_active", + "section_break_63", + "login_before", + "bypass_restrict_ip_check_if_2fa_enabled", + "last_login", + "last_known_versions", + "third_party_authentication", + "social_logins", + "api_access", + "api_key", + "generate_keys", + "column_break_65", + "api_secret", + "onboarding_status", + "connections_tab" + ], + "fields": [ + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled", + "oldfieldname": "enabled", + "oldfieldtype": "Check", + "read_only": 1 + }, + { + "depends_on": "enabled", + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Basic Info" + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "no_copy": 1, + "oldfieldname": "email", + "oldfieldtype": "Data", + "options": "Email", + "reqd": 1 + }, + { + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name", + "oldfieldname": "first_name", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name", + "oldfieldname": "middle_name", + "oldfieldtype": "Data" + }, + { + "bold": 1, + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name", + "oldfieldname": "last_name", + "oldfieldtype": "Data" + }, + { + "fieldname": "full_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Full Name", + "read_only": 1 + }, + { + "bold": 1, + "default": "1", + "depends_on": "eval:doc.__islocal", + "fieldname": "send_welcome_email", + "fieldtype": "Check", + "label": "Send Welcome Email" + }, + { + "default": "0", + "fieldname": "unsubscribed", + "fieldtype": "Check", + "hidden": 1, + "label": "Unsubscribed", + "no_copy": 1 + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "username", + "fieldtype": "Data", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Username", + "unique": 1 + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Language", + "options": "Language" + }, + { + "fieldname": "time_zone", + "fieldtype": "Autocomplete", + "label": "Time Zone" + }, + { + "description": "Get your globally recognized avatar from Gravatar.com", + "fieldname": "user_image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "User Image", + "no_copy": 1, + "print_hide": 1 + }, + { + "depends_on": "eval:in_list(['System User', 'Website User'], doc.user_type) && doc.enabled == 1", + "fieldname": "sb1", + "fieldtype": "Section Break", + "label": "Roles", + "permlevel": 1, + "read_only": 1 + }, + { + "allow_in_quick_entry": 1, + "fieldname": "role_profile_name", + "fieldtype": "Link", + "label": "Role Profile", + "options": "Role Profile", + "permlevel": 1 + }, + { + "fieldname": "roles_html", + "fieldtype": "HTML", + "label": "Roles HTML", + "read_only": 1 + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "hidden": 1, + "label": "Roles Assigned", + "options": "Has Role", + "permlevel": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "depends_on": "enabled", + "fieldname": "short_bio", + "fieldtype": "Tab Break", + "label": "More Information" + }, + { + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "oldfieldname": "gender", + "oldfieldtype": "Select", + "options": "Gender" + }, + { + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "options": "Phone" + }, + { + "fieldname": "mobile_no", + "fieldtype": "Data", + "label": "Mobile No", + "options": "Phone", + "unique": 1 + }, + { + "fieldname": "birth_date", + "fieldtype": "Date", + "label": "Birth Date", + "no_copy": 1, + "oldfieldname": "birth_date", + "oldfieldtype": "Date" + }, + { + "fieldname": "location", + "fieldtype": "Data", + "label": "Location", + "no_copy": 1 + }, + { + "fieldname": "banner_image", + "fieldtype": "Attach Image", + "label": "Banner Image" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fieldname": "interest", + "fieldtype": "Small Text", + "label": "Interests" + }, + { + "fieldname": "bio", + "fieldtype": "Small Text", + "label": "Bio", + "no_copy": 1 + }, + { + "default": "0", + "fieldname": "mute_sounds", + "fieldtype": "Check", + "label": "Mute Sounds" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.enabled && (!doc.__islocal || !cint(doc.send_welcome_email))", + "fieldname": "change_password", + "fieldtype": "Section Break", + "label": "Change Password" + }, + { + "fieldname": "new_password", + "fieldtype": "Password", + "label": "Set New Password", + "no_copy": 1 + }, + { + "default": "1", + "fieldname": "logout_all_sessions", + "fieldtype": "Check", + "label": "Logout From All Devices After Changing Password" + }, + { + "fieldname": "reset_password_key", + "fieldtype": "Data", + "hidden": 1, + "label": "Reset Password Key", + "no_copy": 1, + "permlevel": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "last_password_reset_date", + "fieldtype": "Date", + "hidden": 1, + "label": "Last Password Reset Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "redirect_url", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Redirect URL" + }, + { + "collapsible": 1, + "fieldname": "document_follow_notifications_section", + "fieldtype": "Section Break", + "label": "Document Follow" + }, + { + "default": "0", + "fieldname": "document_follow_notify", + "fieldtype": "Check", + "label": "Send Notifications For Documents Followed By Me" + }, + { + "default": "Daily", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "document_follow_frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "Hourly\nDaily\nWeekly" + }, + { + "collapsible": 1, + "depends_on": "enabled", + "fieldname": "email_settings", + "fieldtype": "Section Break", + "label": "Email" + }, + { + "default": "1", + "fieldname": "thread_notify", + "fieldtype": "Check", + "label": "Send Notifications For Email Threads" + }, + { + "default": "0", + "fieldname": "send_me_a_copy", + "fieldtype": "Check", + "label": "Send Me A Copy of Outgoing Emails" + }, + { + "default": "1", + "fieldname": "allowed_in_mentions", + "fieldtype": "Check", + "label": "Allowed In Mentions" + }, + { + "fieldname": "email_signature", + "fieldtype": "Small Text", + "label": "Email Signature", + "no_copy": 1 + }, + { + "fieldname": "user_emails", + "fieldtype": "Table", + "label": "User Emails", + "options": "User Email", + "permlevel": 1 + }, + { + "depends_on": "eval:in_list(['System User'], doc.user_type)", + "fieldname": "sb_allow_modules", + "fieldtype": "Section Break", + "label": "Allow Modules", + "permlevel": 1 + }, + { + "fieldname": "modules_html", + "fieldtype": "HTML", + "label": "Modules HTML", + "permlevel": 1 + }, + { + "fieldname": "block_modules", + "fieldtype": "Table", + "hidden": 1, + "label": "Block Modules", + "options": "Block Module", + "permlevel": 1 + }, + { + "fieldname": "home_settings", + "fieldtype": "Code", + "hidden": 1, + "label": "Home Settings" + }, + { + "description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", + "fieldname": "sb2", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Defaults", + "oldfieldtype": "Column Break", + "permlevel": 1, + "print_width": "50%", + "read_only": 1, + "width": "50%" + }, + { + "description": "Enter default value fields (keys) and values. If you add multiple values for a field, the first one will be picked. These defaults are also used to set \"match\" permission rules. To see list of fields, go to \"Customize Form\".", + "fieldname": "defaults", + "fieldtype": "Table", + "hidden": 1, + "label": "User Defaults", + "no_copy": 1, + "options": "DefaultValue" + }, + { + "collapsible": 1, + "depends_on": "enabled", + "fieldname": "sb3", + "fieldtype": "Section Break", + "label": "Security Settings", + "oldfieldtype": "Section Break", + "read_only": 1 + }, + { + "default": "2", + "fieldname": "simultaneous_sessions", + "fieldtype": "Int", + "label": "Simultaneous Sessions" + }, + { + "bold": 1, + "default": "System User", + "description": "If the user has any role checked, then the user becomes a \"System User\". \"System User\" has access to the desktop", + "fieldname": "user_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User Type", + "oldfieldname": "user_type", + "oldfieldtype": "Select", + "options": "User Type", + "permlevel": 1 + }, + { + "description": "Allow user to login only after this hour (0-24)", + "fieldname": "login_after", + "fieldtype": "Int", + "label": "Login After", + "permlevel": 1 + }, + { + "description": "Allow user to login only before this hour (0-24)", + "fieldname": "login_before", + "fieldtype": "Int", + "label": "Login Before", + "permlevel": 1 + }, + { + "description": "Restrict user from this IP address only. Multiple IP addresses can be added by separating with commas. Also accepts partial IP addresses like (111.111.111)", + "fieldname": "restrict_ip", + "fieldtype": "Small Text", + "label": "Restrict IP", + "permlevel": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.restrict_ip && doc.restrict_ip.length", + "description": "If enabled, user can login from any IP Address using Two Factor Auth, this can also be set for all users in System Settings", + "fieldname": "bypass_restrict_ip_check_if_2fa_enabled", + "fieldtype": "Check", + "label": "Bypass Restricted IP Address Check If Two Factor Auth Enabled" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "fieldname": "last_login", + "fieldtype": "Read Only", + "label": "Last Login", + "no_copy": 1, + "oldfieldname": "last_login", + "oldfieldtype": "Read Only", + "read_only": 1 + }, + { + "fieldname": "last_ip", + "fieldtype": "Read Only", + "label": "Last IP", + "no_copy": 1, + "oldfieldname": "last_ip", + "oldfieldtype": "Read Only", + "read_only": 1 + }, + { + "fieldname": "last_active", + "fieldtype": "Datetime", + "label": "Last Active", + "no_copy": 1, + "read_only": 1 + }, + { + "description": "Stores the JSON of last known versions of various installed apps. It is used to show release notes.", + "fieldname": "last_known_versions", + "fieldtype": "Text", + "hidden": 1, + "label": "Last Known Versions", + "read_only": 1 + }, + { + "collapsible": 1, + "depends_on": "enabled", + "fieldname": "third_party_authentication", + "fieldtype": "Section Break", + "label": "Third Party Authentication", + "permlevel": 1 + }, + { + "fieldname": "social_logins", + "fieldtype": "Table", + "label": "Social Logins", + "options": "User Social Login" + }, + { + "collapsible": 1, + "fieldname": "api_access", + "fieldtype": "Section Break", + "label": "API Access" + }, + { + "description": "API Key cannot be regenerated", + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key", + "permlevel": 1, + "read_only": 1, + "unique": 1 + }, + { + "fieldname": "generate_keys", + "fieldtype": "Button", + "label": "Generate Keys", + "permlevel": 1 + }, + { + "fieldname": "column_break_65", + "fieldtype": "Column Break" + }, + { + "fieldname": "api_secret", + "fieldtype": "Password", + "label": "API Secret", + "permlevel": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_63", + "fieldtype": "Column Break" + }, + { + "fieldname": "desk_theme", + "fieldtype": "Select", + "label": "Desk Theme", + "options": "Light\nDark\nAutomatic" + }, + { + "fieldname": "module_profile", + "fieldtype": "Link", + "label": "Module Profile", + "options": "Module Profile" + }, + { + "description": "Stores the datetime when the last reset password key was generated.", + "fieldname": "last_reset_password_key_generated_on", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Last Reset Password Key Generated On", + "permlevel": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_75", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "follow_created_documents", + "fieldtype": "Check", + "label": "Auto follow documents that you create" + }, + { + "default": "0", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "follow_commented_documents", + "fieldtype": "Check", + "label": "Auto follow documents that you comment on" + }, + { + "default": "0", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "follow_liked_documents", + "fieldtype": "Check", + "label": "Auto follow documents that you Like" + }, + { + "default": "0", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "follow_shared_documents", + "fieldtype": "Check", + "label": "Auto follow documents that are shared with you" + }, + { + "default": "0", + "depends_on": "eval:(doc.document_follow_notify== 1)", + "fieldname": "follow_assigned_documents", + "fieldtype": "Check", + "label": "Auto follow documents that are assigned to you" + }, + { + "fieldname": "user_details_tab", + "fieldtype": "Tab Break", + "label": "User Details" + }, + { + "fieldname": "roles_permissions_tab", + "fieldtype": "Tab Break", + "label": "Roles & Permissions" + }, + { + "fieldname": "settings_tab", + "fieldtype": "Tab Break", + "label": "Settings" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "collapsible": 1, + "fieldname": "desk_settings_section", + "fieldtype": "Section Break", + "label": "Desk Settings" + }, + { + "default": "{}", + "fieldname": "onboarding_status", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Onboarding Status" + } + ], + "icon": "fa fa-user", + "idx": 413, + "image_field": "user_image", + "links": [ + { + "group": "Profile", + "link_doctype": "Contact", + "link_fieldname": "user" + }, + { + "group": "Profile", + "link_doctype": "Blogger", + "link_fieldname": "user" + }, + { + "group": "Logs", + "link_doctype": "Access Log", + "link_fieldname": "user" + }, + { + "group": "Logs", + "link_doctype": "Activity Log", + "link_fieldname": "user" + }, + { + "group": "Logs", + "link_doctype": "Energy Point Log", + "link_fieldname": "user" + }, + { + "group": "Logs", + "link_doctype": "Route History", + "link_fieldname": "user" + }, + { + "group": "Settings", + "link_doctype": "User Permission", + "link_fieldname": "user" + }, + { + "group": "Settings", + "link_doctype": "Document Follow", + "link_fieldname": "user" + }, + { + "group": "Activity", + "link_doctype": "Communication", + "link_fieldname": "user" + }, + { + "group": "Activity", + "link_doctype": "ToDo", + "link_fieldname": "allocated_to" + }, + { + "group": "Integrations", + "link_doctype": "Token Cache", + "link_fieldname": "user" + } + ], + "modified": "2024-02-11 13:16:29.574666", + "modified_by": "Administrator", + "module": "Core", + "name": "User", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "role": "All", + "select": 1 + } + ], + "quick_entry": 1, + "route": "user", + "search_fields": "full_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "full_name", + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/user/user.py b/xhiveframework/core/doctype/user/user.py new file mode 100644 index 0000000..468d968 --- /dev/null +++ b/xhiveframework/core/doctype/user/user.py @@ -0,0 +1,1352 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from collections.abc import Iterable +from datetime import timedelta + +import xhiveframework +import xhiveframework.defaults +import xhiveframework.permissions +import xhiveframework.share +from xhiveframework import STANDARD_USERS, _, msgprint, throw +from xhiveframework.auth import MAX_PASSWORD_SIZE +from xhiveframework.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + create_notification_settings, + toggle_notifications, +) +from xhiveframework.desk.notifications import clear_notifications +from xhiveframework.model.delete_doc import check_if_doc_is_linked +from xhiveframework.model.document import Document +from xhiveframework.query_builder import DocType +from xhiveframework.rate_limiter import rate_limit +from xhiveframework.utils import ( + cint, + escape_html, + flt, + format_datetime, + get_formatted_email, + get_system_timezone, + has_gravatar, + now_datetime, + today, +) +from xhiveframework.utils.data import sha256_hash +from xhiveframework.utils.deprecations import deprecated +from xhiveframework.utils.password import check_password, get_password_reset_limit +from xhiveframework.utils.password import update_password as _update_password +from xhiveframework.utils.user import get_system_managers +from xhiveframework.website.utils import is_signup_disabled + + +class User(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.block_module.block_module import BlockModule + from xhiveframework.core.doctype.defaultvalue.defaultvalue import DefaultValue + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.core.doctype.user_email.user_email import UserEmail + from xhiveframework.core.doctype.user_social_login.user_social_login import UserSocialLogin + from xhiveframework.types import DF + + allowed_in_mentions: DF.Check + api_key: DF.Data | None + api_secret: DF.Password | None + banner_image: DF.AttachImage | None + bio: DF.SmallText | None + birth_date: DF.Date | None + block_modules: DF.Table[BlockModule] + bypass_restrict_ip_check_if_2fa_enabled: DF.Check + defaults: DF.Table[DefaultValue] + desk_theme: DF.Literal["Light", "Dark", "Automatic"] + document_follow_frequency: DF.Literal["Hourly", "Daily", "Weekly"] + document_follow_notify: DF.Check + email: DF.Data + email_signature: DF.SmallText | None + enabled: DF.Check + first_name: DF.Data + follow_assigned_documents: DF.Check + follow_commented_documents: DF.Check + follow_created_documents: DF.Check + follow_liked_documents: DF.Check + follow_shared_documents: DF.Check + full_name: DF.Data | None + gender: DF.Link | None + home_settings: DF.Code | None + interest: DF.SmallText | None + language: DF.Link | None + last_active: DF.Datetime | None + last_ip: DF.ReadOnly | None + last_known_versions: DF.Text | None + last_login: DF.ReadOnly | None + last_name: DF.Data | None + last_password_reset_date: DF.Date | None + last_reset_password_key_generated_on: DF.Datetime | None + location: DF.Data | None + login_after: DF.Int + login_before: DF.Int + logout_all_sessions: DF.Check + middle_name: DF.Data | None + mobile_no: DF.Data | None + module_profile: DF.Link | None + mute_sounds: DF.Check + new_password: DF.Password | None + onboarding_status: DF.SmallText | None + phone: DF.Data | None + redirect_url: DF.SmallText | None + reset_password_key: DF.Data | None + restrict_ip: DF.SmallText | None + role_profile_name: DF.Link | None + roles: DF.Table[HasRole] + send_me_a_copy: DF.Check + send_welcome_email: DF.Check + simultaneous_sessions: DF.Int + social_logins: DF.Table[UserSocialLogin] + thread_notify: DF.Check + time_zone: DF.Autocomplete | None + unsubscribed: DF.Check + user_emails: DF.Table[UserEmail] + user_image: DF.AttachImage | None + user_type: DF.Link | None + username: DF.Data | None + # end: auto-generated types + __new_password = None + + def __setup__(self): + # because it is handled separately + self.flags.ignore_save_passwords = ["new_password"] + + def autoname(self): + """set name as Email Address""" + if self.get("is_admin") or self.get("is_guest"): + self.name = self.first_name + else: + self.email = self.email.strip().lower() + self.name = self.email + + def onload(self): + from xhiveframework.config import get_modules_from_all_apps + + self.set_onload("all_modules", sorted(m.get("module_name") for m in get_modules_from_all_apps())) + + def before_insert(self): + self.flags.in_insert = True + throttle_user_creation() + + def after_insert(self): + create_notification_settings(self.name) + xhiveframework.cache.delete_key("users_for_mentions") + xhiveframework.cache.delete_key("enabled_users") + + def validate(self): + # clear new password + self.__new_password = self.new_password + self.new_password = "" + + if not xhiveframework.flags.in_test: + self.password_strength_test() + + if self.name not in STANDARD_USERS: + self.validate_email_type(self.email) + self.validate_email_type(self.name) + self.add_system_manager_role() + self.populate_role_profile_roles() + self.check_roles_added() + self.set_system_user() + self.set_full_name() + self.check_enable_disable() + self.ensure_unique_roles() + self.remove_all_roles_for_guest() + self.validate_username() + self.remove_disabled_roles() + self.validate_user_email_inbox() + ask_pass_update() + self.validate_allowed_modules() + self.validate_user_image() + self.set_time_zone() + + if self.language == "Loading...": + self.language = None + + if (self.name not in ["Administrator", "Guest"]) and (not self.get_social_login_userid("xhiveframework")): + self.set_social_login_userid("xhiveframework", xhiveframework.generate_hash(length=39)) + + def populate_role_profile_roles(self): + if self.role_profile_name: + role_profile = xhiveframework.get_doc("Role Profile", self.role_profile_name) + self.set("roles", []) + self.append_roles(*[role.role for role in role_profile.roles]) + + @deprecated + def validate_roles(self): + self.populate_role_profile_roles() + + def validate_allowed_modules(self): + if self.module_profile: + module_profile = xhiveframework.get_doc("Module Profile", self.module_profile) + self.set("block_modules", []) + for d in module_profile.get("block_modules"): + self.append("block_modules", {"module": d.module}) + + def validate_user_image(self): + if self.user_image and len(self.user_image) > 2000: + xhiveframework.throw(_("Not a valid User Image.")) + + def on_update(self): + # clear new password + self.share_with_self() + clear_notifications(user=self.name) + xhiveframework.clear_cache(user=self.name) + now = xhiveframework.flags.in_test or xhiveframework.flags.in_install + self.send_password_notification(self.__new_password) + xhiveframework.enqueue( + "xhiveframework.core.doctype.user.user.create_contact", + user=self, + ignore_mandatory=True, + now=now, + enqueue_after_commit=True, + ) + + if self.name not in STANDARD_USERS and not self.user_image: + xhiveframework.enqueue( + "xhiveframework.core.doctype.user.user.update_gravatar", + name=self.name, + now=now, + enqueue_after_commit=True, + ) + + # Set user selected timezone + if self.time_zone: + xhiveframework.defaults.set_default("time_zone", self.time_zone, self.name) + + if self.has_value_changed("enabled"): + xhiveframework.cache.delete_key("users_for_mentions") + xhiveframework.cache.delete_key("enabled_users") + elif self.has_value_changed("allow_in_mentions") or self.has_value_changed("user_type"): + xhiveframework.cache.delete_key("users_for_mentions") + + def has_website_permission(self, ptype, user, verbose=False): + """Returns true if current user is the session user""" + return self.name == xhiveframework.session.user + + def set_full_name(self): + self.full_name = " ".join(filter(None, [self.first_name, self.last_name])) + + def check_enable_disable(self): + # do not allow disabling administrator/guest + if not cint(self.enabled) and self.name in STANDARD_USERS: + xhiveframework.throw(_("User {0} cannot be disabled").format(self.name)) + + if not cint(self.enabled): + self.a_system_manager_should_exist() + + # clear sessions if disabled + if not cint(self.enabled) and getattr(xhiveframework.local, "login_manager", None): + xhiveframework.local.login_manager.logout(user=self.name) + + # toggle notifications based on the user's status + toggle_notifications(self.name, enable=cint(self.enabled)) + + def add_system_manager_role(self): + if self.is_system_manager_disabled(): + return + + # if adding system manager, do nothing + if not cint(self.enabled) or ( + "System Manager" in [user_role.role for user_role in self.get("roles")] + ): + return + + if ( + self.name not in STANDARD_USERS + and self.user_type == "System User" + and not self.get_other_system_managers() + and cint(xhiveframework.db.get_single_value("System Settings", "setup_complete")) + ): + msgprint(_("Adding System Manager to this User as there must be atleast one System Manager")) + self.append("roles", {"doctype": "Has Role", "role": "System Manager"}) + + if self.name == "Administrator": + # Administrator should always have System Manager Role + self.extend( + "roles", + [ + {"doctype": "Has Role", "role": "System Manager"}, + {"doctype": "Has Role", "role": "Administrator"}, + ], + ) + + def is_system_manager_disabled(self): + return xhiveframework.db.get_value("Role", {"name": "System Manager"}, ["disabled"]) + + def email_new_password(self, new_password=None): + if new_password and not self.flags.in_insert: + _update_password(user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions) + + def set_system_user(self): + """For the standard users like admin and guest, the user type is fixed.""" + user_type_mapper = {"Administrator": "System User", "Guest": "Website User"} + + if self.user_type and not xhiveframework.get_cached_value("User Type", self.user_type, "is_standard"): + if user_type_mapper.get(self.name): + self.user_type = user_type_mapper.get(self.name) + else: + self.set_roles_and_modules_based_on_user_type() + else: + """Set as System User if any of the given roles has desk_access""" + self.user_type = "System User" if self.has_desk_access() else "Website User" + + def set_roles_and_modules_based_on_user_type(self): + user_type_doc = xhiveframework.get_cached_doc("User Type", self.user_type) + if user_type_doc.role: + self.roles = [] + + # Check whether User has linked with the 'Apply User Permission On' doctype or not + if user_linked_with_permission_on_doctype(user_type_doc, self.name): + self.append("roles", {"role": user_type_doc.role}) + + xhiveframework.msgprint( + _("Role has been set as per the user type {0}").format(self.user_type), alert=True + ) + + user_type_doc.update_modules_in_user(self) + + def has_desk_access(self): + """Return true if any of the set roles has desk access""" + if not self.roles: + return False + + role_table = DocType("Role") + return xhiveframework.db.count( + role_table, + ((role_table.desk_access == 1) & (role_table.name.isin([d.role for d in self.roles]))), + ) + + def share_with_self(self): + if self.name in STANDARD_USERS: + return + + xhiveframework.share.add_docshare( + self.doctype, self.name, self.name, write=1, share=1, flags={"ignore_share_permission": True} + ) + + def validate_share(self, docshare): + pass + # if docshare.user == self.name: + # if self.user_type=="System User": + # if docshare.share != 1: + # xhiveframework.throw(_("Sorry! User should have complete access to their own record.")) + # else: + # xhiveframework.throw(_("Sorry! Sharing with Website User is prohibited.")) + + def send_password_notification(self, new_password): + try: + if self.flags.in_insert: + if self.name not in STANDARD_USERS: + if new_password: + # new password given, no email required + _update_password( + user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions + ) + + if not self.flags.no_welcome_mail and cint(self.send_welcome_email): + self.send_welcome_mail_to_user() + self.flags.email_sent = 1 + if xhiveframework.session.user != "Guest": + msgprint(_("Welcome email sent")) + return + else: + self.email_new_password(new_password) + + except xhiveframework.OutgoingEmailError: + xhiveframework.clear_last_message() + xhiveframework.msgprint( + _("Please setup default outgoing Email Account from Settings > Email Account"), alert=True + ) + # email server not set, don't send email + self.log_error("Unable to send new password notification") + + @Document.hook + def validate_reset_password(self): + pass + + def reset_password(self, send_email=False, password_expired=False): + from xhiveframework.utils import get_url + + key = xhiveframework.generate_hash() + hashed_key = sha256_hash(key) + self.db_set("reset_password_key", hashed_key) + self.db_set("last_reset_password_key_generated_on", now_datetime()) + + url = "/update-password?key=" + key + if password_expired: + url = "/update-password?key=" + key + "&password_expired=true" + + link = get_url(url) + if send_email: + self.password_reset_mail(link) + + return link + + def get_other_system_managers(self): + user_doctype = DocType("User").as_("user") + user_role_doctype = DocType("Has Role").as_("user_role") + return ( + xhiveframework.qb.from_(user_doctype) + .from_(user_role_doctype) + .select(user_doctype.name) + .where(user_role_doctype.role == "System Manager") + .where(user_doctype.enabled == 1) + .where(user_role_doctype.parent == user_doctype.name) + .where(user_role_doctype.parent.notin(["Administrator", self.name])) + .limit(1) + ).run() + + def get_fullname(self): + """get first_name space last_name""" + return (self.first_name or "") + (self.first_name and " " or "") + (self.last_name or "") + + def password_reset_mail(self, link): + reset_password_template = xhiveframework.db.get_system_setting("reset_password_template") + + self.send_login_mail( + _("Password Reset"), + "password_reset", + {"link": link}, + now=True, + custom_template=reset_password_template, + ) + + def send_welcome_mail_to_user(self): + from xhiveframework.utils import get_url + + link = self.reset_password() + subject = None + method = xhiveframework.get_hooks("welcome_email") + if method: + subject = xhiveframework.get_attr(method[-1])() + if not subject: + site_name = xhiveframework.db.get_default("site_name") or xhiveframework.get_conf().get("site_name") + if site_name: + subject = _("Welcome to {0}").format(site_name) + else: + subject = _("Complete Registration") + + welcome_email_template = xhiveframework.db.get_system_setting("welcome_email_template") + + self.send_login_mail( + subject, + "new_user", + dict( + link=link, + site_url=get_url(), + ), + custom_template=welcome_email_template, + ) + + def send_login_mail(self, subject, template, add_args, now=None, custom_template=None): + """send mail with login details""" + from xhiveframework.utils import get_url + from xhiveframework.utils.user import get_user_fullname + + created_by = get_user_fullname(xhiveframework.session["user"]) + if created_by == "Guest": + created_by = "Administrator" + + args = { + "first_name": self.first_name or self.last_name or "user", + "user": self.name, + "title": subject, + "login_url": get_url(), + "created_by": created_by, + } + + args.update(add_args) + + sender = ( + xhiveframework.session.user not in STANDARD_USERS and get_formatted_email(xhiveframework.session.user) or None + ) + + if custom_template: + from xhiveframework.email.doctype.email_template.email_template import get_email_template + + email_template = get_email_template(custom_template, args) + subject = email_template.get("subject") + content = email_template.get("message") + + xhiveframework.sendmail( + recipients=self.email, + sender=sender, + subject=subject, + template=template if not custom_template else None, + content=content if custom_template else None, + args=args, + header=[subject, "green"], + delayed=(not now) if now is not None else self.flags.delay_emails, + retry=3, + ) + + def a_system_manager_should_exist(self): + if self.is_system_manager_disabled(): + return + + if not self.get_other_system_managers(): + throw(_("There should remain at least one System Manager")) + + def on_trash(self): + xhiveframework.clear_cache(user=self.name) + if self.name in STANDARD_USERS: + throw(_("User {0} cannot be deleted").format(self.name)) + + self.a_system_manager_should_exist() + + # disable the user and log him/her out + self.enabled = 0 + if getattr(xhiveframework.local, "login_manager", None): + xhiveframework.local.login_manager.logout(user=self.name) + + # delete todos + xhiveframework.db.delete("ToDo", {"allocated_to": self.name}) + todo_table = DocType("ToDo") + ( + xhiveframework.qb.update(todo_table) + .set(todo_table.assigned_by, None) + .where(todo_table.assigned_by == self.name) + ).run() + + # delete events + xhiveframework.db.delete("Event", {"owner": self.name, "event_type": "Private"}) + + # delete shares + xhiveframework.db.delete("DocShare", {"user": self.name}) + # delete messages + table = DocType("Communication") + xhiveframework.db.delete( + table, + filters=( + (table.communication_type.isin(["Chat", "Notification"])) + & (table.reference_doctype == "User") + & ((table.reference_name == self.name) | table.owner == self.name) + ), + run=False, + ) + # unlink contact + table = DocType("Contact") + xhiveframework.qb.update(table).where(table.user == self.name).set(table.user, None).run() + + # delete notification settings + xhiveframework.delete_doc("Notification Settings", self.name, ignore_permissions=True) + + if self.get("allow_in_mentions"): + xhiveframework.cache.delete_key("users_for_mentions") + + xhiveframework.cache.delete_key("enabled_users") + + # delete user permissions + xhiveframework.db.delete("User Permission", {"user": self.name}) + + # Delete OAuth data + xhiveframework.db.delete("OAuth Authorization Code", {"user": self.name}) + xhiveframework.db.delete("Token Cache", {"user": self.name}) + + # Delete EPS data + xhiveframework.db.delete("Energy Point Log", {"user": self.name}) + + # Ask user to disable instead if document is still linked + try: + check_if_doc_is_linked(self) + except xhiveframework.LinkExistsError: + xhiveframework.throw(_("You can disable the user instead of deleting it."), xhiveframework.LinkExistsError) + + def before_rename(self, old_name, new_name, merge=False): + xhiveframework.clear_cache(user=old_name) + self.validate_rename(old_name, new_name) + + def validate_rename(self, old_name, new_name): + # do not allow renaming administrator and guest + if old_name in STANDARD_USERS: + throw(_("User {0} cannot be renamed").format(self.name)) + + self.validate_email_type(new_name) + + def validate_email_type(self, email): + from xhiveframework.utils import validate_email_address + + validate_email_address(email.strip(), True) + + def after_rename(self, old_name, new_name, merge=False): + tables = xhiveframework.db.get_tables() + for tab in tables: + desc = xhiveframework.db.get_table_columns_description(tab) + has_fields = [d.get("name") for d in desc if d.get("name") in ["owner", "modified_by"]] + for field in has_fields: + xhiveframework.db.sql( + """UPDATE `{}` + SET `{}` = {} + WHERE `{}` = {}""".format(tab, field, "%s", field, "%s"), + (new_name, old_name), + ) + + if xhiveframework.db.exists("Notification Settings", old_name): + xhiveframework.rename_doc("Notification Settings", old_name, new_name, force=True, show_alert=False) + + # set email + xhiveframework.db.set_value("User", new_name, "email", new_name) + + def append_roles(self, *roles): + """Add roles to user""" + current_roles = [d.role for d in self.get("roles")] + for role in roles: + if role in current_roles: + continue + self.append("roles", {"role": role}) + + def add_roles(self, *roles): + """Add roles to user and save""" + self.append_roles(*roles) + self.save() + + def remove_roles(self, *roles): + existing_roles = {d.role: d for d in self.get("roles")} + for role in roles: + if role in existing_roles: + self.get("roles").remove(existing_roles[role]) + + self.save() + + def remove_all_roles_for_guest(self): + if self.name == "Guest": + self.set("roles", list({d for d in self.get("roles") if d.role == "Guest"})) + + def remove_disabled_roles(self): + disabled_roles = [d.name for d in xhiveframework.get_all("Role", filters={"disabled": 1})] + for role in list(self.get("roles")): + if role.role in disabled_roles: + self.get("roles").remove(role) + + def ensure_unique_roles(self): + exists = [] + for d in self.get("roles"): + if (not d.role) or (d.role in exists): + self.get("roles").remove(d) + else: + exists.append(d.role) + + def validate_username(self): + if not self.username and self.is_new() and self.first_name: + self.username = xhiveframework.scrub(self.first_name) + + if not self.username: + return + + # strip space and @ + self.username = self.username.strip(" @") + + if self.username_exists(): + if self.user_type == "System User": + xhiveframework.msgprint(_("Username {0} already exists").format(self.username)) + self.suggest_username() + + self.username = "" + + def password_strength_test(self): + """test password strength""" + if self.flags.ignore_password_policy: + return + + if self.__new_password: + user_data = (self.first_name, self.middle_name, self.last_name, self.email, self.birth_date) + result = test_password_strength(self.__new_password, user_data=user_data) + feedback = result.get("feedback", None) + + if feedback and not feedback.get("password_policy_validation_passed", False): + handle_password_test_fail(feedback) + + def suggest_username(self): + def _check_suggestion(suggestion): + if self.username != suggestion and not self.username_exists(suggestion): + return suggestion + + return None + + # @firstname + username = _check_suggestion(xhiveframework.scrub(self.first_name)) + + if not username: + # @firstname_last_name + username = _check_suggestion(xhiveframework.scrub("{} {}".format(self.first_name, self.last_name or ""))) + + if username: + xhiveframework.msgprint(_("Suggested Username: {0}").format(username)) + + return username + + def username_exists(self, username=None): + return xhiveframework.db.get_value("User", {"username": username or self.username, "name": ("!=", self.name)}) + + def get_blocked_modules(self): + """Returns list of modules blocked for that user""" + return [d.module for d in self.block_modules] if self.block_modules else [] + + def validate_user_email_inbox(self): + """check if same email account added in User Emails twice""" + + email_accounts = [user_email.email_account for user_email in self.user_emails] + if len(email_accounts) != len(set(email_accounts)): + xhiveframework.throw(_("Email Account added multiple times")) + + def get_social_login_userid(self, provider: str): + try: + for p in self.social_logins: + if p.provider == provider: + return p.userid + except Exception: + return None + + def set_social_login_userid(self, provider, userid, username=None): + social_logins = {"provider": provider, "userid": userid} + + if username: + social_logins["username"] = username + + self.append("social_logins", social_logins) + + def get_restricted_ip_list(self): + return get_restricted_ip_list(self) + + @classmethod + def find_by_credentials(cls, user_name: str, password: str, validate_password: bool = True): + """Find the user by credentials. + + This is a login utility that needs to check login related system settings while finding the user. + 1. Find user by email ID by default + 2. If allow_login_using_mobile_number is set, you can use mobile number while finding the user. + 3. If allow_login_using_user_name is set, you can use username while finding the user. + """ + + login_with_mobile = cint( + xhiveframework.db.get_single_value("System Settings", "allow_login_using_mobile_number") + ) + login_with_username = cint( + xhiveframework.db.get_single_value("System Settings", "allow_login_using_user_name") + ) + + or_filters = [{"name": user_name}] + if login_with_mobile: + or_filters.append({"mobile_no": user_name}) + if login_with_username: + or_filters.append({"username": user_name}) + + users = xhiveframework.get_all("User", fields=["name", "enabled"], or_filters=or_filters, limit=1) + if not users: + return + + user = users[0] + user["is_authenticated"] = True + if validate_password: + try: + check_password(user["name"], password, delete_tracker_cache=False) + except xhiveframework.AuthenticationError: + user["is_authenticated"] = False + + return user + + def set_time_zone(self): + if not self.time_zone: + self.time_zone = get_system_timezone() + + def check_roles_added(self): + if self.user_type != "System User" or self.roles or not self.is_new(): + return + + xhiveframework.msgprint( + _("Newly created user {0} has no roles enabled.").format(xhiveframework.bold(self.name)), + title=_("No Roles Specified"), + indicator="orange", + primary_action={ + "label": _("Add Roles"), + "client_action": "xhiveframework.set_route", + "args": ["Form", self.doctype, self.name], + }, + ) + + +@xhiveframework.whitelist() +def get_timezones(): + import pytz + + return {"timezones": pytz.all_timezones} + + +@xhiveframework.whitelist() +def get_all_roles(): + """return all roles""" + active_domains = xhiveframework.get_active_domains() + + roles = xhiveframework.get_all( + "Role", + filters={ + "name": ("not in", xhiveframework.permissions.AUTOMATIC_ROLES), + "disabled": 0, + }, + or_filters={"ifnull(restrict_to_domain, '')": "", "restrict_to_domain": ("in", active_domains)}, + order_by="name", + ) + + return sorted([role.get("name") for role in roles]) + + +@xhiveframework.whitelist() +def get_roles(arg=None): + """get roles for a user""" + return xhiveframework.get_roles(xhiveframework.form_dict["uid"]) + + +@xhiveframework.whitelist() +def get_perm_info(role): + """get permission info""" + from xhiveframework.permissions import get_all_perms + + return get_all_perms(role) + + +@xhiveframework.whitelist(allow_guest=True, methods=["POST"]) +def update_password( + new_password: str, logout_all_sessions: int = 0, key: str | None = None, old_password: str | None = None +): + """Update password for the current user. + + Args: + new_password (str): New password. + logout_all_sessions (int, optional): If set to 1, all other sessions will be logged out. Defaults to 0. + key (str, optional): Password reset key. Defaults to None. + old_password (str, optional): Old password. Defaults to None. + """ + + if len(new_password) > MAX_PASSWORD_SIZE: + xhiveframework.throw(_("Password size exceeded the maximum allowed size.")) + + result = test_password_strength(new_password) + feedback = result.get("feedback", None) + + if feedback and not feedback.get("password_policy_validation_passed", False): + handle_password_test_fail(feedback) + + res = _get_user_for_update_password(key, old_password) + if res.get("message"): + xhiveframework.local.response.http_status_code = 410 + return res["message"] + else: + user = res["user"] + + logout_all_sessions = cint(logout_all_sessions) or xhiveframework.db.get_single_value( + "System Settings", "logout_on_password_reset" + ) + _update_password(user, new_password, logout_all_sessions=cint(logout_all_sessions)) + + user_doc, redirect_url = reset_user_data(user) + + # get redirect url from cache + redirect_to = xhiveframework.cache.hget("redirect_after_login", user) + if redirect_to: + redirect_url = redirect_to + xhiveframework.cache.hdel("redirect_after_login", user) + + xhiveframework.local.login_manager.login_as(user) + + xhiveframework.db.set_value("User", user, "last_password_reset_date", today()) + xhiveframework.db.set_value("User", user, "reset_password_key", "") + + if user_doc.user_type == "System User": + return "/app" + else: + return redirect_url or "/" + + +@xhiveframework.whitelist(allow_guest=True) +def test_password_strength(new_password: str, key=None, old_password=None, user_data: tuple | None = None): + from xhiveframework.utils.deprecations import deprecation_warning + from xhiveframework.utils.password_strength import test_password_strength as _test_password_strength + + if key is not None or old_password is not None: + deprecation_warning( + "Arguments `key` and `old_password` are deprecated in function `test_password_strength`." + ) + + enable_password_policy = xhiveframework.get_system_settings("enable_password_policy") or 0 + + if not enable_password_policy: + return {} + + if not user_data: + user_data = xhiveframework.db.get_value( + "User", xhiveframework.session.user, ["first_name", "middle_name", "last_name", "email", "birth_date"] + ) + + if new_password: + result = _test_password_strength(new_password, user_inputs=user_data) + password_policy_validation_passed = False + minimum_password_score = cint(xhiveframework.get_system_settings("minimum_password_score")) or 0 + + # score should be greater than 0 and minimum_password_score + if result.get("score") and result.get("score") >= minimum_password_score: + password_policy_validation_passed = True + + result["feedback"]["password_policy_validation_passed"] = password_policy_validation_passed + result.pop("password", None) + return result + + +@xhiveframework.whitelist() +def has_email_account(email: str): + return xhiveframework.get_list("Email Account", filters={"email_id": email}) + + +@xhiveframework.whitelist(allow_guest=False) +def get_email_awaiting(user): + return xhiveframework.get_all( + "User Email", + fields=["email_account", "email_id"], + filters={"awaiting_password": 1, "parent": user, "used_oauth": 0}, + ) + + +def ask_pass_update(): + # update the sys defaults as to awaiting users + from xhiveframework.utils import set_default + + password_list = xhiveframework.get_all( + "User Email", filters={"awaiting_password": 1, "used_oauth": 0}, pluck="parent", distinct=True + ) + set_default("email_user_password", ",".join(password_list)) + + +def _get_user_for_update_password(key, old_password): + # verify old password + result = xhiveframework._dict() + if key: + hashed_key = sha256_hash(key) + user = xhiveframework.db.get_value( + "User", {"reset_password_key": hashed_key}, ["name", "last_reset_password_key_generated_on"] + ) + result.user, last_reset_password_key_generated_on = user or (None, None) + if result.user: + reset_password_link_expiry = cint( + xhiveframework.db.get_single_value("System Settings", "reset_password_link_expiry_duration") + ) + if ( + reset_password_link_expiry + and now_datetime() + > last_reset_password_key_generated_on + timedelta(seconds=reset_password_link_expiry) + ): + result.message = _("The reset password link has been expired") + else: + result.message = _("The reset password link has either been used before or is invalid") + elif old_password: + # verify old password + xhiveframework.local.login_manager.check_password(xhiveframework.session.user, old_password) + user = xhiveframework.session.user + result.user = user + return result + + +def reset_user_data(user): + user_doc = xhiveframework.get_doc("User", user) + redirect_url = user_doc.redirect_url + user_doc.reset_password_key = "" + user_doc.redirect_url = "" + user_doc.save(ignore_permissions=True) + + return user_doc, redirect_url + + +@xhiveframework.whitelist(methods=["POST"]) +def verify_password(password): + xhiveframework.local.login_manager.check_password(xhiveframework.session.user, password) + + +@xhiveframework.whitelist(allow_guest=True) +def sign_up(email: str, full_name: str, redirect_to: str) -> tuple[int, str]: + if is_signup_disabled(): + xhiveframework.throw(_("Sign Up is disabled"), title=_("Not Allowed")) + + user = xhiveframework.db.get("User", {"email": email}) + if user: + if user.enabled: + return 0, _("Already Registered") + else: + return 0, _("Registered but disabled") + else: + if xhiveframework.db.get_creation_count("User", 60) > 300: + xhiveframework.respond_as_web_page( + _("Temporarily Disabled"), + _( + "Too many users signed up recently, so the registration is disabled. Please try back in an hour" + ), + http_status_code=429, + ) + + from xhiveframework.utils import random_string + + user = xhiveframework.get_doc( + { + "doctype": "User", + "email": email, + "first_name": escape_html(full_name), + "enabled": 1, + "new_password": random_string(10), + "user_type": "Website User", + } + ) + user.flags.ignore_permissions = True + user.flags.ignore_password_policy = True + user.insert() + + # set default signup role as per Portal Settings + default_role = xhiveframework.db.get_single_value("Portal Settings", "default_role") + if default_role: + user.add_roles(default_role) + + if redirect_to: + xhiveframework.cache.hset("redirect_after_login", user.name, redirect_to) + + if user.flags.email_sent: + return 1, _("Please check your email for verification") + else: + return 2, _("Please ask your administrator to verify your sign-up") + + +@xhiveframework.whitelist(allow_guest=True, methods=["POST"]) +@rate_limit(limit=get_password_reset_limit, seconds=60 * 60) +def reset_password(user: str) -> str: + try: + user: User = xhiveframework.get_doc("User", user) + if user.name == "Administrator": + return "not allowed" + if not user.enabled: + return "disabled" + + user.validate_reset_password() + user.reset_password(send_email=True) + + return xhiveframework.msgprint( + msg=_("Password reset instructions have been sent to your email"), + title=_("Password Email Sent"), + ) + except xhiveframework.DoesNotExistError: + xhiveframework.local.response["http_status_code"] = 404 + xhiveframework.clear_messages() + return "not found" + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def user_query(doctype, txt, searchfield, start, page_len, filters): + from xhiveframework.desk.reportview import get_filters_cond, get_match_cond + + doctype = "User" + conditions = [] + + user_type_condition = "and user_type != 'Website User'" + if filters and filters.get("ignore_user_type") and xhiveframework.session.data.user_type == "System User": + user_type_condition = "" + filters and filters.pop("ignore_user_type", None) + + txt = f"%{txt}%" + return xhiveframework.db.sql( + """SELECT `name`, CONCAT_WS(' ', first_name, middle_name, last_name) + FROM `tabUser` + WHERE `enabled`=1 + {user_type_condition} + AND `docstatus` < 2 + AND `name` NOT IN ({standard_users}) + AND ({key} LIKE %(txt)s + OR CONCAT_WS(' ', first_name, middle_name, last_name) LIKE %(txt)s) + {fcond} {mcond} + ORDER BY + CASE WHEN `name` LIKE %(txt)s THEN 0 ELSE 1 END, + CASE WHEN concat_ws(' ', first_name, middle_name, last_name) LIKE %(txt)s + THEN 0 ELSE 1 END, + NAME asc + LIMIT %(page_len)s OFFSET %(start)s + """.format( + user_type_condition=user_type_condition, + standard_users=", ".join(xhiveframework.db.escape(u) for u in STANDARD_USERS), + key=searchfield, + fcond=get_filters_cond(doctype, filters, conditions), + mcond=get_match_cond(doctype), + ), + dict(start=start, page_len=page_len, txt=txt), + ) + + +def get_total_users(): + """Returns total no. of system users""" + return flt( + xhiveframework.db.sql( + """SELECT SUM(`simultaneous_sessions`) + FROM `tabUser` + WHERE `enabled` = 1 + AND `user_type` = 'System User' + AND `name` NOT IN ({})""".format(", ".join(["%s"] * len(STANDARD_USERS))), + STANDARD_USERS, + )[0][0] + ) + + +def get_system_users(exclude_users: Iterable[str] | str | None = None, limit: int | None = None): + _excluded_users = list(STANDARD_USERS) + if isinstance(exclude_users, str): + _excluded_users.append(exclude_users) + elif isinstance(exclude_users, Iterable): + _excluded_users.extend(exclude_users) + + return xhiveframework.get_all( + "User", + filters={ + "enabled": 1, + "user_type": ("!=", "Website User"), + "name": ("not in", _excluded_users), + }, + pluck="name", + limit=limit, + ) + + +def get_active_users(): + """Returns No. of system users who logged in, in the last 3 days""" + return xhiveframework.db.sql( + """select count(*) from `tabUser` + where enabled = 1 and user_type != 'Website User' + and name not in ({}) + and hour(timediff(now(), last_active)) < 72""".format(", ".join(["%s"] * len(STANDARD_USERS))), + STANDARD_USERS, + )[0][0] + + +def get_website_users(): + """Returns total no. of website users""" + return xhiveframework.db.count("User", filters={"enabled": True, "user_type": "Website User"}) + + +def get_active_website_users(): + """Returns No. of website users who logged in, in the last 3 days""" + return xhiveframework.db.sql( + """select count(*) from `tabUser` + where enabled = 1 and user_type = 'Website User' + and hour(timediff(now(), last_active)) < 72""" + )[0][0] + + +def get_permission_query_conditions(user): + if user == "Administrator": + return "" + else: + return """(`tabUser`.name not in ({standard_users}))""".format( + standard_users=", ".join(xhiveframework.db.escape(user) for user in STANDARD_USERS) + ) + + +def has_permission(doc, user): + if (user != "Administrator") and (doc.name in STANDARD_USERS): + # dont allow non Administrator user to view / edit Administrator user + return False + + +def notify_admin_access_to_system_manager(login_manager=None): + if ( + login_manager + and login_manager.user == "Administrator" + and xhiveframework.local.conf.notify_admin_access_to_system_manager + ): + site = '{0}'.format(xhiveframework.local.request.host_url) + date_and_time = "{}".format(format_datetime(now_datetime(), format_string="medium")) + ip_address = xhiveframework.local.request_ip + + access_message = _("Administrator accessed {0} on {1} via IP Address {2}.").format( + site, date_and_time, ip_address + ) + + xhiveframework.sendmail( + recipients=get_system_managers(), + subject=_("Administrator Logged In"), + template="administrator_logged_in", + args={"access_message": access_message}, + header=["Access Notification", "orange"], + ) + + +def handle_password_test_fail(feedback: dict): + # Backward compatibility + if "feedback" in feedback: + feedback = feedback["feedback"] + + suggestions = feedback.get("suggestions", []) + warning = feedback.get("warning", "") + + xhiveframework.throw(msg=" ".join([warning, *suggestions]), title=_("Invalid Password")) + + +def update_gravatar(name): + gravatar = has_gravatar(name) + if gravatar: + xhiveframework.db.set_value("User", name, "user_image", gravatar) + + +def throttle_user_creation(): + if xhiveframework.flags.in_import: + return + + if xhiveframework.db.get_creation_count("User", 60) > xhiveframework.local.conf.get("throttle_user_limit", 60): + xhiveframework.throw(_("Throttled")) + + +@xhiveframework.whitelist() +def get_role_profile(role_profile: str): + return xhiveframework.get_doc("Role Profile", {"role_profile": role_profile}).roles + + +@xhiveframework.whitelist() +def get_module_profile(module_profile: str): + module_profile = xhiveframework.get_doc("Module Profile", {"module_profile_name": module_profile}) + return module_profile.get("block_modules") + + +def create_contact(user, ignore_links=False, ignore_mandatory=False): + from xhiveframework.contacts.doctype.contact.contact import get_contact_name + + if user.name in ["Administrator", "Guest"]: + return + + contact_name = get_contact_name(user.email) + if not contact_name: + try: + contact = xhiveframework.get_doc( + { + "doctype": "Contact", + "first_name": user.first_name, + "last_name": user.last_name, + "user": user.name, + "gender": user.gender, + } + ) + + if user.email: + contact.add_email(user.email, is_primary=True) + + if user.phone: + contact.add_phone(user.phone, is_primary_phone=True) + + if user.mobile_no: + contact.add_phone(user.mobile_no, is_primary_mobile_no=True) + + contact.insert( + ignore_permissions=True, ignore_links=ignore_links, ignore_mandatory=ignore_mandatory + ) + except xhiveframework.DuplicateEntryError: + pass + else: + try: + contact = xhiveframework.get_doc("Contact", contact_name) + contact.first_name = user.first_name + contact.last_name = user.last_name + contact.gender = user.gender + + # Add mobile number if phone does not exists in contact + if user.phone and not any(new_contact.phone == user.phone for new_contact in contact.phone_nos): + # Set primary phone if there is no primary phone number + contact.add_phone( + user.phone, + is_primary_phone=not any( + new_contact.is_primary_phone == 1 for new_contact in contact.phone_nos + ), + ) + + # Add mobile number if mobile does not exists in contact + if user.mobile_no and not any( + new_contact.phone == user.mobile_no for new_contact in contact.phone_nos + ): + # Set primary mobile if there is no primary mobile number + contact.add_phone( + user.mobile_no, + is_primary_mobile_no=not any( + new_contact.is_primary_mobile_no == 1 for new_contact in contact.phone_nos + ), + ) + + contact.save(ignore_permissions=True) + except xhiveframework.TimestampMismatchError: + raise xhiveframework.RetryBackgroundJobError + + +def get_restricted_ip_list(user): + if not user.restrict_ip: + return + + return [i.strip() for i in user.restrict_ip.split(",")] + + +@xhiveframework.whitelist(methods=["POST"]) +def generate_keys(user: str): + """ + generate api key and api secret + + :param user: str + """ + xhiveframework.only_for("System Manager") + user_details: User = xhiveframework.get_doc("User", user) + api_secret = xhiveframework.generate_hash(length=15) + # if api key is not set generate api key + if not user_details.api_key: + api_key = xhiveframework.generate_hash(length=15) + user_details.api_key = api_key + user_details.api_secret = api_secret + user_details.save() + + return {"api_secret": api_secret} + + +@xhiveframework.whitelist() +def switch_theme(theme): + if theme in ["Dark", "Light", "Automatic"]: + xhiveframework.db.set_value("User", xhiveframework.session.user, "desk_theme", theme) + + +def get_enabled_users(): + def _get_enabled_users(): + enabled_users = xhiveframework.get_all("User", filters={"enabled": "1"}, pluck="name") + return enabled_users + + return xhiveframework.cache.get_value("enabled_users", _get_enabled_users) + + +@xhiveframework.whitelist(methods=["POST"]) +def impersonate(user: str, reason: str): + # Note: For now we only allow admins, we MIGHT allow system manager in future. + # All the impersonation code doesn't assume anything about user. + xhiveframework.only_for("Administrator") + + impersonator = xhiveframework.session.user + xhiveframework.get_doc( + { + "doctype": "Activity Log", + "user": user, + "status": "Success", + "subject": _("User {0} impersonated as {1}").format(impersonator, user), + "operation": "Impersonate", + } + ).insert(ignore_permissions=True, ignore_links=True) + + notification = xhiveframework.new_doc( + "Notification Log", + for_user=user, + from_user=xhiveframework.session.user, + subject=_("{0} just impersonated as you. They gave this reason: {1}").format(impersonator, reason), + ) + notification.set("type", "Alert") + notification.insert(ignore_permissions=True) + xhiveframework.local.login_manager.impersonate(user) diff --git a/xhiveframework/core/doctype/user/user_list.js b/xhiveframework/core/doctype/user/user_list.js new file mode 100644 index 0000000..59a3a64 --- /dev/null +++ b/xhiveframework/core/doctype/user/user_list.js @@ -0,0 +1,19 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.listview_settings["User"] = { + add_fields: ["enabled", "user_type", "user_image"], + filters: [["enabled", "=", 1]], + prepare_data: function (data) { + data["user_for_avatar"] = data["name"]; + }, + get_indicator: function (doc) { + if (doc.enabled) { + return [__("Active"), "green", "enabled,=,1"]; + } else { + return [__("Disabled"), "grey", "enabled,=,0"]; + } + }, +}; + +xhiveframework.help.youtube_id["User"] = "w"; diff --git a/xhiveframework/core/doctype/user_document_type/__init__.py b/xhiveframework/core/doctype/user_document_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_document_type/user_document_type.json b/xhiveframework/core/doctype/user_document_type/user_document_type.json new file mode 100644 index 0000000..69983a2 --- /dev/null +++ b/xhiveframework/core/doctype/user_document_type/user_document_type.json @@ -0,0 +1,109 @@ +{ + "actions": [], + "creation": "2021-01-13 01:51:40.158521", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "column_break_2", + "is_custom", + "permissions_section", + "read", + "write", + "create", + "column_break_8", + "submit", + "cancel", + "amend", + "delete" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "permissions_section", + "fieldtype": "Section Break", + "label": "Role Permissions" + }, + { + "default": "1", + "fieldname": "read", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Read" + }, + { + "default": "0", + "fieldname": "write", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Write" + }, + { + "default": "0", + "fieldname": "create", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Create" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "document_type.custom", + "fieldname": "is_custom", + "fieldtype": "Check", + "label": "Is Custom", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "submit", + "fieldtype": "Check", + "label": "Submit" + }, + { + "default": "0", + "fieldname": "cancel", + "fieldtype": "Check", + "label": "Cancel" + }, + { + "default": "0", + "fieldname": "amend", + "fieldtype": "Check", + "label": "Amend" + }, + { + "default": "0", + "fieldname": "delete", + "fieldtype": "Check", + "label": "Delete" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-03-16 00:32:24.414313", + "modified_by": "Administrator", + "module": "Core", + "name": "User Document Type", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_document_type/user_document_type.py b/xhiveframework/core/doctype/user_document_type/user_document_type.py new file mode 100644 index 0000000..746991d --- /dev/null +++ b/xhiveframework/core/doctype/user_document_type/user_document_type.py @@ -0,0 +1,30 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class UserDocumentType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + amend: DF.Check + cancel: DF.Check + create: DF.Check + delete: DF.Check + document_type: DF.Link + is_custom: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + read: DF.Check + submit: DF.Check + write: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/user_email/__init__.py b/xhiveframework/core/doctype/user_email/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_email/user_email.json b/xhiveframework/core/doctype/user_email/user_email.json new file mode 100644 index 0000000..6e3f813 --- /dev/null +++ b/xhiveframework/core/doctype/user_email/user_email.json @@ -0,0 +1,73 @@ +{ + "actions": [], + "creation": "2016-03-30 10:04:25.828742", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email_account", + "email_id", + "column_break_3", + "awaiting_password", + "used_oauth", + "enable_outgoing" + ], + "fields": [ + { + "fieldname": "email_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Email Account", + "options": "Email Account", + "reqd": 1 + }, + { + "fetch_from": "email_account.email_id", + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email ID", + "options": "Email", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "email_account.awaiting_password", + "fieldname": "awaiting_password", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Awaiting Password", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "email_account.enable_outgoing", + "fieldname": "enable_outgoing", + "fieldtype": "Check", + "label": "Enable Outgoing", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "used_oauth", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Used OAuth", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2022-06-03 14:25:46.944733", + "modified_by": "Administrator", + "module": "Core", + "name": "User Email", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_email/user_email.py b/xhiveframework/core/doctype/user_email/user_email.py new file mode 100644 index 0000000..8bfe1da --- /dev/null +++ b/xhiveframework/core/doctype/user_email/user_email.py @@ -0,0 +1,25 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class UserEmail(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + awaiting_password: DF.Check + email_account: DF.Link + email_id: DF.Data | None + enable_outgoing: DF.Check + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + used_oauth: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/user_group/__init__.py b/xhiveframework/core/doctype/user_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_group/test_user_group.py b/xhiveframework/core/doctype/user_group/test_user_group.py new file mode 100644 index 0000000..a7fe566 --- /dev/null +++ b/xhiveframework/core/doctype/user_group/test_user_group.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestUserGroup(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/user_group/user_group.js b/xhiveframework/core/doctype/user_group/user_group.js new file mode 100644 index 0000000..d27ce65 --- /dev/null +++ b/xhiveframework/core/doctype/user_group/user_group.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("User Group", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/user_group/user_group.json b/xhiveframework/core/doctype/user_group/user_group.json new file mode 100644 index 0000000..a67ceea --- /dev/null +++ b/xhiveframework/core/doctype/user_group/user_group.json @@ -0,0 +1,50 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2021-04-12 15:17:24.751710", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user_group_members" + ], + "fields": [ + { + "fieldname": "user_group_members", + "fieldtype": "Table MultiSelect", + "label": "User Group Members", + "options": "User Group Member", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 22:25:16.941457", + "modified_by": "Administrator", + "module": "Core", + "name": "User Group", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Desk User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_group/user_group.py b/xhiveframework/core/doctype/user_group/user_group.py new file mode 100644 index 0000000..ce696d0 --- /dev/null +++ b/xhiveframework/core/doctype/user_group/user_group.py @@ -0,0 +1,27 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework + +# import xhiveframework +from xhiveframework.model.document import Document + + +class UserGroup(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.user_group_member.user_group_member import UserGroupMember + from xhiveframework.types import DF + + user_group_members: DF.TableMultiSelect[UserGroupMember] + + # end: auto-generated types + def after_insert(self): + xhiveframework.cache.delete_key("user_groups") + + def on_trash(self): + xhiveframework.cache.delete_key("user_groups") diff --git a/xhiveframework/core/doctype/user_group_member/__init__.py b/xhiveframework/core/doctype/user_group_member/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_group_member/test_user_group_member.py b/xhiveframework/core/doctype/user_group_member/test_user_group_member.py new file mode 100644 index 0000000..4ec829f --- /dev/null +++ b/xhiveframework/core/doctype/user_group_member/test_user_group_member.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestUserGroupMember(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/core/doctype/user_group_member/user_group_member.js b/xhiveframework/core/doctype/user_group_member/user_group_member.js new file mode 100644 index 0000000..566f883 --- /dev/null +++ b/xhiveframework/core/doctype/user_group_member/user_group_member.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("User Group Member", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/core/doctype/user_group_member/user_group_member.json b/xhiveframework/core/doctype/user_group_member/user_group_member.json new file mode 100644 index 0000000..d2ff149 --- /dev/null +++ b/xhiveframework/core/doctype/user_group_member/user_group_member.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2021-04-12 15:16:29.279107", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-12 15:17:18.773046", + "modified_by": "Administrator", + "module": "Core", + "name": "User Group Member", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_group_member/user_group_member.py b/xhiveframework/core/doctype/user_group_member/user_group_member.py new file mode 100644 index 0000000..7ae61da --- /dev/null +++ b/xhiveframework/core/doctype/user_group_member/user_group_member.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class UserGroupMember(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + user: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/user_permission/__init__.py b/xhiveframework/core/doctype/user_permission/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_permission/test_user_permission.py b/xhiveframework/core/doctype/user_permission/test_user_permission.py new file mode 100644 index 0000000..38a05c0 --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/test_user_permission.py @@ -0,0 +1,332 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See LICENSE +import xhiveframework +from xhiveframework.core.doctype.doctype.test_doctype import new_doctype +from xhiveframework.core.doctype.user_permission.user_permission import ( + add_user_permissions, + remove_applicable, +) +from xhiveframework.permissions import add_permission, has_user_permission +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.website.doctype.blog_post.test_blog_post import make_test_blog + + +class TestUserPermission(XhiveFrameworkTestCase): + def setUp(self): + test_users = ( + "test_bulk_creation_update@example.com", + "test_user_perm1@example.com", + "nested_doc_user@example.com", + ) + xhiveframework.db.delete("User Permission", {"user": ("in", test_users)}) + xhiveframework.delete_doc_if_exists("DocType", "Person") + xhiveframework.db.sql_ddl("DROP TABLE IF EXISTS `tabPerson`") + xhiveframework.delete_doc_if_exists("DocType", "Doc A") + xhiveframework.db.sql_ddl("DROP TABLE IF EXISTS `tabDoc A`") + + def test_default_user_permission_validation(self): + user = create_user("test_default_permission@example.com") + param = get_params(user, "User", user.name, is_default=1) + add_user_permissions(param) + # create a duplicate entry with default + perm_user = create_user("test_user_perm@example.com") + param = get_params(user, "User", perm_user.name, is_default=1) + self.assertRaises(xhiveframework.ValidationError, add_user_permissions, param) + + def test_default_user_permission_corectness(self): + user = create_user("test_default_corectness_permission_1@example.com") + param = get_params(user, "User", user.name, is_default=1, hide_descendants=1) + add_user_permissions(param) + # create a duplicate entry with default + perm_user = create_user("test_default_corectness2@example.com") + test_blog = make_test_blog() + param = get_params(perm_user, "Blog Post", test_blog.name, is_default=1, hide_descendants=1) + add_user_permissions(param) + xhiveframework.db.delete("User Permission", filters={"for_value": test_blog.name}) + xhiveframework.delete_doc("Blog Post", test_blog.name) + + def test_default_user_permission(self): + xhiveframework.set_user("Administrator") + user = create_user("test_user_perm1@example.com", "Website Manager") + for category in ["general", "public"]: + if not xhiveframework.db.exists("Blog Category", category): + xhiveframework.get_doc({"doctype": "Blog Category", "title": category}).insert() + + param = get_params(user, "Blog Category", "general", is_default=1) + add_user_permissions(param) + + param = get_params(user, "Blog Category", "public") + add_user_permissions(param) + + xhiveframework.set_user("test_user_perm1@example.com") + doc = xhiveframework.new_doc("Blog Post") + + self.assertEqual(doc.blog_category, "general") + xhiveframework.set_user("Administrator") + + def test_apply_to_all(self): + """Create User permission for User having access to all applicable Doctypes""" + user = create_user("test_bulk_creation_update@example.com") + param = get_params(user, "User", user.name) + is_created = add_user_permissions(param) + self.assertEqual(is_created, 1) + + def test_for_apply_to_all_on_update_from_apply_all(self): + user = create_user("test_bulk_creation_update@example.com") + param = get_params(user, "User", user.name) + + # Initially create User Permission document with apply_to_all checked + is_created = add_user_permissions(param) + + self.assertEqual(is_created, 1) + is_created = add_user_permissions(param) + + # User Permission should not be changed + self.assertEqual(is_created, 0) + + def test_for_applicable_on_update_from_apply_to_all(self): + """Update User Permission from all to some applicable Doctypes""" + user = create_user("test_bulk_creation_update@example.com") + param = get_params(user, "User", user.name, applicable=["Comment", "Contact"]) + + # Initially create User Permission document with apply_to_all checked + is_created = add_user_permissions(get_params(user, "User", user.name)) + + self.assertEqual(is_created, 1) + + is_created = add_user_permissions(param) + xhiveframework.db.commit() + + removed_apply_to_all = xhiveframework.db.exists("User Permission", get_exists_param(user)) + is_created_applicable_first = xhiveframework.db.exists( + "User Permission", get_exists_param(user, applicable="Comment") + ) + is_created_applicable_second = xhiveframework.db.exists( + "User Permission", get_exists_param(user, applicable="Contact") + ) + + # Check that apply_to_all is removed + self.assertIsNone(removed_apply_to_all) + + # Check that User Permissions for applicable is created + self.assertIsNotNone(is_created_applicable_first) + self.assertIsNotNone(is_created_applicable_second) + self.assertEqual(is_created, 1) + + def test_for_apply_to_all_on_update_from_applicable(self): + """Update User Permission from some to all applicable Doctypes""" + user = create_user("test_bulk_creation_update@example.com") + param = get_params(user, "User", user.name) + + # create User permissions that with applicable + is_created = add_user_permissions( + get_params(user, "User", user.name, applicable=["Comment", "Contact"]) + ) + + self.assertEqual(is_created, 1) + + is_created = add_user_permissions(param) + is_created_apply_to_all = xhiveframework.db.exists("User Permission", get_exists_param(user)) + removed_applicable_first = xhiveframework.db.exists( + "User Permission", get_exists_param(user, applicable="Comment") + ) + removed_applicable_second = xhiveframework.db.exists( + "User Permission", get_exists_param(user, applicable="Contact") + ) + + # To check that a User permission with apply_to_all exists + self.assertIsNotNone(is_created_apply_to_all) + + # Check that all User Permission with applicable is removed + self.assertIsNone(removed_applicable_first) + self.assertIsNone(removed_applicable_second) + self.assertEqual(is_created, 1) + + def test_user_perm_for_nested_doctype(self): + """Test if descendants' visibility is controlled for a nested DocType.""" + from xhiveframework.core.doctype.doctype.test_doctype import new_doctype + + user = create_user("nested_doc_user@example.com", "Blogger") + if not xhiveframework.db.exists("DocType", "Person"): + doc = new_doctype( + "Person", + fields=[{"label": "Person Name", "fieldname": "person_name", "fieldtype": "Data"}], + unique=0, + ) + doc.is_tree = 1 + doc.insert() + + parent_record = xhiveframework.get_doc({"doctype": "Person", "person_name": "Parent", "is_group": 1}).insert() + + child_record = xhiveframework.get_doc( + { + "doctype": "Person", + "person_name": "Child", + "is_group": 0, + "parent_person": parent_record.name, + } + ).insert() + + add_user_permissions(get_params(user, "Person", parent_record.name)) + + # check if adding perm on a group record, makes child record visible + self.assertTrue(has_user_permission(xhiveframework.get_doc("Person", parent_record.name), user.name)) + self.assertTrue(has_user_permission(xhiveframework.get_doc("Person", child_record.name), user.name)) + + # give access of Parent DocType to Blogger role + add_permission("Person", "Blogger") + xhiveframework.set_user(user.name) + visible_names = xhiveframework.get_list( + doctype="Person", + pluck="person_name", + ) + + user_permission = xhiveframework.get_doc( + "User Permission", {"allow": "Person", "for_value": parent_record.name} + ) + user_permission.hide_descendants = 1 + user_permission.save(ignore_permissions=True) + + # check if adding perm on a group record with hide_descendants enabled, + # hides child records + self.assertTrue(has_user_permission(xhiveframework.get_doc("Person", parent_record.name), user.name)) + self.assertFalse(has_user_permission(xhiveframework.get_doc("Person", child_record.name), user.name)) + + visible_names_after_hide_descendants = xhiveframework.get_list( + "Person", + pluck="person_name", + ) + self.assertEqual(visible_names, ["Child", "Parent"]) + self.assertEqual(visible_names_after_hide_descendants, ["Parent"]) + xhiveframework.set_user("Administrator") + + def test_user_perm_on_new_doc_with_field_default(self): + """Test User Perm impact on xhiveframework.new_doc. with *field* default value""" + xhiveframework.set_user("Administrator") + user = create_user("new_doc_test@example.com", "Blogger") + + # make a doctype "Doc A" with 'doctype' link field and default value ToDo + if not xhiveframework.db.exists("DocType", "Doc A"): + doc = new_doctype( + "Doc A", + fields=[ + { + "label": "DocType", + "fieldname": "doc", + "fieldtype": "Link", + "options": "DocType", + "default": "ToDo", + } + ], + unique=0, + ) + doc.insert() + + # make User Perm on DocType 'ToDo' in Assignment Rule (unrelated doctype) + add_user_permissions(get_params(user, "DocType", "ToDo", applicable=["Assignment Rule"])) + xhiveframework.set_user("new_doc_test@example.com") + + new_doc = xhiveframework.new_doc("Doc A") + + # User perm is created on ToDo but for doctype Assignment Rule only + # it should not have impact on Doc A + self.assertEqual(new_doc.doc, "ToDo") + + xhiveframework.set_user("Administrator") + remove_applicable(["Assignment Rule"], "new_doc_test@example.com", "DocType", "ToDo") + + def test_user_perm_on_new_doc_with_user_default(self): + """Test User Perm impact on xhiveframework.new_doc. with *user* default value""" + from xhiveframework.core.doctype.session_default_settings.session_default_settings import ( + clear_session_defaults, + set_session_default_values, + ) + + xhiveframework.set_user("Administrator") + user = create_user("user_default_test@example.com", "Blogger") + + # make a doctype "Doc A" with 'doctype' link field + if not xhiveframework.db.exists("DocType", "Doc A"): + doc = new_doctype( + "Doc A", + fields=[ + { + "label": "DocType", + "fieldname": "doc", + "fieldtype": "Link", + "options": "DocType", + } + ], + unique=0, + ) + doc.insert() + + # create a 'DocType' session default field + if not xhiveframework.db.exists("Session Default", {"ref_doctype": "DocType"}): + settings = xhiveframework.get_single("Session Default Settings") + settings.append("session_defaults", {"ref_doctype": "DocType"}) + settings.save() + + # make User Perm on DocType 'ToDo' in Assignment Rule (unrelated doctype) + add_user_permissions(get_params(user, "DocType", "ToDo", applicable=["Assignment Rule"])) + + # User default Doctype value is ToDo via Session Defaults + xhiveframework.set_user("user_default_test@example.com") + set_session_default_values({"doc": "ToDo"}) + + new_doc = xhiveframework.new_doc("Doc A") + + # User perm is created on ToDo but for doctype Assignment Rule only + # it should not have impact on Doc A + self.assertEqual(new_doc.doc, "ToDo") + + xhiveframework.set_user("Administrator") + clear_session_defaults() + remove_applicable(["Assignment Rule"], "user_default_test@example.com", "DocType", "ToDo") + + +def create_user(email, *roles): + """create user with role system manager""" + if xhiveframework.db.exists("User", email): + return xhiveframework.get_doc("User", email) + + user = xhiveframework.new_doc("User") + user.email = email + user.first_name = email.split("@", 1)[0] + + if not roles: + roles = ("System Manager",) + + user.add_roles(*roles) + return user + + +def get_params(user, doctype, docname, is_default=0, hide_descendants=0, applicable=None): + """Return param to insert""" + param = { + "user": user.name, + "doctype": doctype, + "docname": docname, + "is_default": is_default, + "apply_to_all_doctypes": 1, + "applicable_doctypes": [], + "hide_descendants": hide_descendants, + } + if applicable: + param.update({"apply_to_all_doctypes": 0}) + param.update({"applicable_doctypes": applicable}) + return param + + +def get_exists_param(user, applicable=None): + """param to check existing Document""" + param = { + "user": user.name, + "allow": "User", + "for_value": user.name, + } + if applicable: + param.update({"applicable_for": applicable}) + else: + param.update({"apply_to_all_doctypes": 1}) + return param diff --git a/xhiveframework/core/doctype/user_permission/user_permission.js b/xhiveframework/core/doctype/user_permission/user_permission.js new file mode 100644 index 0000000..9a438de --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/user_permission.js @@ -0,0 +1,58 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("User Permission", { + setup: (frm) => { + frm.set_query("allow", () => { + return { + filters: { + issingle: 0, + istable: 0, + }, + }; + }); + + frm.set_query("applicable_for", () => { + return { + query: "xhiveframework.core.doctype.user_permission.user_permission.get_applicable_for_doctype_list", + doctype: frm.doc.allow, + }; + }); + }, + + refresh: (frm) => { + frm.add_custom_button(__("View Permitted Documents"), () => + xhiveframework.set_route("query-report", "Permitted Documents For User", { + user: frm.doc.user, + }) + ); + frm.trigger("set_applicable_for_constraint"); + frm.trigger("toggle_hide_descendants"); + }, + + allow: (frm) => { + if (frm.doc.allow) { + if (frm.doc.for_value) { + frm.set_value("for_value", null); + } + frm.trigger("toggle_hide_descendants"); + } + }, + + apply_to_all_doctypes: (frm) => { + frm.trigger("set_applicable_for_constraint"); + }, + + set_applicable_for_constraint: (frm) => { + frm.toggle_reqd("applicable_for", !frm.doc.apply_to_all_doctypes); + + if (frm.doc.apply_to_all_doctypes && frm.doc.applicable_for) { + frm.set_value("applicable_for", null, null, true); + } + }, + + toggle_hide_descendants: (frm) => { + let show = xhiveframework.boot.nested_set_doctypes.includes(frm.doc.allow); + frm.toggle_display("hide_descendants", show); + }, +}); diff --git a/xhiveframework/core/doctype/user_permission/user_permission.json b/xhiveframework/core/doctype/user_permission/user_permission.json new file mode 100644 index 0000000..60b6779 --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/user_permission.json @@ -0,0 +1,117 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2017-07-17 14:25:27.881871", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "allow", + "for_value", + "column_break_3", + "is_default", + "advanced_control_section", + "apply_to_all_doctypes", + "applicable_for", + "column_break_9", + "hide_descendants" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User", + "options": "User", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "allow", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Allow", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "for_value", + "fieldtype": "Dynamic Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "For Value", + "options": "allow", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "label": "Is Default" + }, + { + "fieldname": "advanced_control_section", + "fieldtype": "Section Break", + "label": "Advanced Control" + }, + { + "default": "1", + "fieldname": "apply_to_all_doctypes", + "fieldtype": "Check", + "label": "Apply To All Document Types" + }, + { + "depends_on": "eval:!doc.apply_to_all_doctypes", + "fieldname": "applicable_for", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Applicable For", + "options": "DocType" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Hide descendant records of For Value.", + "fieldname": "hide_descendants", + "fieldtype": "Check", + "hidden": 1, + "label": "Hide Descendants" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + } + ], + "links": [], + "modified": "2022-01-03 11:25:03.726150", + "modified_by": "Administrator", + "module": "Core", + "name": "User Permission", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "user", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_permission/user_permission.py b/xhiveframework/core/doctype/user_permission/user_permission.py new file mode 100644 index 0000000..bdfde7b --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/user_permission.py @@ -0,0 +1,339 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.utils import find +from xhiveframework.desk.form.linked_with import get_linked_doctypes +from xhiveframework.model.document import Document +from xhiveframework.utils import cstr + + +class UserPermission(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow: DF.Link + applicable_for: DF.Link | None + apply_to_all_doctypes: DF.Check + for_value: DF.DynamicLink + hide_descendants: DF.Check + is_default: DF.Check + user: DF.Link + + # end: auto-generated types + def validate(self): + self.validate_user_permission() + self.validate_default_permission() + + def on_update(self): + xhiveframework.cache.hdel("user_permissions", self.user) + xhiveframework.publish_realtime("update_user_permissions", user=self.user, after_commit=True) + + def on_trash(self): + xhiveframework.cache.hdel("user_permissions", self.user) + xhiveframework.publish_realtime("update_user_permissions", user=self.user, after_commit=True) + + def validate_user_permission(self): + """checks for duplicate user permission records""" + + duplicate_exists = xhiveframework.get_all( + self.doctype, + filters={ + "allow": self.allow, + "for_value": self.for_value, + "user": self.user, + "applicable_for": cstr(self.applicable_for), + "apply_to_all_doctypes": self.apply_to_all_doctypes, + "name": ["!=", self.name], + }, + limit=1, + ) + if duplicate_exists: + xhiveframework.throw(_("User permission already exists"), xhiveframework.DuplicateEntryError) + + def validate_default_permission(self): + """validate user permission overlap for default value of a particular doctype""" + overlap_exists = [] + if self.is_default: + overlap_exists = xhiveframework.get_all( + self.doctype, + filters={"allow": self.allow, "user": self.user, "is_default": 1, "name": ["!=", self.name]}, + or_filters={ + "applicable_for": cstr(self.applicable_for), + "apply_to_all_doctypes": 1, + }, + limit=1, + ) + if overlap_exists: + ref_link = xhiveframework.get_desk_link(self.doctype, overlap_exists[0].name) + xhiveframework.throw(_("{0} has already assigned default value for {1}.").format(ref_link, self.allow)) + + +def send_user_permissions(bootinfo): + bootinfo.user["user_permissions"] = get_user_permissions() + + +@xhiveframework.whitelist() +def get_user_permissions(user=None): + """Get all users permissions for the user as a dict of doctype""" + # if this is called from client-side, + # user can access only his/her user permissions + if xhiveframework.request and xhiveframework.local.form_dict.cmd == "get_user_permissions": + user = xhiveframework.session.user + + if not user: + user = xhiveframework.session.user + + if not user or user in ("Administrator", "Guest"): + return {} + + cached_user_permissions = xhiveframework.cache.hget("user_permissions", user) + + if cached_user_permissions is not None: + return cached_user_permissions + + out = {} + + def add_doc_to_perm(perm, doc_name, is_default): + # group rules for each type + # for example if allow is "Customer", then build all allowed customers + # in a list + if not out.get(perm.allow): + out[perm.allow] = [] + + out[perm.allow].append( + xhiveframework._dict( + {"doc": doc_name, "applicable_for": perm.get("applicable_for"), "is_default": is_default} + ) + ) + + try: + for perm in xhiveframework.get_all( + "User Permission", + fields=["allow", "for_value", "applicable_for", "is_default", "hide_descendants"], + filters=dict(user=user), + ): + meta = xhiveframework.get_meta(perm.allow) + add_doc_to_perm(perm, perm.for_value, perm.is_default) + + if meta.is_nested_set() and not perm.hide_descendants: + decendants = xhiveframework.db.get_descendants(perm.allow, perm.for_value) + for doc in decendants: + add_doc_to_perm(perm, doc, False) + + out = xhiveframework._dict(out) + xhiveframework.cache.hset("user_permissions", user, out) + except xhiveframework.db.SQLError as e: + if xhiveframework.db.is_table_missing(e): + # called from patch + pass + + return out + + +def user_permission_exists(user, allow, for_value, applicable_for=None): + """Checks if similar user permission already exists""" + user_permissions = get_user_permissions(user).get(allow, []) + if not user_permissions: + return None + return find( + user_permissions, + lambda perm: perm["doc"] == for_value and perm.get("applicable_for") == applicable_for, + ) + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_applicable_for_doctype_list(doctype, txt, searchfield, start, page_len, filters): + linked_doctypes_map = get_linked_doctypes(doctype, True) + + linked_doctypes = [] + for linked_doctype, linked_doctype_values in linked_doctypes_map.items(): + linked_doctypes.append(linked_doctype) + child_doctype = linked_doctype_values.get("child_doctype") + if child_doctype: + linked_doctypes.append(child_doctype) + + linked_doctypes += [doctype] + + if txt: + linked_doctypes = [d for d in linked_doctypes if txt.lower() in d.lower()] + + linked_doctypes.sort() + + return [[doctype] for doctype in linked_doctypes[start:page_len]] + + +def get_permitted_documents(doctype): + """Returns permitted documents from the given doctype for the session user""" + # sort permissions in a way to make the first permission in the list to be default + user_perm_list = sorted( + get_user_permissions().get(doctype, []), key=lambda x: x.get("is_default"), reverse=True + ) + + return [d.get("doc") for d in user_perm_list if d.get("doc")] + + +@xhiveframework.whitelist() +def check_applicable_doc_perm(user, doctype, docname): + xhiveframework.only_for("System Manager") + applicable = [] + doc_exists = xhiveframework.get_all( + "User Permission", + fields=["name"], + filters={ + "user": user, + "allow": doctype, + "for_value": docname, + "apply_to_all_doctypes": 1, + }, + limit=1, + ) + if doc_exists: + applicable = get_linked_doctypes(doctype).keys() + else: + data = xhiveframework.get_all( + "User Permission", + fields=["applicable_for"], + filters={ + "user": user, + "allow": doctype, + "for_value": docname, + }, + ) + for permission in data: + applicable.append(permission.applicable_for) + return applicable + + +@xhiveframework.whitelist() +def clear_user_permissions(user, for_doctype): + xhiveframework.only_for("System Manager") + total = xhiveframework.db.count("User Permission", {"user": user, "allow": for_doctype}) + + if total: + xhiveframework.db.delete( + "User Permission", + { + "allow": for_doctype, + "user": user, + }, + ) + xhiveframework.clear_cache() + + return total + + +@xhiveframework.whitelist() +def add_user_permissions(data): + """Add and update the user permissions""" + xhiveframework.only_for("System Manager") + if isinstance(data, str): + data = json.loads(data) + data = xhiveframework._dict(data) + + # get all doctypes on whom this permission is applied + perm_applied_docs = check_applicable_doc_perm(data.user, data.doctype, data.docname) + exists = xhiveframework.db.exists( + "User Permission", + { + "user": data.user, + "allow": data.doctype, + "for_value": data.docname, + "apply_to_all_doctypes": 1, + }, + ) + if data.apply_to_all_doctypes == 1 and not exists: + remove_applicable(perm_applied_docs, data.user, data.doctype, data.docname) + insert_user_perm( + data.user, data.doctype, data.docname, data.is_default, data.hide_descendants, apply_to_all=1 + ) + return 1 + elif len(data.applicable_doctypes) > 0 and data.apply_to_all_doctypes != 1: + remove_apply_to_all(data.user, data.doctype, data.docname) + update_applicable(perm_applied_docs, data.applicable_doctypes, data.user, data.doctype, data.docname) + for applicable in data.applicable_doctypes: + if applicable not in perm_applied_docs: + insert_user_perm( + data.user, + data.doctype, + data.docname, + data.is_default, + data.hide_descendants, + applicable=applicable, + ) + elif exists: + insert_user_perm( + data.user, + data.doctype, + data.docname, + data.is_default, + data.hide_descendants, + applicable=applicable, + ) + return 1 + return 0 + + +def insert_user_perm( + user, doctype, docname, is_default=0, hide_descendants=0, apply_to_all=None, applicable=None +): + user_perm = xhiveframework.new_doc("User Permission") + user_perm.user = user + user_perm.allow = doctype + user_perm.for_value = docname + user_perm.is_default = is_default + user_perm.hide_descendants = hide_descendants + if applicable: + user_perm.applicable_for = applicable + user_perm.apply_to_all_doctypes = 0 + else: + user_perm.apply_to_all_doctypes = 1 + user_perm.insert() + + +def remove_applicable(perm_applied_docs, user, doctype, docname): + for applicable_for in perm_applied_docs: + xhiveframework.db.delete( + "User Permission", + { + "applicable_for": applicable_for, + "for_value": docname, + "allow": doctype, + "user": user, + }, + ) + + +def remove_apply_to_all(user, doctype, docname): + xhiveframework.db.delete( + "User Permission", + { + "apply_to_all_doctypes": 1, + "for_value": docname, + "allow": doctype, + "user": user, + }, + ) + + +def update_applicable(already_applied, to_apply, user, doctype, docname): + for applied in already_applied: + if applied not in to_apply: + xhiveframework.db.delete( + "User Permission", + { + "applicable_for": applied, + "for_value": docname, + "allow": doctype, + "user": user, + }, + ) diff --git a/xhiveframework/core/doctype/user_permission/user_permission_help.html b/xhiveframework/core/doctype/user_permission/user_permission_help.html new file mode 100644 index 0000000..d4b48fe --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/user_permission_help.html @@ -0,0 +1,8 @@ +
    +
    {%= __("Records for following doctypes will be filtered") %}
    +
      + {% Object.keys(linked_doctypes).forEach(key => { %} +
    • = 10 ? "col-md-4" : "" }}>{%= __(key) %}
    • + {% }) %} +
    +
    \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_permission/user_permission_list.js b/xhiveframework/core/doctype/user_permission/user_permission_list.js new file mode 100644 index 0000000..f057c10 --- /dev/null +++ b/xhiveframework/core/doctype/user_permission/user_permission_list.js @@ -0,0 +1,293 @@ +xhiveframework.listview_settings["User Permission"] = { + onload: function (list_view) { + var me = this; + list_view.page.add_inner_button(__("Add / Update"), function () { + let dialog = new xhiveframework.ui.Dialog({ + title: __("Add User Permissions"), + fields: [ + { + fieldname: "user", + label: __("For User"), + fieldtype: "Link", + options: "User", + reqd: 1, + onchange: function () { + dialog.fields_dict.doctype.set_input(undefined); + dialog.fields_dict.docname.set_input(undefined); + dialog.set_df_property("docname", "hidden", 1); + dialog.set_df_property("is_default", "hidden", 1); + dialog.set_df_property("apply_to_all_doctypes", "hidden", 1); + dialog.set_df_property("applicable_doctypes", "hidden", 1); + dialog.set_df_property("hide_descendants", "hidden", 1); + }, + }, + { + fieldname: "doctype", + label: __("Document Type"), + fieldtype: "Link", + options: "DocType", + reqd: 1, + onchange: function () { + me.on_doctype_change(dialog); + }, + }, + { + fieldname: "docname", + label: __("Document Name"), + fieldtype: "Dynamic Link", + options: "doctype", + hidden: 1, + onchange: function () { + let field = dialog.fields_dict["docname"]; + if (field.value != field.last_value) { + if ( + dialog.fields_dict.doctype.value && + dialog.fields_dict.docname.value && + dialog.fields_dict.user.value + ) { + me.get_applicable_doctype(dialog).then((applicable) => { + me.get_multi_select_options(dialog, applicable).then( + (options) => { + me.applicable_options = options; + me.on_docname_change(dialog, options, applicable); + if (options.length > 5) { + dialog.fields_dict.applicable_doctypes.setup_select_all(); + } + } + ); + }); + } + } + }, + }, + { + fieldtype: "Section Break", + hide_border: 1, + }, + { + fieldname: "is_default", + label: __("Is Default"), + fieldtype: "Check", + hidden: 1, + }, + { + fieldname: "apply_to_all_doctypes", + label: __("Apply to all Documents Types"), + fieldtype: "Check", + hidden: 1, + onchange: function () { + if ( + dialog.fields_dict.doctype.value && + dialog.fields_dict.docname.value && + dialog.fields_dict.user.value + ) { + me.on_apply_to_all_doctypes_change(dialog, me.applicable_options); + if (me.applicable_options.length > 5) { + dialog.fields_dict.applicable_doctypes.setup_select_all(); + } + } + }, + }, + { + fieldtype: "Column Break", + }, + { + fieldname: "hide_descendants", + label: __("Hide Descendants"), + fieldtype: "Check", + hidden: 1, + }, + { + fieldtype: "Section Break", + hide_border: 1, + }, + { + label: __("Applicable Document Types"), + fieldname: "applicable_doctypes", + fieldtype: "MultiCheck", + options: [], + columns: 2, + hidden: 1, + }, + ], + primary_action: (data) => { + data = me.validate(dialog, data); + xhiveframework.call({ + async: false, + method: "xhiveframework.core.doctype.user_permission.user_permission.add_user_permissions", + args: { + data: data, + }, + callback: function (r) { + if (r.message === 1) { + xhiveframework.show_alert({ + message: __("User Permissions created sucessfully"), + indicator: "blue", + }); + } else { + xhiveframework.show_alert({ + message: __("Nothing to update"), + indicator: "red", + }); + } + }, + }); + dialog.hide(); + list_view.refresh(); + }, + primary_action_label: __("Submit"), + }); + dialog.show(); + }); + list_view.page.add_inner_button(__("Bulk Delete"), function () { + const dialog = new xhiveframework.ui.Dialog({ + title: __("Clear User Permissions"), + fields: [ + { + fieldname: "user", + label: __("For User"), + fieldtype: "Link", + options: "User", + reqd: 1, + }, + { + fieldname: "for_doctype", + label: __("For Document Type"), + fieldtype: "Link", + options: "DocType", + reqd: 1, + }, + ], + primary_action: (data) => { + // mandatory not filled + if (!data) return; + + xhiveframework.confirm(__("Are you sure?"), () => { + xhiveframework + .xcall( + "xhiveframework.core.doctype.user_permission.user_permission.clear_user_permissions", + data + ) + .then((data) => { + dialog.hide(); + let message = ""; + if (data === 0) { + message = __("No records deleted"); + } else if (data === 1) { + message = __("{0} record deleted", [data]); + } else { + message = __("{0} records deleted", [data]); + } + xhiveframework.show_alert({ + message, + indicator: "info", + }); + list_view.refresh(); + }); + }); + }, + primary_action_label: __("Delete"), + }); + + dialog.show(); + }); + }, + + validate: function (dialog, data) { + if (dialog.fields_dict.applicable_doctypes.get_unchecked_options().length == 0) { + data.apply_to_all_doctypes = 1; + data.applicable_doctypes = []; + return data; + } + if (data.apply_to_all_doctypes == 0 && !("applicable_doctypes" in data)) { + xhiveframework.throw(__("Please select applicable Doctypes")); + } + return data; + }, + + get_applicable_doctype: function (dialog) { + return new Promise((resolve) => { + xhiveframework + .call({ + method: "xhiveframework.core.doctype.user_permission.user_permission.check_applicable_doc_perm", + async: false, + args: { + user: dialog.fields_dict.user.value, + doctype: dialog.fields_dict.doctype.value, + docname: dialog.fields_dict.docname.value, + }, + }) + .then((r) => { + resolve(r.message); + }); + }); + }, + + get_multi_select_options: function (dialog, applicable) { + return new Promise((resolve) => { + xhiveframework + .call({ + method: "xhiveframework.desk.form.linked_with.get_linked_doctypes", + async: false, + args: { + user: dialog.fields_dict.user.value, + doctype: dialog.fields_dict.doctype.value, + docname: dialog.fields_dict.docname.value, + }, + }) + .then((r) => { + var options = []; + for (var d in r.message) { + var checked = $.inArray(d, applicable) != -1 ? 1 : 0; + options.push({ label: d, value: d, checked: checked }); + } + resolve(options); + }); + }); + }, + + on_doctype_change: function (dialog) { + dialog.set_df_property("docname", "hidden", 0); + dialog.set_df_property("docname", "reqd", 1); + dialog.set_df_property("is_default", "hidden", 0); + dialog.set_df_property("apply_to_all_doctypes", "hidden", 0); + dialog.set_value("apply_to_all_doctypes", "checked", 1); + let show = xhiveframework.boot.nested_set_doctypes.includes(dialog.get_value("doctype")); + dialog.set_df_property("hide_descendants", "hidden", !show); + dialog.refresh(); + }, + + on_docname_change: function (dialog, options, applicable) { + if (applicable.length != 0) { + dialog.set_primary_action("Update"); + dialog.set_title("Update User Permissions"); + dialog.set_df_property("applicable_doctypes", "options", options); + if ( + dialog.fields_dict.applicable_doctypes.get_checked_options().length == + options.length + ) { + dialog.set_df_property("applicable_doctypes", "hidden", 1); + } else { + dialog.set_df_property("applicable_doctypes", "hidden", 0); + dialog.set_df_property("apply_to_all_doctypes", "checked", 0); + } + } else { + dialog.set_primary_action("Submit"); + dialog.set_title("Add User Permissions"); + dialog.set_df_property("applicable_doctypes", "options", options); + dialog.set_df_property("applicable_doctypes", "hidden", 1); + } + dialog.refresh(); + }, + + on_apply_to_all_doctypes_change: function (dialog, options) { + if (dialog.fields_dict.apply_to_all_doctypes.get_value() == 0) { + dialog.set_df_property("applicable_doctypes", "hidden", 0); + dialog.set_df_property("applicable_doctypes", "options", options); + } else { + dialog.set_df_property("applicable_doctypes", "options", options); + dialog.set_df_property("applicable_doctypes", "hidden", 1); + } + dialog.refresh_sections(); + }, +}; diff --git a/xhiveframework/core/doctype/user_select_document_type/__init__.py b/xhiveframework/core/doctype/user_select_document_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.json b/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.json new file mode 100644 index 0000000..86e1942 --- /dev/null +++ b/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2021-01-17 18:28:14.208576", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-01-17 18:45:44.993190", + "modified_by": "Administrator", + "module": "Core", + "name": "User Select Document Type", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.py b/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.py new file mode 100644 index 0000000..4648d77 --- /dev/null +++ b/xhiveframework/core/doctype/user_select_document_type/user_select_document_type.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class UserSelectDocumentType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document_type: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/user_social_login/__init__.py b/xhiveframework/core/doctype/user_social_login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_social_login/user_social_login.json b/xhiveframework/core/doctype/user_social_login/user_social_login.json new file mode 100644 index 0000000..6b4b182 --- /dev/null +++ b/xhiveframework/core/doctype/user_social_login/user_social_login.json @@ -0,0 +1,58 @@ +{ + "actions": [], + "creation": "2017-12-02 13:01:20.507112", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "provider", + "section_break_0", + "username", + "column_break_0", + "userid" + ], + "fields": [ + { + "fieldname": "provider", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Provider", + "read_only": 1 + }, + { + "fieldname": "section_break_0", + "fieldtype": "Section Break" + }, + { + "fieldname": "username", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Username", + "read_only": 1 + }, + { + "fieldname": "column_break_0", + "fieldtype": "Column Break" + }, + { + "fieldname": "userid", + "fieldtype": "Data", + "in_list_view": 1, + "label": "User ID", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:53.800689", + "modified_by": "Administrator", + "module": "Core", + "name": "User Social Login", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_social_login/user_social_login.py b/xhiveframework/core/doctype/user_social_login/user_social_login.py new file mode 100644 index 0000000..abcfa20 --- /dev/null +++ b/xhiveframework/core/doctype/user_social_login/user_social_login.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class UserSocialLogin(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + provider: DF.Data | None + userid: DF.Data | None + username: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/user_type/__init__.py b/xhiveframework/core/doctype/user_type/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_type/test_user_type.py b/xhiveframework/core/doctype/user_type/test_user_type.py new file mode 100644 index 0000000..4ef9744 --- /dev/null +++ b/xhiveframework/core/doctype/user_type/test_user_type.py @@ -0,0 +1,64 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.installer import update_site_config +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestUserType(XhiveFrameworkTestCase): + def setUp(self): + create_role() + + def test_add_select_perm_doctypes(self): + user_type = create_user_type("Test User Type") + + # select perms added for all link fields + doc = xhiveframework.get_meta("Contact") + link_fields = doc.get_link_fields() + select_doctypes = xhiveframework.get_all( + "User Select Document Type", {"parent": user_type.name}, pluck="document_type" + ) + + for entry in link_fields: + self.assertTrue(entry.options in select_doctypes) + + # select perms added for all child table link fields + link_fields = [] + for child_table in doc.get_table_fields(): + child_doc = xhiveframework.get_meta(child_table.options) + link_fields.extend(child_doc.get_link_fields()) + + for entry in link_fields: + self.assertTrue(entry.options in select_doctypes) + + def tearDown(self): + xhiveframework.db.rollback() + + +def create_user_type(user_type): + if xhiveframework.db.exists("User Type", user_type): + xhiveframework.delete_doc("User Type", user_type) + + user_type_limit = {xhiveframework.scrub(user_type): 1} + update_site_config("user_type_doctype_limit", user_type_limit) + + doc = xhiveframework.get_doc( + { + "doctype": "User Type", + "name": user_type, + "role": "_Test User Type", + "user_id_field": "user", + "apply_user_permission_on": "User", + } + ) + + doc.append("user_doctypes", {"document_type": "Contact", "read": 1, "write": 1}) + + return doc.insert() + + +def create_role(): + if not xhiveframework.db.exists("Role", "_Test User Type"): + xhiveframework.get_doc( + {"doctype": "Role", "role_name": "_Test User Type", "desk_access": 1, "is_custom": 1} + ).insert() diff --git a/xhiveframework/core/doctype/user_type/user_type.js b/xhiveframework/core/doctype/user_type/user_type.js new file mode 100644 index 0000000..a4f6f4e --- /dev/null +++ b/xhiveframework/core/doctype/user_type/user_type.js @@ -0,0 +1,71 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("User Type", { + refresh: function (frm) { + if (frm.is_new() && !xhiveframework.boot.developer_mode) frm.set_value("is_standard", 1); + + frm.set_query("document_type", "user_doctypes", function () { + return { + filters: { + istable: 0, + }, + }; + }); + + frm.set_query("document_type", "select_doctypes", function () { + return { + filters: { + istable: 0, + }, + }; + }); + + frm.set_query("document_type", "custom_select_doctypes", function () { + return { + filters: { + istable: 0, + }, + }; + }); + + frm.set_query("role", function () { + return { + filters: { + is_custom: 1, + disabled: 0, + desk_access: 1, + }, + }; + }); + + frm.set_query("apply_user_permission_on", function () { + return { + query: "xhiveframework.core.doctype.user_type.user_type.get_user_linked_doctypes", + }; + }); + }, + + onload: function (frm) { + frm.trigger("get_user_id_fields"); + }, + + apply_user_permission_on: function (frm) { + frm.set_value("user_id_field", ""); + frm.trigger("get_user_id_fields"); + }, + + get_user_id_fields: function (frm) { + if (frm.doc.apply_user_permission_on) { + xhiveframework.call({ + method: "xhiveframework.core.doctype.user_type.user_type.get_user_id", + args: { + parent: frm.doc.apply_user_permission_on, + }, + callback: function (r) { + set_field_options("user_id_field", [""].concat(r.message)); + }, + }); + } + }, +}); diff --git a/xhiveframework/core/doctype/user_type/user_type.json b/xhiveframework/core/doctype/user_type/user_type.json new file mode 100644 index 0000000..b0bdb21 --- /dev/null +++ b/xhiveframework/core/doctype/user_type/user_type.json @@ -0,0 +1,145 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2021-01-13 01:48:02.378548", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_standard", + "section_break_2", + "role", + "column_break_4", + "apply_user_permission_on", + "user_id_field", + "section_break_6", + "user_doctypes", + "custom_select_doctypes", + "select_doctypes", + "allowed_modules_section", + "user_type_modules" + ], + "fields": [ + { + "default": "0", + "depends_on": "eval: xhiveframework.boot.developer_mode", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only_depends_on": "eval: !xhiveframework.boot.developer_mode" + }, + { + "depends_on": "eval: !doc.is_standard", + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Document Types and Permissions" + }, + { + "depends_on": "eval: !doc.is_standard", + "fieldname": "user_doctypes", + "fieldtype": "Table", + "label": "Document Types", + "mandatory_depends_on": "eval: !doc.is_standard", + "options": "User Document Type" + }, + { + "depends_on": "eval: !doc.is_standard", + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "mandatory_depends_on": "eval: !doc.is_standard", + "options": "Role" + }, + { + "fieldname": "select_doctypes", + "fieldtype": "Table", + "hidden": 1, + "label": "Document Types (Select Permissions Only)", + "options": "User Select Document Type", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: !doc.is_standard", + "description": "Can only list down the document types which has been linked to the User document type.", + "fieldname": "apply_user_permission_on", + "fieldtype": "Link", + "label": "Apply User Permission On", + "mandatory_depends_on": "eval: !doc.is_standard", + "options": "DocType" + }, + { + "depends_on": "eval: !doc.is_standard", + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "depends_on": "apply_user_permission_on", + "fieldname": "user_id_field", + "fieldtype": "Select", + "label": "User Id Field", + "mandatory_depends_on": "eval: !doc.is_standard" + }, + { + "depends_on": "eval: !doc.is_standard", + "fieldname": "allowed_modules_section", + "fieldtype": "Section Break", + "label": "Allowed Modules" + }, + { + "fieldname": "user_type_modules", + "fieldtype": "Table", + "label": "User Type Module", + "no_copy": 1, + "options": "User Type Module", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "custom_select_doctypes", + "fieldtype": "Table", + "label": "Custom Document Types (Select Permission)", + "options": "User Select Document Type" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-06-09 14:00:36.820306", + "modified_by": "Administrator", + "module": "Core", + "name": "User Type", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/core/doctype/user_type/user_type.py b/xhiveframework/core/doctype/user_type/user_type.py new file mode 100644 index 0000000..c3f4054 --- /dev/null +++ b/xhiveframework/core/doctype/user_type/user_type.py @@ -0,0 +1,346 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.config import get_modules_from_app +from xhiveframework.model.document import Document +from xhiveframework.permissions import add_permission, add_user_permission +from xhiveframework.utils import get_link_to_form + + +class UserType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.user_document_type.user_document_type import UserDocumentType + from xhiveframework.core.doctype.user_select_document_type.user_select_document_type import ( + UserSelectDocumentType, + ) + from xhiveframework.core.doctype.user_type_module.user_type_module import UserTypeModule + from xhiveframework.types import DF + + apply_user_permission_on: DF.Link | None + custom_select_doctypes: DF.Table[UserSelectDocumentType] + is_standard: DF.Check + role: DF.Link | None + select_doctypes: DF.Table[UserSelectDocumentType] + user_doctypes: DF.Table[UserDocumentType] + user_id_field: DF.Literal[None] + user_type_modules: DF.Table[UserTypeModule] + + # end: auto-generated types + def validate(self): + self.set_modules() + self.add_select_perm_doctypes() + + def clear_cache(self): + super().clear_cache() + + if not self.is_standard: + xhiveframework.cache.delete_value("non_standard_user_types") + + def on_update(self): + if self.is_standard: + return + + self.validate_document_type_limit() + self.validate_role() + self.add_role_permissions_for_user_doctypes() + self.add_role_permissions_for_select_doctypes() + self.add_role_permissions_for_file() + self.update_users() + self.remove_permission_for_deleted_doctypes() + + def on_trash(self): + if self.is_standard: + xhiveframework.throw(_("Standard user type {0} can not be deleted.").format(xhiveframework.bold(self.name))) + + def set_modules(self): + if not self.user_doctypes: + return + + modules = xhiveframework.get_all( + "DocType", + filters={"name": ("in", [d.document_type for d in self.user_doctypes])}, + distinct=True, + pluck="module", + ) + + self.set("user_type_modules", []) + for module in modules: + self.append("user_type_modules", {"module": module}) + + def validate_document_type_limit(self): + limit = xhiveframework.conf.get("user_type_doctype_limit", {}).get(xhiveframework.scrub(self.name)) + + if not limit and xhiveframework.session.user != "Administrator": + xhiveframework.throw( + _("User does not have permission to create the new {0}").format(xhiveframework.bold(_("User Type"))), + title=_("Permission Error"), + ) + + if not limit: + xhiveframework.throw( + _("The limit has not set for the user type {0} in the site config file.").format( + xhiveframework.bold(self.name) + ), + title=_("Set Limit"), + ) + + if self.user_doctypes and len(self.user_doctypes) > limit: + xhiveframework.throw( + _("The total number of user document types limit has been crossed."), + title=_("User Document Types Limit Exceeded"), + ) + + custom_doctypes = [row.document_type for row in self.user_doctypes if row.is_custom] + if custom_doctypes and len(custom_doctypes) > 3: + xhiveframework.throw( + _("You can only set the 3 custom doctypes in the Document Types table."), + title=_("Custom Document Types Limit Exceeded"), + ) + + def validate_role(self): + if not self.role: + xhiveframework.throw(_("The field {0} is mandatory").format(xhiveframework.bold(_("Role")))) + + if not xhiveframework.db.get_value("Role", self.role, "is_custom"): + xhiveframework.throw( + _("The role {0} should be a custom role.").format( + xhiveframework.bold(get_link_to_form("Role", self.role)) + ) + ) + + def update_users(self): + for row in xhiveframework.get_all("User", filters={"user_type": self.name}): + user = xhiveframework.get_cached_doc("User", row.name) + self.update_roles_in_user(user) + self.update_modules_in_user(user) + user.update_children() + + def update_roles_in_user(self, user): + user.set("roles", []) + user.append("roles", {"role": self.role}) + + def update_modules_in_user(self, user): + block_modules = xhiveframework.get_all( + "Module Def", + fields=["name as module"], + filters={"name": ["not in", [d.module for d in self.user_type_modules]]}, + ) + + if block_modules: + user.set("block_modules", block_modules) + + def add_role_permissions_for_user_doctypes(self): + perms = ["read", "write", "create", "submit", "cancel", "amend", "delete"] + for row in self.user_doctypes: + docperm = add_role_permissions(row.document_type, self.role) + + values = {perm: row.get(perm) or 0 for perm in perms} + for perm in ["print", "email", "share"]: + values[perm] = 1 + + xhiveframework.db.set_value("Custom DocPerm", docperm, values) + + def add_select_perm_doctypes(self): + if xhiveframework.flags.ignore_select_perm: + return + + self.select_doctypes = [] + + select_doctypes = [] + user_doctypes = [row.document_type for row in self.user_doctypes] + + for doctype in user_doctypes: + doc = xhiveframework.get_meta(doctype) + self.prepare_select_perm_doctypes(doc, user_doctypes, select_doctypes) + + for child_table in doc.get_table_fields(): + child_doc = xhiveframework.get_meta(child_table.options) + if child_doc: + self.prepare_select_perm_doctypes(child_doc, user_doctypes, select_doctypes) + + if select_doctypes: + select_doctypes = set(select_doctypes) + for select_doctype in select_doctypes: + self.append("select_doctypes", {"document_type": select_doctype}) + + def prepare_select_perm_doctypes(self, doc, user_doctypes, select_doctypes): + for field in doc.get_link_fields(): + if field.options not in user_doctypes: + select_doctypes.append(field.options) + + def add_role_permissions_for_select_doctypes(self): + for doctype in ["select_doctypes", "custom_select_doctypes"]: + for row in self.get(doctype): + docperm = add_role_permissions(row.document_type, self.role) + xhiveframework.db.set_value( + "Custom DocPerm", docperm, {"select": 1, "read": 0, "create": 0, "write": 0} + ) + + def add_role_permissions_for_file(self): + docperm = add_role_permissions("File", self.role) + xhiveframework.db.set_value("Custom DocPerm", docperm, {"read": 1, "create": 1, "write": 1}) + + def remove_permission_for_deleted_doctypes(self): + doctypes = [d.document_type for d in self.user_doctypes] + + # Do not remove the doc permission for the file doctype + doctypes.append("File") + + for doctype in ["select_doctypes", "custom_select_doctypes"]: + doctypes.extend(dt.document_type for dt in self.get(doctype)) + for perm in xhiveframework.get_all( + "Custom DocPerm", filters={"role": self.role, "parent": ["not in", doctypes]} + ): + xhiveframework.delete_doc("Custom DocPerm", perm.name) + + +def add_role_permissions(doctype, role): + name = xhiveframework.get_value("Custom DocPerm", dict(parent=doctype, role=role, permlevel=0)) + + if not name: + name = add_permission(doctype, role, 0) + + return name + + +def get_non_standard_user_types(): + user_types = xhiveframework.get_all( + "User Type", + fields=["apply_user_permission_on", "name", "user_id_field"], + filters={"is_standard": 0}, + ) + + return {d.name: [d.apply_user_permission_on, d.user_id_field] for d in user_types} + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_user_linked_doctypes(doctype, txt, searchfield, start, page_len, filters): + modules = [d.get("module_name") for d in get_modules_from_app("xhiveframework")] + + filters = [ + ["DocField", "options", "=", "User"], + ["DocType", "is_submittable", "=", 0], + ["DocType", "issingle", "=", 0], + ["DocType", "module", "not in", modules], + ["DocType", "read_only", "=", 0], + ["DocType", "name", "like", f"%{txt}%"], + ] + + doctypes = xhiveframework.get_all( + "DocType", + fields=["`tabDocType`.`name`"], + filters=filters, + order_by="`tabDocType`.`idx` desc", + limit_start=start, + limit_page_length=page_len, + as_list=1, + ) + + custom_dt_filters = [ + ["Custom Field", "dt", "like", f"%{txt}%"], + ["Custom Field", "options", "=", "User"], + ["Custom Field", "fieldtype", "=", "Link"], + ] + + custom_doctypes = xhiveframework.get_all( + "Custom Field", fields=["dt as name"], filters=custom_dt_filters, as_list=1 + ) + + return doctypes + custom_doctypes + + +@xhiveframework.whitelist() +def get_user_id(parent): + data = ( + xhiveframework.get_all( + "DocField", + fields=["label", "fieldname as value"], + filters={"options": "User", "fieldtype": "Link", "parent": parent}, + ) + or [] + ) + + data.extend( + xhiveframework.get_all( + "Custom Field", + fields=["label", "fieldname as value"], + filters={"options": "User", "fieldtype": "Link", "dt": parent}, + ) + ) + + return data + + +def user_linked_with_permission_on_doctype(doc, user): + if not doc.apply_user_permission_on: + return True + + if not doc.user_id_field: + xhiveframework.throw(_("User Id Field is mandatory in the user type {0}").format(xhiveframework.bold(doc.name))) + + if xhiveframework.db.get_value(doc.apply_user_permission_on, {doc.user_id_field: user}, "name"): + return True + else: + label = xhiveframework.get_meta(doc.apply_user_permission_on).get_field(doc.user_id_field).label + + xhiveframework.msgprint( + _( + "To set the role {0} in the user {1}, kindly set the {2} field as {3} in one of the {4} record." + ).format( + xhiveframework.bold(doc.role), + xhiveframework.bold(user), + xhiveframework.bold(label), + xhiveframework.bold(user), + xhiveframework.bold(doc.apply_user_permission_on), + ) + ) + + return False + + +def apply_permissions_for_non_standard_user_type(doc, method=None): + """Create user permission for the non standard user type""" + if not xhiveframework.db.table_exists("User Type") or xhiveframework.flags.in_migrate: + return + + user_types = xhiveframework.cache.get_value( + "non_standard_user_types", + get_non_standard_user_types, + ) + + if not user_types: + return + + for user_type, data in user_types.items(): + if not doc.get(data[1]) or doc.doctype != data[0]: + continue + + if xhiveframework.get_cached_value("User", doc.get(data[1]), "user_type") != user_type: + return + + if doc.get(data[1]) and ( + not doc._doc_before_save + or doc.get(data[1]) != doc._doc_before_save.get(data[1]) + or not xhiveframework.db.get_value( + "User Permission", {"user": doc.get(data[1]), "allow": data[0], "for_value": doc.name}, "name" + ) + ): + perm_data = xhiveframework.db.get_value( + "User Permission", {"allow": doc.doctype, "for_value": doc.name}, ["name", "user"] + ) + + if not perm_data: + user_doc = xhiveframework.get_cached_doc("User", doc.get(data[1])) + user_doc.set_roles_and_modules_based_on_user_type() + user_doc.update_children() + add_user_permission(doc.doctype, doc.name, doc.get(data[1])) + else: + xhiveframework.db.set_value("User Permission", perm_data[0], "user", doc.get(data[1])) diff --git a/xhiveframework/core/doctype/user_type/user_type_dashboard.py b/xhiveframework/core/doctype/user_type/user_type_dashboard.py new file mode 100644 index 0000000..8cc610f --- /dev/null +++ b/xhiveframework/core/doctype/user_type/user_type_dashboard.py @@ -0,0 +1,5 @@ +from xhiveframework import _ + + +def get_data(): + return {"fieldname": "user_type", "transactions": [{"label": _("Reference"), "items": ["User"]}]} diff --git a/xhiveframework/core/doctype/user_type/user_type_list.js b/xhiveframework/core/doctype/user_type/user_type_list.js new file mode 100644 index 0000000..a6b22b1 --- /dev/null +++ b/xhiveframework/core/doctype/user_type/user_type_list.js @@ -0,0 +1,10 @@ +xhiveframework.listview_settings["User Type"] = { + add_fields: ["is_standard"], + get_indicator: function (doc) { + if (doc.is_standard) { + return [__("Standard"), "green", "is_standard,=,1"]; + } else { + return [__("Custom"), "blue", "is_standard,=,0"]; + } + }, +}; diff --git a/xhiveframework/core/doctype/user_type_module/__init__.py b/xhiveframework/core/doctype/user_type_module/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/user_type_module/user_type_module.json b/xhiveframework/core/doctype/user_type_module/user_type_module.json new file mode 100644 index 0000000..0f9cbef --- /dev/null +++ b/xhiveframework/core/doctype/user_type_module/user_type_module.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2021-01-24 03:05:24.634719", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "module" + ], + "fields": [ + { + "fieldname": "module", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Module", + "options": "Module Def", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-01-24 03:07:43.602927", + "modified_by": "Administrator", + "module": "Core", + "name": "User Type Module", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/user_type_module/user_type_module.py b/xhiveframework/core/doctype/user_type_module/user_type_module.py new file mode 100644 index 0000000..6e5f16f --- /dev/null +++ b/xhiveframework/core/doctype/user_type_module/user_type_module.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class UserTypeModule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + module: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/core/doctype/version/__init__.py b/xhiveframework/core/doctype/version/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/version/test_records.json b/xhiveframework/core/doctype/version/test_records.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/xhiveframework/core/doctype/version/test_records.json @@ -0,0 +1 @@ +[] diff --git a/xhiveframework/core/doctype/version/test_version.py b/xhiveframework/core/doctype/version/test_version.py new file mode 100644 index 0000000..d8b3edd --- /dev/null +++ b/xhiveframework/core/doctype/version/test_version.py @@ -0,0 +1,58 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import copy + +import xhiveframework +from xhiveframework.core.doctype.version.version import get_diff +from xhiveframework.test_runner import make_test_objects +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestVersion(XhiveFrameworkTestCase): + def test_get_diff(self): + xhiveframework.set_user("Administrator") + test_records = make_test_objects("Event", reset=True) + old_doc = xhiveframework.get_doc("Event", test_records[0]) + new_doc = copy.deepcopy(old_doc) + + old_doc.color = None + new_doc.color = "#fafafa" + + diff = get_diff(old_doc, new_doc)["changed"] + + self.assertEqual(get_fieldnames(diff)[0], "color") + self.assertTrue(get_old_values(diff)[0] is None) + self.assertEqual(get_new_values(diff)[0], "#fafafa") + + new_doc.starts_on = "2017-07-20" + + diff = get_diff(old_doc, new_doc)["changed"] + + self.assertEqual(get_fieldnames(diff)[1], "starts_on") + self.assertEqual(get_old_values(diff)[1], "01-01-2014 00:00:00") + self.assertEqual(get_new_values(diff)[1], "07-20-2017 00:00:00") + + def test_no_version_on_new_doc(self): + from xhiveframework.desk.form.load import get_versions + + t = xhiveframework.get_doc(doctype="ToDo", description="something") + t.save(ignore_version=False) + + self.assertFalse(get_versions(t)) + + t = xhiveframework.get_doc(t.doctype, t.name) + t.description = "changed" + t.save(ignore_version=False) + self.assertTrue(get_versions(t)) + + +def get_fieldnames(change_array): + return [d[0] for d in change_array] + + +def get_old_values(change_array): + return [d[1] for d in change_array] + + +def get_new_values(change_array): + return [d[2] for d in change_array] diff --git a/xhiveframework/core/doctype/version/version.js b/xhiveframework/core/doctype/version/version.js new file mode 100644 index 0000000..8eed509 --- /dev/null +++ b/xhiveframework/core/doctype/version/version.js @@ -0,0 +1,12 @@ +xhiveframework.ui.form.on("Version", "refresh", function (frm) { + $( + xhiveframework.render_template("version_view", { doc: frm.doc, data: JSON.parse(frm.doc.data) }) + ).appendTo(frm.fields_dict.table_html.$wrapper.empty()); + + frm.add_custom_button(__("Show all Versions"), function () { + xhiveframework.set_route("List", "Version", { + ref_doctype: frm.doc.ref_doctype, + docname: frm.doc.docname, + }); + }); +}); diff --git a/xhiveframework/core/doctype/version/version.json b/xhiveframework/core/doctype/version/version.json new file mode 100644 index 0000000..570b536 --- /dev/null +++ b/xhiveframework/core/doctype/version/version.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2014-02-20 17:22:37", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "column_break_3", + "docname", + "data", + "section_break_4", + "table_html" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "docname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Document Name", + "reqd": 1 + }, + { + "fieldname": "data", + "fieldtype": "Code", + "hidden": 1, + "label": "Data" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "table_html", + "fieldtype": "HTML", + "label": "Table HTML" + } + ], + "icon": "fa fa-copy", + "idx": 1, + "in_create": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Core", + "name": "Version", + "owner": "Administrator", + "permissions": [ + { + "export": 1, + "read": 1, + "report": 1, + "role": "System Manager" + }, + { + "delete": 1, + "read": 1, + "role": "Administrator" + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "docname", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/version/version.py b/xhiveframework/core/doctype/version/version.py new file mode 100644 index 0000000..cfa4a3c --- /dev/null +++ b/xhiveframework/core/doctype/version/version.py @@ -0,0 +1,169 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.model import no_value_fields, table_fields +from xhiveframework.model.document import Document + + +class Version(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data: DF.Code | None + docname: DF.Data + ref_doctype: DF.Link + + # end: auto-generated types + def update_version_info(self, old: Document | None, new: Document) -> bool: + """Update changed info and return true if change contains useful data.""" + if not old: + # Check if doc has some information about creation source like data import + return self.for_insert(new) + else: + return self.set_diff(old, new) + + @staticmethod + def set_impersonator(data): + if not xhiveframework.session: + return + if impersonator := xhiveframework.session.data.get("impersonated_by"): + data["impersonated_by"] = impersonator + + def set_diff(self, old: Document, new: Document) -> bool: + """Set the data property with the diff of the docs if present""" + diff = get_diff(old, new) + if diff: + self.set_impersonator(diff) + self.ref_doctype = new.doctype + self.docname = new.name + self.data = xhiveframework.as_json(diff, indent=None, separators=(",", ":")) + return True + else: + return False + + def for_insert(self, doc: Document) -> bool: + updater_reference = doc.flags.updater_reference + if not updater_reference: + return False + + data = { + "creation": doc.creation, + "updater_reference": updater_reference, + "created_by": doc.owner, + } + self.set_impersonator(data) + self.ref_doctype = doc.doctype + self.docname = doc.name + self.data = xhiveframework.as_json(data, indent=None, separators=(",", ":")) + return True + + def get_data(self): + return json.loads(self.data) + + +def get_diff(old, new, for_child=False, compare_cancelled=False): + """Get diff between 2 document objects + + If there is a change, then returns a dict like: + + { + "changed" : [[fieldname1, old, new], [fieldname2, old, new]], + "added" : [[table_fieldname1, {dict}], ], + "removed" : [[table_fieldname1, {dict}], ], + "row_changed": [[table_fieldname1, row_name1, row_index, + [[child_fieldname1, old, new], + [child_fieldname2, old, new]], ] + ], + + }""" + if not new: + return None + + blacklisted_fields = ["Markdown Editor", "Text Editor", "Code", "HTML Editor"] + + # capture data import if set + data_import = new.flags.via_data_import + updater_reference = new.flags.updater_reference + + out = xhiveframework._dict( + changed=[], + added=[], + removed=[], + row_changed=[], + data_import=data_import, + updater_reference=updater_reference, + ) + + if not for_child: + amended_from = new.get("amended_from") + old_row_name_field = "_amended_from" if (amended_from and amended_from == old.name) else "name" + + for df in new.meta.fields: + if df.fieldtype in no_value_fields and df.fieldtype not in table_fields: + continue + + old_value, new_value = old.get(df.fieldname), new.get(df.fieldname) + + if not for_child and df.fieldtype in table_fields: + old_rows_by_name = {} + for d in old_value: + old_rows_by_name[d.name] = d + + found_rows = set() + + # check rows for additions, changes + for i, d in enumerate(new_value): + old_row_name = getattr(d, old_row_name_field, None) + if compare_cancelled: + if amended_from: + if len(old_value) > i: + old_row_name = old_value[i].name + + if old_row_name and old_row_name in old_rows_by_name: + found_rows.add(old_row_name) + + diff = get_diff(old_rows_by_name[old_row_name], d, for_child=True) + if diff and diff.changed: + out.row_changed.append((df.fieldname, i, d.name, diff.changed)) + else: + out.added.append([df.fieldname, d.as_dict()]) + + # check for deletions + for d in old_value: + if d.name not in found_rows: + out.removed.append([df.fieldname, d.as_dict()]) + + elif old_value != new_value: + if df.fieldtype not in blacklisted_fields: + old_value = old.get_formatted(df.fieldname) if old_value else old_value + new_value = new.get_formatted(df.fieldname) if new_value else new_value + + if old_value != new_value: + out.changed.append((df.fieldname, old_value, new_value)) + + # name & docstatus + if not for_child: + for key in ("name", "docstatus"): + old_value = getattr(old, key) + new_value = getattr(new, key) + + if old_value != new_value: + out.changed.append([key, old_value, new_value]) + + if any((out.changed, out.added, out.removed, out.row_changed)): + return out + + else: + return None + + +def on_doctype_update(): + xhiveframework.db.add_index("Version", ["ref_doctype", "docname"]) diff --git a/xhiveframework/core/doctype/version/version_view.html b/xhiveframework/core/doctype/version/version_view.html new file mode 100644 index 0000000..81d3e5c --- /dev/null +++ b/xhiveframework/core/doctype/version/version_view.html @@ -0,0 +1,95 @@ +
    +{% if data.comment %} +

    {{ __("Comment") + " (" + data.comment_type }})

    +

    {{ data.comment }}

    +{% endif %} + +{% if data.changed && data.changed.length %} +

    {{ __("Values Changed") }}

    + + + + + + + + + + {% for item in data.changed %} + + + + + + {% endfor %} + +
    {{ __("Property") }}{{ __("Original Value") }}{{ __("New Value") }}
    {{ xhiveframework.meta.get_label(doc.ref_doctype, item[0]) }}{{ xhiveframework.utils.escape_html(item[1]) }}{{ xhiveframework.utils.escape_html(item[2]) }}
    +{% endif %} + +{% var _keys = ["added", "removed"]; %} +{% for key in _keys %} + {% if data[key] && data[key].length %} + {% var title = key==="added" ? __("Rows Added") : __("Rows Removed"); %} +

    {{ title }}

    + + + + + + + + + {% var values = data[key]; %} + {% for item in values %} + + + + + {% endfor %} + +
    {{ __("Property") }}{{ title }}
    {{ xhiveframework.meta.get_label(doc.ref_doctype, item[0]) }} + {% var item_keys = Object.keys(item[1]).sort(); %} + + + {% for row_key in item_keys %} + + + + + {% endfor %} + +
    {{ row_key }}{{ xhiveframework.utils.escape_html(item[1][row_key]) }}
    +
    + + {% endif %} +{% endfor %} + +{% if data.row_changed && data.row_changed.length %} +

    {{ __("Row Values Changed") }}

    + + + + + + + + + + + + {% var values = data.row_changed; %} + {% for table_info in values %} + {% var _changed = table_info[3]; %} + {% for item in _changed %} + + + + + + + + {% endfor %} + {% endfor %} + +{% endif %} + diff --git a/xhiveframework/core/doctype/view_log/__init__.py b/xhiveframework/core/doctype/view_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/doctype/view_log/test_view_log.py b/xhiveframework/core/doctype/view_log/test_view_log.py new file mode 100644 index 0000000..513f13b --- /dev/null +++ b/xhiveframework/core/doctype/view_log/test_view_log.py @@ -0,0 +1,34 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestViewLog(XhiveFrameworkTestCase): + def tearDown(self): + xhiveframework.set_user("Administrator") + + def test_if_user_is_added(self): + ev = xhiveframework.get_doc( + { + "doctype": "Event", + "subject": "test event for view logs", + "starts_on": "2018-06-04 14:11:00", + "event_type": "Public", + } + ).insert() + + xhiveframework.set_user("test@example.com") + + from xhiveframework.desk.form.load import getdoc + + # load the form + getdoc("Event", ev.name) + a = xhiveframework.get_value( + doctype="View Log", + filters={"reference_doctype": "Event", "reference_name": ev.name}, + fieldname=["viewed_by"], + ) + + self.assertEqual("test@example.com", a) + self.assertNotEqual("test1@example.com", a) diff --git a/xhiveframework/core/doctype/view_log/view_log.js b/xhiveframework/core/doctype/view_log/view_log.js new file mode 100644 index 0000000..f023205 --- /dev/null +++ b/xhiveframework/core/doctype/view_log/view_log.js @@ -0,0 +1,6 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("View Log", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/core/doctype/view_log/view_log.json b/xhiveframework/core/doctype/view_log/view_log.json new file mode 100644 index 0000000..a350ae8 --- /dev/null +++ b/xhiveframework/core/doctype/view_log/view_log.json @@ -0,0 +1,56 @@ +{ + "actions": [], + "creation": "2018-05-27 02:20:11.193944", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "viewed_by", + "reference_doctype", + "reference_name" + ], + "fields": [ + { + "fieldname": "viewed_by", + "fieldtype": "Data", + "label": "Viewed By", + "set_only_once": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "set_only_once": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference name", + "options": "reference_doctype", + "search_index": 1, + "set_only_once": 1 + } + ], + "links": [], + "modified": "2022-09-07 05:16:14.587628", + "modified_by": "Administrator", + "module": "Core", + "name": "View Log", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/core/doctype/view_log/view_log.py b/xhiveframework/core/doctype/view_log/view_log.py new file mode 100644 index 0000000..d1b9311 --- /dev/null +++ b/xhiveframework/core/doctype/view_log/view_log.py @@ -0,0 +1,28 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class ViewLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + viewed_by: DF.Data | None + + # end: auto-generated types + @staticmethod + def clear_old_logs(days=180): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("View Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) diff --git a/xhiveframework/core/form_tour/doctype/doctype.json b/xhiveframework/core/form_tour/doctype/doctype.json new file mode 100644 index 0000000..391d3ec --- /dev/null +++ b/xhiveframework/core/form_tour/doctype/doctype.json @@ -0,0 +1,56 @@ +{ + "creation": "2021-11-23 12:38:52.807353", + "docstatus": 0, + "doctype": "Form Tour", + "first_document": 0, + "idx": 0, + "include_name_field": 1, + "is_standard": 1, + "modified": "2021-11-25 17:03:01.646360", + "modified_by": "Administrator", + "module": "Core", + "name": "Doctype", + "owner": "Administrator", + "reference_doctype": "DocType", + "save_on_complete": 1, + "steps": [ + { + "description": "Select a Module to which this DocType would belong", + "field": "", + "fieldname": "module", + "fieldtype": "Link", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Module", + "parent_field": "", + "position": "Right", + "title": "Module" + }, + { + "description": "Check this to make the DocType as Custom", + "field": "", + "fieldname": "custom", + "fieldtype": "Check", + "has_next_condition": 1, + "is_table_field": 0, + "label": "Custom?", + "next_step_condition": "eval: doc.custom", + "parent_field": "", + "position": "Left", + "title": "Custom " + }, + { + "description": "A Field (or a docfield) defines a property of a DocType. You can define the column name, label, datatype and more for DocFields. For instance, a ToDo doctype has fields description, status and priority. These ultimately become columns in the database table tabToDo.", + "field": "", + "fieldname": "fields", + "fieldtype": "Table", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Fields", + "parent_field": "", + "position": "Top", + "title": "Fields" + } + ], + "title": "Doctype" +} \ No newline at end of file diff --git a/xhiveframework/core/notifications.py b/xhiveframework/core/notifications.py new file mode 100644 index 0000000..b7bee35 --- /dev/null +++ b/xhiveframework/core/notifications.py @@ -0,0 +1,44 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def get_notification_config(): + return { + "for_doctype": { + "Error Log": {"seen": 0}, + "Communication": {"status": "Open", "communication_type": "Communication"}, + "ToDo": "xhiveframework.core.notifications.get_things_todo", + "Event": "xhiveframework.core.notifications.get_todays_events", + "Workflow Action": {"status": "Open"}, + }, + } + + +def get_things_todo(as_list=False): + """Returns a count of incomplete todos""" + data = xhiveframework.get_list( + "ToDo", + fields=["name", "description"] if as_list else "count(*)", + filters=[["ToDo", "status", "=", "Open"]], + or_filters=[ + ["ToDo", "allocated_to", "=", xhiveframework.session.user], + ["ToDo", "assigned_by", "=", xhiveframework.session.user], + ], + as_list=True, + ) + + if as_list: + return data + return data[0][0] + + +def get_todays_events(as_list: bool = False): + """Returns a count of todays events in calendar""" + from xhiveframework.desk.doctype.event.event import get_events + from xhiveframework.utils import nowdate + + today = nowdate() + events = get_events(today, today) + return events if as_list else len(events) diff --git a/xhiveframework/core/page/__init__.py b/xhiveframework/core/page/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/page/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/page/dashboard_view/__init__.py b/xhiveframework/core/page/dashboard_view/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/page/dashboard_view/dashboard_view.js b/xhiveframework/core/page/dashboard_view/dashboard_view.js new file mode 100644 index 0000000..00ce70a --- /dev/null +++ b/xhiveframework/core/page/dashboard_view/dashboard_view.js @@ -0,0 +1,196 @@ +// Copyright (c) 2019, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.provide("xhiveframework.dashboards"); +xhiveframework.provide("xhiveframework.dashboards.chart_sources"); + +xhiveframework.pages["dashboard-view"].on_page_load = function (wrapper) { + xhiveframework.ui.make_app_page({ + parent: wrapper, + title: __("Dashboard"), + single_column: true, + }); + + xhiveframework.dashboard = new Dashboard(wrapper); + $(wrapper).bind("show", function () { + xhiveframework.dashboard.show(); + }); +}; + +class Dashboard { + constructor(wrapper) { + this.wrapper = $(wrapper); + $(`
    +
    +
    `).appendTo(this.wrapper.find(".page-content").empty()); + this.container = this.wrapper.find(".dashboard-graph"); + this.page = wrapper.page; + } + + show() { + this.route = xhiveframework.get_route(); + this.set_breadcrumbs(); + if (this.route.length > 1) { + // from route + this.show_dashboard(this.route.slice(-1)[0]); + } else { + // last opened + if (xhiveframework.last_dashboard) { + xhiveframework.set_re_route("dashboard-view", xhiveframework.last_dashboard); + } else { + // default dashboard + xhiveframework.db.get_list("Dashboard", { filters: { is_default: 1 } }).then((data) => { + if (data && data.length) { + xhiveframework.set_re_route("dashboard-view", data[0].name); + } else { + // no default, get the latest one + xhiveframework.db.get_list("Dashboard", { limit: 1 }).then((data) => { + if (data && data.length) { + xhiveframework.set_re_route("dashboard-view", data[0].name); + } else { + // create a new dashboard! + xhiveframework.new_doc("Dashboard"); + } + }); + } + }); + } + } + } + + show_dashboard(current_dashboard_name) { + if (this.dashboard_name !== current_dashboard_name) { + this.dashboard_name = current_dashboard_name; + let title = this.dashboard_name; + if (!this.dashboard_name.toLowerCase().includes(__("dashboard"))) { + // ensure dashboard title has "dashboard" + title = __("{0} Dashboard", [__(title)]); + } + this.page.set_title(__(title)); + this.set_dropdown(); + this.container.empty(); + this.refresh(); + } + this.charts = {}; + xhiveframework.last_dashboard = current_dashboard_name; + } + + set_breadcrumbs() { + xhiveframework.breadcrumbs.add("Desk", "Dashboard"); + } + + refresh() { + xhiveframework.run_serially([() => this.render_cards(), () => this.render_charts()]); + } + + render_charts() { + return this.get_permitted_items( + "xhiveframework.desk.doctype.dashboard.dashboard.get_permitted_charts" + ).then((charts) => { + if (!charts.length) { + xhiveframework.msgprint( + __("No Permitted Charts on this Dashboard"), + __("No Permitted Charts") + ); + } + + xhiveframework.dashboard_utils.get_dashboard_settings().then((settings) => { + let chart_config = settings.chart_config ? JSON.parse(settings.chart_config) : {}; + this.charts = charts.map((chart) => { + return { + chart_name: chart.chart, + label: chart.chart, + chart_settings: chart_config[chart.chart] || {}, + ...chart, + }; + }); + + this.chart_group = new xhiveframework.widget.WidgetGroup({ + title: null, + container: this.container, + type: "chart", + columns: 2, + options: { + allow_sorting: false, + allow_create: false, + allow_delete: false, + allow_hiding: false, + allow_edit: false, + }, + widgets: this.charts, + }); + }); + }); + } + + render_cards() { + return this.get_permitted_items( + "xhiveframework.desk.doctype.dashboard.dashboard.get_permitted_cards" + ).then((cards) => { + if (!cards.length) { + return; + } + + this.number_cards = cards.map((card) => { + return { + name: card.card, + }; + }); + + this.number_card_group = new xhiveframework.widget.WidgetGroup({ + container: this.container, + type: "number_card", + columns: 3, + options: { + allow_sorting: false, + allow_create: false, + allow_delete: false, + allow_hiding: false, + allow_edit: false, + }, + widgets: this.number_cards, + }); + }); + } + + get_permitted_items(method) { + return xhiveframework + .xcall(method, { + dashboard_name: this.dashboard_name, + }) + .then((items) => { + return items; + }); + } + + set_dropdown() { + this.page.clear_menu(); + + this.page.add_menu_item(__("Edit"), () => { + xhiveframework.set_route("Form", "Dashboard", xhiveframework.dashboard.dashboard_name); + }); + + this.page.add_menu_item(__("New"), () => { + xhiveframework.new_doc("Dashboard"); + }); + + this.page.add_menu_item(__("Refresh All"), () => { + this.chart_group && this.chart_group.widgets_list.forEach((chart) => chart.refresh()); + this.number_card_group && + this.number_card_group.widgets_list.forEach((card) => card.render_card()); + }); + + xhiveframework.db.get_list("Dashboard").then((dashboards) => { + dashboards.map((dashboard) => { + let name = dashboard.name; + if (name != this.dashboard_name) { + this.page.add_menu_item( + name, + () => xhiveframework.set_route("dashboard-view", name), + 1 + ); + } + }); + }); + } +} diff --git a/xhiveframework/core/page/dashboard_view/dashboard_view.json b/xhiveframework/core/page/dashboard_view/dashboard_view.json new file mode 100644 index 0000000..4ece98a --- /dev/null +++ b/xhiveframework/core/page/dashboard_view/dashboard_view.json @@ -0,0 +1,19 @@ +{ + "content": null, + "creation": "2019-01-08 19:19:48.073410", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2020-12-16 12:29:08.610352", + "modified_by": "Administrator", + "module": "Core", + "name": "dashboard-view", + "owner": "Administrator", + "page_name": "dashboard-view", + "roles": [], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Dashboard" +} \ No newline at end of file diff --git a/xhiveframework/core/page/permission_manager/README.md b/xhiveframework/core/page/permission_manager/README.md new file mode 100644 index 0000000..c62ccb3 --- /dev/null +++ b/xhiveframework/core/page/permission_manager/README.md @@ -0,0 +1 @@ +Interface for easy browsing and setting of user permissions (DocPerm) on DocTypes. \ No newline at end of file diff --git a/xhiveframework/core/page/permission_manager/__init__.py b/xhiveframework/core/page/permission_manager/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/page/permission_manager/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/page/permission_manager/permission_manager.css b/xhiveframework/core/page/permission_manager/permission_manager.css new file mode 100644 index 0000000..fec486a --- /dev/null +++ b/xhiveframework/core/page/permission_manager/permission_manager.css @@ -0,0 +1,51 @@ +.table { + margin-bottom: 0px; + margin-top: 0px; + border-radius: var(--border-radius-md); +} + +thead { + border: none; + background-color: var(--control-bg); + border-radius: var(--border-radius-md); +} + +thead > tr { + border-radius: var(--border-radius-md); +} + +thead > tr > th:first-child { + border-radius: var(--border-radius-md) 0 0 var(--border-radius-md); +} +thead > tr > th:last-child { + border-radius: 0 var(--border-radius-md) var(--border-radius-md) 0; +} + +/* Space between thead and tbody */ +/* tbody:before { + content: "@"; + display: block; + line-height: var(--margin-md); + text-indent: -99999px; +} */ + +td[data-fieldname="permissions"] > .row > .col-md-4 { + margin-bottom: var(--margin-sm); +} + +tbody > tr { + border-top: 1px solid var(--border-color); +} + +tbody > tr:first-child { + border-top: none; +} + +button.btn-remove-perm { + box-shadow: none; + padding: var(--padding-xs) var(--padding-xs); +} + +button.btn-remove-perm > svg > use { + stroke: var(--white); +} diff --git a/xhiveframework/core/page/permission_manager/permission_manager.js b/xhiveframework/core/page/permission_manager/permission_manager.js new file mode 100644 index 0000000..35bec88 --- /dev/null +++ b/xhiveframework/core/page/permission_manager/permission_manager.js @@ -0,0 +1,522 @@ +xhiveframework.pages["permission-manager"].on_page_load = (wrapper) => { + let page = xhiveframework.ui.make_app_page({ + parent: wrapper, + title: __("Role Permissions Manager"), + card_layout: true, + single_column: true, + }); + + xhiveframework.breadcrumbs.add("Setup"); + + $("
    ").appendTo( + page.main + ); + $(xhiveframework.render_template("permission_manager_help", {})).appendTo(page.main); + wrapper.permission_engine = new xhiveframework.PermissionEngine(wrapper); +}; + +xhiveframework.pages["permission-manager"].refresh = function (wrapper) { + wrapper.permission_engine.set_from_route(); +}; + +xhiveframework.PermissionEngine = class PermissionEngine { + constructor(wrapper) { + this.wrapper = wrapper; + this.page = wrapper.page; + this.body = $(this.wrapper).find(".perm-engine"); + this.make(); + this.refresh(); + this.add_check_events(); + } + + make() { + this.make_reset_button(); + xhiveframework + .call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "get_roles_and_doctypes", + }) + .then((res) => { + this.options = res.message; + this.setup_page(); + }); + } + + setup_page() { + this.doctype_select = this.wrapper.page.add_field({ + fieldname: "doctype_select", + label: __("Document Type"), + fieldtype: "Link", + options: "DocType", + change: function () { + xhiveframework.set_route("permission-manager", this.get_value()); + }, + }); + + this.role_select = this.wrapper.page.add_field({ + fieldname: "role_select", + label: __("Roles"), + fieldtype: "Link", + options: "Role", + change: () => this.refresh(), + }); + + this.page.add_inner_button(__("Set User Permissions"), () => { + return xhiveframework.set_route("List", "User Permission"); + }); + this.set_from_route(); + } + + set_from_route() { + if (!this.doctype_select) { + // selects not yet loaded, call again after a bit + setTimeout(() => { + this.set_from_route(); + }, 500); + return; + } + if (xhiveframework.get_route()[1]) { + this.doctype_select.set_value(xhiveframework.get_route()[1]); + } else if (xhiveframework.route_options) { + if (xhiveframework.route_options.doctype) { + this.doctype_select.set_value(xhiveframework.route_options.doctype); + } + if (xhiveframework.route_options.role) { + this.role_select.set_value(xhiveframework.route_options.role); + } + xhiveframework.route_options = null; + } + this.refresh(); + } + + get_standard_permissions(callback) { + let doctype = this.get_doctype(); + if (doctype) { + return xhiveframework.call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "get_standard_permissions", + args: { doctype: doctype }, + callback: callback, + }); + } + return false; + } + + reset_std_permissions(data) { + let doctype = this.get_doctype(); + let d = xhiveframework.confirm(__("Reset Permissions for {0}?", [doctype]), () => { + return xhiveframework + .call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "reset", + args: { doctype }, + }) + .then(() => { + this.refresh(); + }); + }); + + // show standard permissions + let $d = $(d.wrapper) + .find(".xhiveframework-confirm-message") + .append("
    Standard Permissions:

    "); + let $wrapper = $("

    ").appendTo($d); + data.message.forEach((d) => { + let rights = this.rights + .filter((r) => d[r]) + .map((r) => { + return __(toTitle(xhiveframework.unscrub(r))); + }); + + d.rights = rights.join(", "); + + $wrapper.append(`
    \ +
    ${d.role}, Level ${d.permlevel || 0}
    \ +
    ${d.rights}
    \ +

    `); + }); + } + + get_doctype() { + return this.doctype_select.get_value(); + } + + get_role() { + return this.role_select.get_value(); + } + + set_empty_message(message) { + this.body.html(` +
    +

    + ${message} +

    +
    `); + } + + refresh() { + this.page.clear_secondary_action(); + this.page.clear_primary_action(); + + if (!this.doctype_select) { + return this.set_empty_message(__("Loading")); + } + + let doctype = this.get_doctype(); + let role = this.get_role(); + + if (!doctype && !role) { + return this.set_empty_message(__("Select Document Type or Role to start.")); + } + + // get permissions + xhiveframework + .call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "get_permissions", + args: { doctype, role }, + }) + .then((r) => { + this.render(r.message); + }); + } + + render(perm_list) { + this.body.empty(); + this.perm_list = perm_list || []; + if (!this.perm_list.length) { + this.set_empty_message(__("No Permissions set for this criteria.")); + } else { + this.show_permission_table(this.perm_list); + } + this.show_add_rule(); + this.get_doctype() && this.make_reset_button(); + } + + show_permission_table(perm_list) { + this.table = $( + "
    \ +
    {{ __("Table Field") }}{{ __("Row #") }}{{ __("Property") }}{{ __("Original Value") }}{{ __("New Value") }}
    {{ xhiveframework.meta.get_label(doc.ref_doctype, table_info[0]) }}{{ table_info[1] }}{{ item[0] }}{{ xhiveframework.utils.escape_html(item[1]) }}{{ xhiveframework.utils.escape_html(item[2]) }}
    \ + \ + \ +
    \ +
    " + ).appendTo(this.body); + + const table_columns = [ + [__("Document Type"), 150], + [__("Role"), 170], + [__("Level"), 40], + [__("Permissions"), 350], + ["", 40], + ]; + + table_columns.forEach((col) => { + $("") + .html(col[0]) + .css("width", col[1] + "px") + .appendTo(this.table.find("thead tr")); + }); + + perm_list.forEach((d) => { + if (d.parent === "DocType") { + return; + } + + if (!d.permlevel) d.permlevel = 0; + + let row = $("").appendTo(this.table.find("tbody")); + this.add_cell(row, d, "parent"); + let role_cell = this.add_cell(row, d, "role"); + + this.set_show_users(role_cell, d.role); + + if (d.permlevel === 0) { + // this.setup_user_permissions(d, role_cell); + this.setup_if_owner(d, role_cell); + } + + let cell = this.add_cell(row, d, "permlevel"); + + if (d.permlevel == 0) { + cell.css("font-weight", "bold"); + } + + let perm_cell = this.add_cell(row, d, "permissions"); + let perm_container = $("
    ").appendTo(perm_cell); + + this.rights.forEach((r) => { + if (!d.is_submittable && ["submit", "cancel", "amend"].includes(r)) return; + if (d.in_create && ["create", "delete"].includes(r)) return; + this.add_check(perm_container, d, r); + + if (d.if_owner && r == "report") { + perm_container.find("div[data-fieldname='report']").toggle(false); + } + }); + + // buttons + this.add_delete_button(row, d); + }); + } + + add_cell(row, d, fieldname) { + return $("") + .appendTo(row) + .attr("data-fieldname", fieldname) + .addClass("pt-4") + .html(__(d[fieldname])); + } + + add_check(cell, d, fieldname, label, description = "") { + if (!label) label = toTitle(fieldname.replace(/_/g, " ")); + if (d.permlevel > 0 && ["read", "write"].indexOf(fieldname) == -1) { + return; + } + + let checkbox = $( + `
    +
    + +

    ${__(description)}

    +
    +
    ` + ) + .appendTo(cell) + .attr("data-fieldname", fieldname); + + checkbox + .find("input") + .prop("checked", d[fieldname] ? true : false) + .attr("data-ptype", fieldname) + .attr("data-role", d.role) + .attr("data-permlevel", d.permlevel) + .attr("data-if_owner", d.if_owner) + .attr("data-doctype", d.parent); + + checkbox.find("label").css("text-transform", "capitalize"); + + return checkbox; + } + + setup_if_owner(d, role_cell) { + this.add_check(role_cell, d, "if_owner", "Only if Creator") + .removeClass("col-md-4") + .css({ "margin-top": "15px" }); + } + + get rights() { + return [ + "select", + "read", + "write", + "create", + "delete", + "submit", + "cancel", + "amend", + "print", + "email", + "report", + "import", + "export", + "share", + ]; + } + + set_show_users(cell, role) { + cell.html("" + __(role) + "") + .find("a") + .attr("data-role", role) + .click(function () { + let role = $(this).attr("data-role"); + xhiveframework.call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "get_users_with_role", + args: { + role: role, + }, + callback: function (r) { + r.message = $.map(r.message, function (p) { + return $.format('{1}', [p, p]); + }); + xhiveframework.msgprint( + __("Users with role {0}:", [__(role)]) + + "
    " + + r.message.join("
    ") + ); + }, + }); + return false; + }); + } + + add_delete_button(row, d) { + $( + `` + ) + .appendTo($(``).appendTo(row)) + .attr("data-doctype", d.parent) + .attr("data-role", d.role) + .attr("data-permlevel", d.permlevel) + .on("click", () => { + return xhiveframework.call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "remove", + args: { + doctype: d.parent, + role: d.role, + permlevel: d.permlevel, + if_owner: d.if_owner, + }, + callback: (r) => { + if (r.exc) { + xhiveframework.msgprint(__("Did not remove")); + } else { + this.refresh(); + } + }, + }); + }); + } + + add_check_events() { + let me = this; + this.body.on("click", ".show-user-permissions", () => { + xhiveframework.route_options = { allow: this.get_doctype() || "" }; + xhiveframework.set_route("List", "User Permission"); + }); + + this.body.on("click", "input[type='checkbox']", function () { + xhiveframework.dom.freeze(); + let chk = $(this); + let args = { + role: chk.attr("data-role"), + permlevel: chk.attr("data-permlevel"), + doctype: chk.attr("data-doctype"), + ptype: chk.attr("data-ptype"), + value: chk.prop("checked") ? 1 : 0, + if_owner: chk.attr("data-if_owner"), + }; + return xhiveframework.call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "update", + args: args, + callback: (r) => { + xhiveframework.dom.unfreeze(); + if (r.exc) { + // exception: reverse + chk.prop("checked", !chk.prop("checked")); + } else { + me.get_perm(args.role)[args.ptype] = args.value; + + if (args.ptype == "if_owner") { + let report_checkbox = chk + .closest("div.row") + .find("div[data-fieldname='report']"); + report_checkbox.toggle(!args.value); + } + } + }, + }); + }); + } + + show_add_rule() { + this.page.set_primary_action( + __("Add A New Rule"), + () => { + let d = new xhiveframework.ui.Dialog({ + title: __("Add New Permission Rule"), + fields: [ + { + fieldtype: "Select", + label: __("Document Type"), + options: this.options.doctypes, + reqd: 1, + fieldname: "parent", + }, + { + fieldtype: "Select", + label: __("Role"), + options: this.options.roles, + reqd: 1, + fieldname: "role", + }, + { + fieldtype: "Select", + label: __("Permission Level"), + options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + reqd: 1, + fieldname: "permlevel", + description: __( + "Level 0 is for document level permissions, higher levels for field level permissions." + ), + }, + ], + }); + if (this.get_doctype()) { + d.set_value("parent", this.get_doctype()); + d.get_input("parent").prop("disabled", true); + } + if (this.get_role()) { + d.set_value("role", this.get_role()); + d.get_input("role").prop("disabled", true); + } + d.set_value("permlevel", "0"); + d.set_primary_action(__("Add"), () => { + let args = d.get_values(); + if (!args) { + return; + } + xhiveframework.call({ + module: "xhiveframework.core", + page: "permission_manager", + method: "add", + args: args, + callback: (r) => { + if (r.exc) { + xhiveframework.msgprint(__("Did not add")); + } else { + this.refresh(); + } + }, + }); + d.hide(); + }); + d.show(); + }, + "small-add" + ); + } + + make_reset_button() { + this.page.set_secondary_action(__("Restore Original Permissions"), () => { + this.get_standard_permissions((data) => { + this.reset_std_permissions(data); + }); + }); + } + + get_perm(role) { + return $.map(this.perm_list, function (d) { + if (d.role == role) return d; + })[0]; + } + + get_link_fields(doctype) { + return xhiveframework.get_children("DocType", doctype, "fields", { + fieldtype: "Link", + options: ["not in", ["User", "[Select]"]], + }); + } +}; diff --git a/xhiveframework/core/page/permission_manager/permission_manager.json b/xhiveframework/core/page/permission_manager/permission_manager.json new file mode 100644 index 0000000..0af33ca --- /dev/null +++ b/xhiveframework/core/page/permission_manager/permission_manager.json @@ -0,0 +1,20 @@ +{ + "creation": "2013-01-01 11:00:01.000000", + "docstatus": 0, + "doctype": "Page", + "icon": "fa fa-lock", + "idx": 1, + "modified": "2013-07-11 14:43:43.000000", + "modified_by": "Administrator", + "module": "Core", + "name": "permission-manager", + "owner": "Administrator", + "page_name": "permission-manager", + "roles": [ + { + "role": "System Manager" + } + ], + "standard": "Yes", + "title": "Role Permissions Manager" +} \ No newline at end of file diff --git a/xhiveframework/core/page/permission_manager/permission_manager.py b/xhiveframework/core/page/permission_manager/permission_manager.py new file mode 100644 index 0000000..deccda0 --- /dev/null +++ b/xhiveframework/core/page/permission_manager/permission_manager.py @@ -0,0 +1,183 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +import xhiveframework +import xhiveframework.defaults +from xhiveframework import _ +from xhiveframework.core.doctype.doctype.doctype import ( + clear_permissions_cache, + validate_permissions_for_doctype, +) +from xhiveframework.exceptions import DoesNotExistError +from xhiveframework.modules.import_file import get_file_path, read_doc_from_file +from xhiveframework.permissions import ( + AUTOMATIC_ROLES, + add_permission, + get_all_perms, + get_linked_doctypes, + reset_perms, + setup_custom_perms, + update_permission_property, +) +from xhiveframework.utils.user import get_users_with_role as _get_user_with_role + +not_allowed_in_permission_manager = ["DocType", "Patch Log", "Module Def", "Transaction Log"] + + +@xhiveframework.whitelist() +def get_roles_and_doctypes(): + xhiveframework.only_for("System Manager") + + active_domains = xhiveframework.get_active_domains() + + doctypes = xhiveframework.get_all( + "DocType", + filters={ + "istable": 0, + "name": ("not in", ",".join(not_allowed_in_permission_manager)), + }, + or_filters={"ifnull(restrict_to_domain, '')": "", "restrict_to_domain": ("in", active_domains)}, + fields=["name"], + ) + + restricted_roles = ["Administrator"] + if xhiveframework.session.user != "Administrator": + custom_user_type_roles = xhiveframework.get_all("User Type", filters={"is_standard": 0}, fields=["role"]) + restricted_roles.extend(row.role for row in custom_user_type_roles) + restricted_roles.extend(AUTOMATIC_ROLES) + + roles = xhiveframework.get_all( + "Role", + filters={ + "name": ("not in", restricted_roles), + "disabled": 0, + }, + or_filters={"ifnull(restrict_to_domain, '')": "", "restrict_to_domain": ("in", active_domains)}, + fields=["name"], + ) + + doctypes_list = [{"label": _(d.get("name")), "value": d.get("name")} for d in doctypes] + roles_list = [{"label": _(d.get("name")), "value": d.get("name")} for d in roles] + + return { + "doctypes": sorted(doctypes_list, key=lambda d: d["label"].casefold()), + "roles": sorted(roles_list, key=lambda d: d["label"].casefold()), + } + + +@xhiveframework.whitelist() +def get_permissions(doctype: str | None = None, role: str | None = None): + xhiveframework.only_for("System Manager") + + if role: + out = get_all_perms(role) + if doctype: + out = [p for p in out if p.parent == doctype] + + else: + filters = {"parent": doctype} + if xhiveframework.session.user != "Administrator": + custom_roles = xhiveframework.get_all("Role", filters={"is_custom": 1}, pluck="name") + filters["role"] = ["not in", custom_roles] + + out = xhiveframework.get_all("Custom DocPerm", fields="*", filters=filters, order_by="permlevel") + if not out: + out = xhiveframework.get_all("DocPerm", fields="*", filters=filters, order_by="permlevel") + + linked_doctypes = {} + for d in out: + if d.parent not in linked_doctypes: + try: + linked_doctypes[d.parent] = get_linked_doctypes(d.parent) + except DoesNotExistError: + # exclude & continue if linked doctype is not found + xhiveframework.clear_last_message() + continue + d.linked_doctypes = linked_doctypes[d.parent] + if meta := xhiveframework.get_meta(d.parent): + d.is_submittable = meta.is_submittable + d.in_create = meta.in_create + + return out + + +@xhiveframework.whitelist() +def add(parent, role, permlevel): + xhiveframework.only_for("System Manager") + add_permission(parent, role, permlevel) + + +@xhiveframework.whitelist() +def update(doctype, role, permlevel, ptype, value=None, if_owner=0): + """Update role permission params + + Args: + doctype (str): Name of the DocType to update params for + role (str): Role to be updated for, eg "Website Manager". + permlevel (int): perm level the provided rule applies to + ptype (str): permission type, example "read", "delete", etc. + value (None, optional): value for ptype, None indicates False + + Returns: + str: Refresh flag is permission is updated successfully + """ + + def clear_cache(): + xhiveframework.clear_cache(doctype=doctype) + + xhiveframework.only_for("System Manager") + + if ptype == "report" and value == "1" and if_owner == "1": + xhiveframework.throw(_("Cannot set 'Report' permission if 'Only If Creator' permission is set")) + + out = update_permission_property(doctype, role, permlevel, ptype, value, if_owner=if_owner) + + if ptype == "if_owner" and value == "1": + update_permission_property(doctype, role, permlevel, "report", "0", if_owner=value) + + xhiveframework.db.after_commit.add(clear_cache) + + return "refresh" if out else None + + +@xhiveframework.whitelist() +def remove(doctype, role, permlevel, if_owner=0): + xhiveframework.only_for("System Manager") + setup_custom_perms(doctype) + + xhiveframework.db.delete( + "Custom DocPerm", + {"parent": doctype, "role": role, "permlevel": permlevel, "if_owner": if_owner}, + ) + + if not xhiveframework.get_all("Custom DocPerm", {"parent": doctype}): + xhiveframework.throw(_("There must be atleast one permission rule."), title=_("Cannot Remove")) + + validate_permissions_for_doctype(doctype, for_remove=True, alert=True) + + +@xhiveframework.whitelist() +def reset(doctype): + xhiveframework.only_for("System Manager") + reset_perms(doctype) + clear_permissions_cache(doctype) + + +@xhiveframework.whitelist() +def get_users_with_role(role): + xhiveframework.only_for("System Manager") + return _get_user_with_role(role) + + +@xhiveframework.whitelist() +def get_standard_permissions(doctype): + xhiveframework.only_for("System Manager") + meta = xhiveframework.get_meta(doctype) + if meta.custom: + doc = xhiveframework.get_doc("DocType", doctype) + return [p.as_dict() for p in doc.permissions] + else: + # also used to setup permissions via patch + path = get_file_path(meta.module, "DocType", doctype) + return read_doc_from_file(path).get("permissions") diff --git a/xhiveframework/core/page/permission_manager/permission_manager_help.html b/xhiveframework/core/page/permission_manager/permission_manager_help.html new file mode 100644 index 0000000..15ea59f --- /dev/null +++ b/xhiveframework/core/page/permission_manager/permission_manager_help.html @@ -0,0 +1,41 @@ +
    +
    +

    {%= __("Quick Help for Setting Permissions") %}:

    +
      +
    1. {%= __("Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.") %}
    2. +
    3. {%= __("Permissions get applied on Users based on what Roles they are assigned.") %}
    4. +
    5. {%= __("Roles can be set for users from their User page.") %} + {%= __("Setup > User") %}
    6. +
    7. {%= __("The system provides many pre-defined roles. You can add new roles to set finer permissions.") %} {%= __("Add a New Role") %}
    8. +
    9. {%= __("Permissions are automatically applied to Standard Reports and searches.") %}
    10. +
    11. {%= __("As a best practice, do not assign the same set of permission rule to different Roles. Instead, set multiple Roles to the same User.") %}
    12. +
    +
    +

    {%= __("Meaning of Submit, Cancel, Amend") %}:

    +
      +
    1. {%= __("Certain documents, like an Invoice, should not be changed once final. The final state for such documents is called Submitted. You can restrict which roles can Submit.") %}
    2. +
    3. {%= __("You can change Submitted documents by cancelling them and then, amending them.") %}
    4. +
    5. {%= __("When you Amend a document after Cancel and save it, it will get a new number that is a version of the old number.") %}
    6. +
    7. {%= __("For example if you cancel and amend INV004 it will become a new document INV004-1. This helps you to keep track of each amendment.") %}
    8. +
    +
    +

    {%= __("Permission Levels") %}:

    +
      +
    1. {%= __("Permissions at level 0 are Document Level permissions, i.e. they are primary for access to the document.") %}
    2. +
    3. {%= __("If a Role does not have access at Level 0, then higher levels are meaningless.") %}
    4. +
    5. {%= __("Permissions at higher levels are Field Level permissions. All Fields have a Permission Level set against them and the rules defined at that permissions apply to the field. This is useful in case you want to hide or make certain field read-only for certain Roles.") %}
    6. +
    7. {%= __("You can use Customize Form to set levels on fields.") %} {%= __("Setup > Customize Form") %}
    8. +
    +
    +

    {%= __("User Permissions") %}:

    +
      +
    1. {%= __("User Permissions are used to limit users to specific records.") %} + {%= __("Setup > User Permissions") %}
    2. +
    3. {%= __("Select Document Types to set which User Permissions are used to limit access.") %}
    4. +
    5. {%= __("Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).") %}
    6. +
    7. {%= __("Apart from System Manager, roles with Set User Permissions right can set permissions for other users for that Document Type.") %}
    8. +
    +

    {%= __("If these instructions where not helpful, please add in your suggestions on GitHub Issues.") %} + {%= __("Submit an Issue") %} +

    +
    diff --git a/xhiveframework/core/report/__init__.py b/xhiveframework/core/report/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/core/report/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/core/report/database_storage_usage_by_tables/__init__.py b/xhiveframework/core/report/database_storage_usage_by_tables/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js new file mode 100644 index 0000000..10ea12f --- /dev/null +++ b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js @@ -0,0 +1,6 @@ +// Copyright (c) 2022, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["Database Storage Usage By Tables"] = { + filters: [], +}; diff --git a/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.json b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.json new file mode 100644 index 0000000..773cb77 --- /dev/null +++ b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 1, + "columns": [], + "creation": "2022-10-19 02:25:24.326791", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2022-10-19 02:59:00.365307", + "modified_by": "Administrator", + "module": "Core", + "name": "Database Storage Usage By Tables", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Error Log", + "report_name": "Database Storage Usage By Tables", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + } + ] +} diff --git a/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py new file mode 100644 index 0000000..d00b21c --- /dev/null +++ b/xhiveframework/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py @@ -0,0 +1,40 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework + +COLUMNS = [ + {"label": "Table", "fieldname": "table", "fieldtype": "Data", "width": 200}, + {"label": "Size (MB)", "fieldname": "size", "fieldtype": "Float"}, + {"label": "Data (MB)", "fieldname": "data_size", "fieldtype": "Float"}, + {"label": "Index (MB)", "fieldname": "index_size", "fieldtype": "Float"}, +] + + +def execute(filters=None): + xhiveframework.only_for("System Manager") + + data = xhiveframework.db.multisql( + { + "mariadb": """ + SELECT table_name AS `table`, + round(((data_length + index_length) / 1024 / 1024), 2) `size`, + round((data_length / 1024 / 1024), 2) as data_size, + round((index_length / 1024 / 1024), 2) as index_size + FROM information_schema.TABLES + ORDER BY (data_length + index_length) DESC; + """, + "postgres": """ + SELECT + table_name as "table", + round(pg_total_relation_size(quote_ident(table_name)) / 1024 / 1024, 2) as "size", + round(pg_relation_size(quote_ident(table_name)) / 1024 / 1024, 2) as "data_size", + round(pg_indexes_size(quote_ident(table_name)) / 1024 / 1024, 2) as "index_size" + FROM information_schema.tables + WHERE table_schema = 'public' + ORDER BY 2 DESC; + """, + }, + as_dict=1, + ) + return COLUMNS, data diff --git a/xhiveframework/core/report/database_storage_usage_by_tables/test_database_storage_usage_by_tables.py b/xhiveframework/core/report/database_storage_usage_by_tables/test_database_storage_usage_by_tables.py new file mode 100644 index 0000000..88d6bbd --- /dev/null +++ b/xhiveframework/core/report/database_storage_usage_by_tables/test_database_storage_usage_by_tables.py @@ -0,0 +1,15 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + + +from xhiveframework.core.report.database_storage_usage_by_tables.database_storage_usage_by_tables import ( + execute, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDBUsageReport(XhiveFrameworkTestCase): + def test_basic_query(self): + _, data = execute() + tables = [d.table for d in data] + self.assertFalse({"tabUser", "tabDocField"}.difference(tables)) diff --git a/xhiveframework/core/report/document_share_report/__init__.py b/xhiveframework/core/report/document_share_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/report/document_share_report/document_share_report.json b/xhiveframework/core/report/document_share_report/document_share_report.json new file mode 100644 index 0000000..db2f2b6 --- /dev/null +++ b/xhiveframework/core/report/document_share_report/document_share_report.json @@ -0,0 +1,24 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2015-02-05 06:01:35.060098", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"DocShare.modified\", \"sort_order\": \"desc\", \"sort_by_next\": null, \"filters\": [], \"sort_order_next\": \"desc\", \"columns\": [[\"name\", \"DocShare\"], [\"user\", \"DocShare\"], [\"share_doctype\", \"DocShare\"], [\"share_name\", \"DocShare\"], [\"read\", \"DocShare\"], [\"write\", \"DocShare\"], [\"share\", \"DocShare\"]]}", + "modified": "2017-02-24 20:01:16.232286", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Share Report", + "owner": "Administrator", + "ref_doctype": "DocShare", + "report_name": "Document Share Report", + "report_type": "Report Builder", + "roles": [ + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/core/report/permitted_documents_for_user/__init__.py b/xhiveframework/core/report/permitted_documents_for_user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.js b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.js new file mode 100644 index 0000000..1cad077 --- /dev/null +++ b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.js @@ -0,0 +1,34 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.query_reports["Permitted Documents For User"] = { + filters: [ + { + fieldname: "user", + label: __("User"), + fieldtype: "Link", + options: "User", + reqd: 1, + }, + { + fieldname: "doctype", + label: __("DocType"), + fieldtype: "Link", + options: "DocType", + reqd: 1, + get_query: function () { + return { + query: "xhiveframework.core.report.permitted_documents_for_user.permitted_documents_for_user.query_doctypes", + filters: { + user: xhiveframework.query_report.get_filter_value("user"), + }, + }; + }, + }, + { + fieldname: "show_permissions", + label: __("Show Permissions"), + fieldtype: "Check", + }, + ], +}; diff --git a/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.json b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.json new file mode 100644 index 0000000..bf384ea --- /dev/null +++ b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.json @@ -0,0 +1,23 @@ +{ + "add_total_row": 0, + "creation": "2014-06-03 05:20:35.218263", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2018-06-29 15:46:42.805039", + "modified_by": "Administrator", + "module": "Core", + "name": "Permitted Documents For User", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "User", + "report_name": "Permitted Documents For User", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.py b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.py new file mode 100644 index 0000000..5365717 --- /dev/null +++ b/xhiveframework/core/report/permitted_documents_for_user/permitted_documents_for_user.py @@ -0,0 +1,60 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +import xhiveframework.utils.user +from xhiveframework.model import data_fieldtypes +from xhiveframework.permissions import rights + + +def execute(filters=None): + xhiveframework.only_for("System Manager") + + user, doctype, show_permissions = ( + filters.get("user"), + filters.get("doctype"), + filters.get("show_permissions"), + ) + + columns, fields = get_columns_and_fields(doctype) + data = xhiveframework.get_list(doctype, fields=fields, as_list=True, user=user) + + if show_permissions: + columns = columns + [xhiveframework.unscrub(right) + ":Check:80" for right in rights] + data = list(data) + for i, doc in enumerate(data): + permission = xhiveframework.permissions.get_doc_permissions(xhiveframework.get_doc(doctype, doc[0]), user) + data[i] = doc + tuple(permission.get(right) for right in rights) + + return columns, data + + +def get_columns_and_fields(doctype): + columns = [f"Name:Link/{doctype}:200"] + fields = ["name"] + for df in xhiveframework.get_meta(doctype).fields: + if df.in_list_view and df.fieldtype in data_fieldtypes: + fields.append(f"`{df.fieldname}`") + fieldtype = f"Link/{df.options}" if df.fieldtype == "Link" else df.fieldtype + columns.append(f"{df.label}:{fieldtype}:{df.width or 100}") + + return columns, fields + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def query_doctypes(doctype, txt, searchfield, start, page_len, filters): + user = filters.get("user") + user_perms = xhiveframework.utils.user.UserPermissions(user) + user_perms.build_permissions() + can_read = user_perms.can_read # Does not include child tables + include_single_doctypes = filters.get("include_single_doctypes") + + single_doctypes = [d[0] for d in xhiveframework.db.get_values("DocType", {"issingle": 1})] + + return [ + [dt] + for dt in can_read + if txt.lower().replace("%", "") in xhiveframework._(dt).lower() + and (include_single_doctypes or dt not in single_doctypes) + ] diff --git a/xhiveframework/core/report/transaction_log_report/__init__.py b/xhiveframework/core/report/transaction_log_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/report/transaction_log_report/transaction_log_report.js b/xhiveframework/core/report/transaction_log_report/transaction_log_report.js new file mode 100644 index 0000000..064b95d --- /dev/null +++ b/xhiveframework/core/report/transaction_log_report/transaction_log_report.js @@ -0,0 +1,10 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["Transaction Log Report"] = { + onload: function (query_report) { + query_report.add_make_chart_button = function () { + // + }; + }, +}; diff --git a/xhiveframework/core/report/transaction_log_report/transaction_log_report.json b/xhiveframework/core/report/transaction_log_report/transaction_log_report.json new file mode 100644 index 0000000..6d6fb78 --- /dev/null +++ b/xhiveframework/core/report/transaction_log_report/transaction_log_report.json @@ -0,0 +1,26 @@ +{ + "add_total_row": 0, + "creation": "2018-03-15 18:37:48.783779", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-12-27 18:10:29.785415", + "modified_by": "Administrator", + "module": "Core", + "name": "Transaction Log Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Transaction Log", + "report_name": "Transaction Log Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Administrator" + }, + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/core/report/transaction_log_report/transaction_log_report.py b/xhiveframework/core/report/transaction_log_report/transaction_log_report.py new file mode 100644 index 0000000..d55af29 --- /dev/null +++ b/xhiveframework/core/report/transaction_log_report/transaction_log_report.py @@ -0,0 +1,117 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import hashlib + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils import format_datetime + + +def execute(filters=None): + columns, data = get_columns(filters), get_data(filters) + + return columns, data + + +def get_data(filters=None): + result = [] + logs = xhiveframework.get_all("Transaction Log", fields=["*"], order_by="creation desc") + + for l in logs: + row_index = int(l.row_index) + if row_index > 1: + previous_hash = xhiveframework.get_all( + "Transaction Log", + fields=["chaining_hash"], + filters={"row_index": row_index - 1}, + ) + if not previous_hash: + integrity = False + else: + integrity = check_data_integrity( + l.chaining_hash, l.transaction_hash, l.previous_hash, previous_hash[0][0] + ) + + result.append( + [ + _(str(integrity)), + _(l.reference_doctype), + l.document_name, + l.owner, + l.modified_by, + format_datetime(l.timestamp, "YYYYMMDDHHmmss"), + ] + ) + else: + result.append( + [ + _("First Transaction"), + _(l.reference_doctype), + l.document_name, + l.owner, + l.modified_by, + format_datetime(l.timestamp, "YYYYMMDDHHmmss"), + ] + ) + + return result + + +def check_data_integrity(chaining_hash, transaction_hash, registered_previous_hash, previous_hash): + if registered_previous_hash != previous_hash: + return False + + calculated_chaining_hash = calculate_chain(transaction_hash, previous_hash) + + if calculated_chaining_hash != chaining_hash: + return False + else: + return True + + +def calculate_chain(transaction_hash, previous_hash): + sha = hashlib.sha256() + sha.update(str(transaction_hash) + str(previous_hash)) + return sha.hexdigest() + + +def get_columns(filters=None): + return [ + { + "label": _("Chain Integrity"), + "fieldname": "chain_integrity", + "fieldtype": "Data", + "width": 150, + }, + { + "label": _("Reference Doctype"), + "fieldname": "reference_doctype", + "fieldtype": "Data", + "width": 150, + }, + { + "label": _("Reference Name"), + "fieldname": "reference_name", + "fieldtype": "Data", + "width": 150, + }, + { + "label": _("Owner"), + "fieldname": "owner", + "fieldtype": "Data", + "width": 100, + }, + { + "label": _("Modified By"), + "fieldname": "modified_by", + "fieldtype": "Data", + "width": 100, + }, + { + "label": _("Timestamp"), + "fieldname": "timestamp", + "fieldtype": "Data", + "width": 100, + }, + ] diff --git a/xhiveframework/core/utils.py b/xhiveframework/core/utils.py new file mode 100644 index 0000000..1024bb7 --- /dev/null +++ b/xhiveframework/core/utils.py @@ -0,0 +1,91 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from markdownify import markdownify as md + +import xhiveframework + + +def get_parent_doc(doc): + """Return document of `reference_doctype`, `reference_doctype`.""" + if not getattr(doc, "parent_doc", None): + if doc.reference_doctype and doc.reference_name: + doc.parent_doc = xhiveframework.get_doc(doc.reference_doctype, doc.reference_name) + else: + doc.parent_doc = None + return doc.parent_doc + + +def set_timeline_doc(doc): + """Set timeline_doctype and timeline_name""" + parent_doc = get_parent_doc(doc) + if (doc.timeline_doctype and doc.timeline_name) or not parent_doc: + return + + timeline_field = parent_doc.meta.timeline_field + if not timeline_field: + return + + doctype = parent_doc.meta.get_link_doctype(timeline_field) + name = parent_doc.get(timeline_field) + + if doctype and name: + doc.timeline_doctype = doctype + doc.timeline_name = name + + else: + return + + +def find(list_of_dict, match_function): + """Returns a dict in a list of dicts on matching the conditions + provided in match function + + Usage: + list_of_dict = [{'name': 'Suraj'}, {'name': 'Aditya'}] + + required_dict = find(list_of_dict, lambda d: d['name'] == 'Aditya') + """ + + for entry in list_of_dict: + if match_function(entry): + return entry + return None + + +def find_all(list_of_dict, match_function): + """Returns all matching dicts in a list of dicts. + Uses matching function to filter out the dicts + + Usage: + colored_shapes = [ + {'color': 'red', 'shape': 'square'}, + {'color': 'red', 'shape': 'circle'}, + {'color': 'blue', 'shape': 'triangle'} + ] + + red_shapes = find_all(colored_shapes, lambda d: d['color'] == 'red') + """ + return [entry for entry in list_of_dict if match_function(entry)] + + +def ljust_list(_list, length, fill_word=None): + """ + Similar to ljust but for list. + + Usage: + $ ljust_list([1, 2, 3], 5) + > [1, 2, 3, None, None] + """ + # make a copy to avoid mutation of passed list + _list = list(_list) + fill_length = length - len(_list) + if fill_length > 0: + _list.extend([fill_word] * fill_length) + + return _list + + +def html2text(html, strip_links=False, wrap=True): + strip = ["a"] if strip_links else None + return md(html, heading_style="ATX", strip=strip, wrap=wrap) diff --git a/xhiveframework/core/web_form/__init__.py b/xhiveframework/core/web_form/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/web_form/edit_profile/__init__.py b/xhiveframework/core/web_form/edit_profile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/core/web_form/edit_profile/edit_profile.js b/xhiveframework/core/web_form/edit_profile/edit_profile.js new file mode 100644 index 0000000..64d0606 --- /dev/null +++ b/xhiveframework/core/web_form/edit_profile/edit_profile.js @@ -0,0 +1,3 @@ +xhiveframework.ready(function () { + // bind events here +}); diff --git a/xhiveframework/core/web_form/edit_profile/edit_profile.json b/xhiveframework/core/web_form/edit_profile/edit_profile.json new file mode 100644 index 0000000..b631908 --- /dev/null +++ b/xhiveframework/core/web_form/edit_profile/edit_profile.json @@ -0,0 +1,156 @@ +{ + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 0, + "allow_print": 0, + "apply_document_permissions": 0, + "breadcrumbs": "[{\"title\": _(\"My Account\"), \"route\": \"me\"}]", + "client_script": "xhiveframework.web_form.after_load = () => {\n if (window.location.pathname.endsWith(\"/new\") && xhiveframework.session.user) {\n let current_path = window.location.href;\n window.location.href = current_path.replace(\"/new\", \"/\" + xhiveframework.session.user);\n }\n}", + "creation": "2016-09-19 05:16:59.242754", + "doc_type": "User", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "introduction_text": "", + "is_standard": 1, + "list_columns": [], + "login_required": 1, + "max_attachment_size": 0, + "modified": "2023-01-18 10:26:26.766414", + "modified_by": "Administrator", + "module": "Core", + "name": "edit-profile", + "owner": "Administrator", + "published": 1, + "route": "update-profile", + "show_attachments": 0, + "show_list": 0, + "show_sidebar": 0, + "success_message": "Profile updated successfully.", + "success_url": "/me", + "title": "Update Profile", + "web_form_fields": [ + { + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name (Optional)", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "description": "", + "fieldname": "user_image", + "fieldtype": "Attach Image", + "hidden": 0, + "label": "Profile Picture", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldtype": "Section Break", + "hidden": 0, + "label": "More Information", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "phone", + "fieldtype": "Data", + "hidden": 0, + "label": "Phone", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "mobile_no", + "fieldtype": "Data", + "hidden": 0, + "label": "Mobile Number", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "", + "fieldtype": "Column Break", + "hidden": 0, + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "description": "", + "fieldname": "language", + "fieldtype": "Link", + "hidden": 0, + "label": "Language", + "max_length": 0, + "max_value": 0, + "options": "Language", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + } + ] +} diff --git a/xhiveframework/core/web_form/edit_profile/edit_profile.py b/xhiveframework/core/web_form/edit_profile/edit_profile.py new file mode 100644 index 0000000..02e3e93 --- /dev/null +++ b/xhiveframework/core/web_form/edit_profile/edit_profile.py @@ -0,0 +1,3 @@ +def get_context(context): + # do your magic here + pass diff --git a/xhiveframework/core/workspace/build/build.json b/xhiveframework/core/workspace/build/build.json new file mode 100644 index 0000000..8154259 --- /dev/null +++ b/xhiveframework/core/workspace/build/build.json @@ -0,0 +1,388 @@ +{ + "charts": [], + "content": "[{\"id\":\"5nnLaQeoFa\",\"type\":\"header\",\"data\":{\"text\":\"Get started
    \",\"col\":12}},{\"id\":\"HXRmktXYHy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"DocType\",\"col\":3}},{\"id\":\"pYALX3MwBW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customize Form\",\"col\":3}},{\"id\":\"XC78DuYB65\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Report\",\"col\":3}},{\"id\":\"XPm50Ppq3J\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Client Script\",\"col\":3}},{\"id\":\"yoU6nWiT83\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Server Script\",\"col\":3}},{\"id\":\"5UgFESBY0N\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Format Builder\",\"col\":3}},{\"id\":\"0gE0s-S70E\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"id\":\"62hseENHbd\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tOCrOgLW1G\",\"type\":\"header\",\"data\":{\"text\":\"Components to build your app\",\"col\":12}},{\"id\":\"cJ6CVsa8qW\",\"type\":\"card\",\"data\":{\"card_name\":\"Models\",\"col\":4}},{\"id\":\"MmEJpjEdGR\",\"type\":\"card\",\"data\":{\"card_name\":\"Views\",\"col\":4}},{\"id\":\"2ZdtgxQZqq\",\"type\":\"card\",\"data\":{\"card_name\":\"Customization\",\"col\":4}},{\"id\":\"NPFolijIcb\",\"type\":\"card\",\"data\":{\"card_name\":\"Scripting\",\"col\":4}},{\"id\":\"BIHjudL0T_\",\"type\":\"card\",\"data\":{\"card_name\":\"Modules\",\"col\":4}},{\"id\":\"iK3JQ9RXJE\",\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}},{\"id\":\"TiO9FCUUeC\",\"type\":\"card\",\"data\":{\"card_name\":\"System Logs\",\"col\":4}}]", + "creation": "2021-01-02 10:51:16.579957", + "custom_blocks": [], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "tool", + "idx": 1, + "is_hidden": 0, + "label": "Build", + "links": [ + { + "description": "Customize properties, naming, fields and more for standard doctypes", + "hidden": 0, + "is_query_report": 0, + "label": "Customization", + "link_count": 4, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Customize Form", + "link_count": 0, + "link_to": "Customize Form", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Custom Field", + "link_count": 0, + "link_to": "Custom Field", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Custom Translation", + "link_count": 0, + "link_to": "Translation", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Navbar Settings", + "link_count": 0, + "link_to": "Navbar Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "description": "Group your custom doctypes under modules", + "hidden": 0, + "is_query_report": 0, + "label": "Modules", + "link_count": 2, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Module Def", + "link_count": 0, + "link_to": "Module Def", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Module Onboarding", + "link_count": 0, + "link_to": "Module Onboarding", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "description": "Monitor logs for errors, background jobs, communications, and user activity", + "hidden": 0, + "is_query_report": 0, + "label": "System Logs", + "link_count": 5, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Background Jobs", + "link_count": 0, + "link_to": "RQ Job", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Scheduled Jobs Logs", + "link_count": 0, + "link_to": "Scheduled Job Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Error Logs", + "link_count": 0, + "link_to": "Error Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Communication Logs", + "link_count": 0, + "link_to": "Communication", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Activity Log", + "link_count": 0, + "link_to": "Activity Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "description": "Packages are lightweight apps (collection of Module Defs) that can be created, imported, or released right from the UI", + "hidden": 0, + "is_query_report": 0, + "label": "Packages", + "link_count": 2, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Package", + "link_count": 0, + "link_to": "Package", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Package Import", + "link_count": 0, + "link_to": "Package Import", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "description": "Automate processes and extend standard functionality using scripts and background jobs", + "hidden": 0, + "is_query_report": 0, + "label": "Scripting", + "link_count": 3, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Server Script", + "link_count": 0, + "link_to": "Server Script", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Client Script", + "link_count": 0, + "link_to": "Client Script", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Scheduled Job Type", + "link_count": 0, + "link_to": "Scheduled Job Type", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "description": "Build your own reports, print formats, and dashboards. Create personalized workspaces for easier navigation", + "hidden": 0, + "is_query_report": 0, + "label": "Views", + "link_count": 5, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Report", + "link_count": 0, + "link_to": "Report", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Print Format", + "link_count": 0, + "link_to": "Print Format", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Workspace", + "link_count": 0, + "link_to": "Workspace", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Dashboard", + "link_count": 0, + "link_to": "Dashboard", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Dashboard Chart", + "link_count": 0, + "link_to": "Dashboard Chart", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "description": "Create new forms and views with doctypes. Set up multi-level workflows for approval", + "hidden": 0, + "is_query_report": 0, + "label": "Models", + "link_count": 2, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "DocType", + "link_count": 0, + "link_to": "DocType", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Workflow", + "link_count": 0, + "link_to": "Workflow", + "link_type": "DocType", + "onboard": 0, + "only_for": "", + "type": "Link" + } + ], + "modified": "2024-01-24 12:27:44.769958", + "modified_by": "Administrator", + "module": "Core", + "name": "Build", + "number_cards": [], + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 27.0, + "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Print Format Builder", + "link_to": "print-format-builder", + "type": "Page" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "System Settings", + "link_to": "System Settings", + "type": "DocType", + "url": "https://xhiveframework.school/courses/xhiveframework-framework-course?utm_source=in_app" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Client Script", + "link_to": "Client Script", + "type": "DocType" + }, + { + "doc_view": "", + "label": "DocType", + "link_to": "DocType", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Customize Form", + "link_to": "Customize Form", + "type": "DocType" + }, + { + "color": "Grey", + "doc_view": "List", + "label": "Server Script", + "link_to": "Server Script", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Report", + "link_to": "Report", + "type": "DocType" + } + ], + "title": "Build" +} diff --git a/xhiveframework/core/workspace/users/users.json b/xhiveframework/core/workspace/users/users.json new file mode 100644 index 0000000..3899ad5 --- /dev/null +++ b/xhiveframework/core/workspace/users/users.json @@ -0,0 +1,202 @@ +{ + "charts": [], + "content": "[{\"id\":\"YpGCeLfign\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"b7abeqw4NZ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"User\",\"col\":3}},{\"id\":\"eghSJPhZRC\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Role\",\"col\":3}},{\"id\":\"uAzl_lT_C0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Permission Manager\",\"col\":3}},{\"id\":\"EpBz2lplSt\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"User Profile\",\"col\":3}},{\"id\":\"vHWhzaFoAH\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"User Type\",\"col\":3}},{\"id\":\"oFB4l28FMU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yJNNylguxk\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"NMpIkExl3i\",\"type\":\"card\",\"data\":{\"card_name\":\"Users\",\"col\":4}},{\"id\":\"VepG3durKm\",\"type\":\"card\",\"data\":{\"card_name\":\"Logs\",\"col\":4}},{\"id\":\"S9FeWt7xXE\",\"type\":\"card\",\"data\":{\"card_name\":\"Permissions\",\"col\":4}}]", + "creation": "2020-03-02 15:12:16.754449", + "custom_blocks": [], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "users", + "idx": 0, + "is_hidden": 0, + "label": "Users", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Logs", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Activity Log", + "link_count": 0, + "link_to": "Activity Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Access Log", + "link_count": 0, + "link_to": "Access Log", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Permissions", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Role Permissions Manager", + "link_count": 0, + "link_to": "permission-manager", + "link_type": "Page", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "User Permissions", + "link_count": 0, + "link_to": "User Permission", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Role Permission for Page and Report", + "link_count": 0, + "link_to": "Role Permission for Page and Report", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "User", + "hidden": 0, + "is_query_report": 1, + "label": "Permitted Documents For User", + "link_count": 0, + "link_to": "Permitted Documents For User", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "DocShare", + "hidden": 0, + "is_query_report": 0, + "label": "Document Share Report", + "link_count": 0, + "link_to": "Document Share Report", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Users", + "link_count": 4, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "User", + "link_count": 0, + "link_to": "User", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Role", + "link_count": 0, + "link_to": "Role", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Role Profile", + "link_count": 0, + "link_to": "Role Profile", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Module Profile", + "link_count": 0, + "link_to": "Module Profile", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2024-01-02 15:39:13.811700", + "modified_by": "Administrator", + "module": "Core", + "name": "Users", + "number_cards": [], + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 13.0, + "shortcuts": [ + { + "label": "User", + "link_to": "User", + "type": "DocType" + }, + { + "label": "Role", + "link_to": "Role", + "type": "DocType" + }, + { + "label": "Permission Manager", + "link_to": "permission-manager", + "type": "Page" + }, + { + "label": "User Profile", + "link_to": "user-profile", + "type": "Page" + }, + { + "doc_view": "", + "label": "User Type", + "link_to": "User Type", + "type": "DocType" + } + ], + "title": "Users" +} \ No newline at end of file diff --git a/xhiveframework/core/workspace/welcome_workspace/welcome_workspace.json b/xhiveframework/core/workspace/welcome_workspace/welcome_workspace.json new file mode 100644 index 0000000..1b45434 --- /dev/null +++ b/xhiveframework/core/workspace/welcome_workspace/welcome_workspace.json @@ -0,0 +1,28 @@ +{ + "charts": [], + "content": "[{\"id\":\"2eyXSHwMTE\",\"type\":\"header\",\"data\":{\"text\":\"Hi,\",\"col\":12}},{\"id\":\"ZusKvFOXgu\",\"type\":\"paragraph\",\"data\":{\"text\":\"I guess you don't have access to any workspace yet, but you can create one just for yourself. Click on the Create Workspace button to create one.
    \",\"col\":12}}]", + "creation": "2023-07-28 17:14:28.608321", + "custom_blocks": [], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "image-view", + "idx": 1, + "is_hidden": 0, + "label": "Welcome Workspace", + "links": [], + "modified": "2023-07-28 20:15:32.222029", + "modified_by": "Administrator", + "module": "Core", + "name": "Welcome Workspace", + "number_cards": [], + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "roles": [], + "sequence_id": 22.0, + "shortcuts": [], + "title": "Welcome Workspace" +} \ No newline at end of file diff --git a/xhiveframework/coverage.py b/xhiveframework/coverage.py new file mode 100644 index 0000000..1605a5a --- /dev/null +++ b/xhiveframework/coverage.py @@ -0,0 +1,77 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE +""" + xhiveframework.coverage + ~~~~~~~~~~~~~~~~ + + Coverage settings for xhiveframework +""" + +STANDARD_INCLUSIONS = ["*.py"] + +STANDARD_EXCLUSIONS = [ + "*.js", + "*.xml", + "*.pyc", + "*.css", + "*.less", + "*.scss", + "*.vue", + "*.html", + "*/test_*", + "*/node_modules/*", + "*/doctype/*/*_dashboard.py", + "*/patches/*", +] + +# tested via commands' test suite +TESTED_VIA_CLI = [ + "*/xhiveframework/installer.py", + "*/xhiveframework/build.py", + "*/xhiveframework/database/__init__.py", + "*/xhiveframework/database/db_manager.py", + "*/xhiveframework/database/**/setup_db.py", +] + +XHIVEFRAMEWORK_EXCLUSIONS = [ + "*/tests/*", + "*/commands/*", + "*/xhiveframework/change_log/*", + "*/xhiveframework/exceptions*", + "*/xhiveframework/coverage.py", + "*xhiveframework/setup.py", + "*/doctype/*/*_dashboard.py", + "*/patches/*", + *TESTED_VIA_CLI, +] + + +class CodeCoverage: + def __init__(self, with_coverage, app): + self.with_coverage = with_coverage + self.app = app or "xhiveframework" + + def __enter__(self): + if self.with_coverage: + import os + + from coverage import Coverage + + from xhiveframework.utils import get_bench_path + + # Generate coverage report only for app that is being tested + source_path = os.path.join(get_bench_path(), "apps", self.app) + omit = STANDARD_EXCLUSIONS[:] + + if self.app == "xhiveframework": + omit.extend(XHIVEFRAMEWORK_EXCLUSIONS) + + self.coverage = Coverage(source=[source_path], omit=omit, include=STANDARD_INCLUSIONS) + self.coverage.start() + + def __exit__(self, exc_type, exc_value, traceback): + if self.with_coverage: + self.coverage.stop() + self.coverage.save() + self.coverage.xml_report() + print("Saved Coverage") diff --git a/xhiveframework/custom/__init__.py b/xhiveframework/custom/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/doctype/__init__.py b/xhiveframework/custom/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/doctype/client_script/README.md b/xhiveframework/custom/doctype/client_script/README.md new file mode 100644 index 0000000..df0eded --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/README.md @@ -0,0 +1,11 @@ +Client or server script appended to standard DocType code. + +For client: + +- Code is appended to the js code. +- Best practice is to declare additional methods beginning with prefix `custom_` e.g. `custom_validate` to standard events. + +For server: + +- Script is appended to module code before DocType is `execute`d. +- All class methods must be written with a tab \ No newline at end of file diff --git a/xhiveframework/custom/doctype/client_script/__init__.py b/xhiveframework/custom/doctype/client_script/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/custom/doctype/client_script/client_script.js b/xhiveframework/custom/doctype/client_script/client_script.js new file mode 100644 index 0000000..33d129d --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/client_script.js @@ -0,0 +1,158 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Client Script", { + setup(frm) { + frm.get_field("sample").html(SAMPLE_HTML); + }, + refresh(frm) { + if (frm.doc.dt && frm.doc.script) { + frm.add_custom_button(__("Go to {0}", [frm.doc.dt]), () => + xhiveframework.set_route("List", frm.doc.dt, "List") + ); + } + + if (frm.doc.view == "Form") { + frm.add_custom_button(__("Add script for Child Table"), () => { + xhiveframework.model.with_doctype(frm.doc.dt, () => { + const child_tables = xhiveframework.meta + .get_docfields(frm.doc.dt, null, { + fieldtype: "Table", + }) + .map((df) => df.options); + + const d = new xhiveframework.ui.Dialog({ + title: __("Select Child Table"), + fields: [ + { + label: __("Select Child Table"), + fieldtype: "Link", + fieldname: "cdt", + options: "DocType", + get_query: () => { + return { + filters: { + istable: 1, + name: ["in", child_tables], + }, + }; + }, + }, + ], + primary_action: ({ cdt }) => { + cdt = d.get_field("cdt").value; + frm.events.add_script_for_doctype(frm, cdt); + d.hide(); + }, + }); + + d.show(); + }); + }); + + if (!frm.is_new()) { + frm.add_custom_button(__("Compare Versions"), () => { + new xhiveframework.ui.DiffView("Client Script", "script", frm.doc.name); + }); + } + } + + frm.set_query("dt", { + filters: { + istable: 0, + }, + }); + }, + + dt(frm) { + frm.toggle_display("view", !xhiveframework.boot.single_types.includes(frm.doc.dt)); + + if (!frm.doc.script) { + frm.events.add_script_for_doctype(frm, frm.doc.dt); + } + + if (frm.doc.script && !frm.doc.script.includes(frm.doc.dt)) { + frm.doc.script = ""; + frm.events.add_script_for_doctype(frm, frm.doc.dt); + } + }, + + view(frm) { + let has_form_boilerplate = frm.doc.script.includes("xhiveframework.ui.form.on"); + if (frm.doc.view === "List" && has_form_boilerplate) { + frm.set_value("script", ""); + } + if (frm.doc.view === "Form" && !has_form_boilerplate) { + frm.trigger("dt"); + } + }, + + add_script_for_doctype(frm, doctype) { + if (!doctype) return; + let boilerplate = ` +xhiveframework.ui.form.on('${doctype}', { + refresh(frm) { + // your code here + } +}) + `.trim(); + let script = frm.doc.script || ""; + if (script) { + script += "\n\n"; + } + frm.set_value("script", script + boilerplate); + }, +}); + +const SAMPLE_HTML = `

    Client Script Help

    +

    Client Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

    +
    
    +
    +// fetch local_tax_no on selection of customer
    +// cur_frm.add_fetch(link_field,  source_fieldname,  target_fieldname);
    +cur_frm.add_fetch("customer",  "local_tax_no',  'local_tax_no');
    +
    +// additional validation on dates
    +xhiveframework.ui.form.on('Task',  'validate',  function(frm) {
    +    if (frm.doc.from_date < get_today()) {
    +        msgprint('You can not select past date in From Date');
    +        validated = false;
    +    }
    +});
    +
    +// make a field read-only after saving
    +xhiveframework.ui.form.on('Task',  {
    +    refresh: function(frm) {
    +        // use the __islocal value of doc,  to check if the doc is saved or not
    +        frm.set_df_property('myfield',  'read_only',  frm.doc.__islocal ? 0 : 1);
    +    }
    +});
    +
    +// additional permission check
    +xhiveframework.ui.form.on('Task',  {
    +    validate: function(frm) {
    +        if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {
    +            msgprint('You are only allowed Material Receipt');
    +            validated = false;
    +        }
    +    }
    +});
    +
    +// calculate sales incentive
    +xhiveframework.ui.form.on('Sales Invoice',  {
    +    validate: function(frm) {
    +        // calculate incentives for each person on the deal
    +        total_incentive = 0
    +        $.each(frm.doc.sales_team,  function(i,  d) {
    +            // calculate incentive
    +            var incentive_percent = 2;
    +            if(frm.doc.base_grand_total > 400) incentive_percent = 4;
    +            // actual incentive
    +            d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;
    +            total_incentive += flt(d.incentives)
    +        });
    +        frm.doc.total_incentive = total_incentive;
    +    }
    +})
    +
    +
    `; diff --git a/xhiveframework/custom/doctype/client_script/client_script.json b/xhiveframework/custom/doctype/client_script/client_script.json new file mode 100644 index 0000000..dddf0c0 --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/client_script.json @@ -0,0 +1,114 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "Prompt", + "creation": "2013-01-10 16:34:01", + "description": "Adds a custom client script to a DocType", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "dt", + "view", + "column_break_3", + "module", + "enabled", + "section_break_6", + "script", + "sample" + ], + "fields": [ + { + "fieldname": "dt", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "DocType", + "oldfieldname": "dt", + "oldfieldtype": "Link", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "script", + "fieldtype": "Code", + "label": "Script", + "oldfieldname": "script", + "oldfieldtype": "Code", + "options": "JS" + }, + { + "fieldname": "sample", + "fieldtype": "HTML", + "label": "Sample" + }, + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "default": "Form", + "fieldname": "view", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Apply To", + "options": "List\nForm", + "set_only_once": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module (for export)", + "options": "Module Def" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + } + ], + "icon": "fa fa-glass", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Custom", + "name": "Client Script", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/custom/doctype/client_script/client_script.py b/xhiveframework/custom/doctype/client_script/client_script.py new file mode 100644 index 0000000..d9c1534 --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/client_script.py @@ -0,0 +1,27 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.model.document import Document + + +class ClientScript(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + dt: DF.Link + enabled: DF.Check + module: DF.Link | None + script: DF.Code | None + view: DF.Literal["List", "Form"] + + # end: auto-generated types + def on_update(self): + xhiveframework.clear_cache(doctype=self.dt) + + def on_trash(self): + xhiveframework.clear_cache(doctype=self.dt) diff --git a/xhiveframework/custom/doctype/client_script/test_client_script.py b/xhiveframework/custom/doctype/client_script/test_client_script.py new file mode 100644 index 0000000..11de604 --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/test_client_script.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Client Script') + + +class TestClientScript(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/custom/doctype/client_script/ui_test_client_script.js b/xhiveframework/custom/doctype/client_script/ui_test_client_script.js new file mode 100644 index 0000000..0d202d6 --- /dev/null +++ b/xhiveframework/custom/doctype/client_script/ui_test_client_script.js @@ -0,0 +1,98 @@ +context("Client Script", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + it("should run form script in doctype form", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo form script", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script')`, + }, + true + ); + cy.visit("/app/todo/new", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + }, + }); + cy.get("@consoleLog").should("be.calledWith", "todo form script"); + }); + + it("should run list script in doctype list view", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo list script", + dt: "ToDo", + view: "List", + enabled: 1, + script: `console.log('todo list script')`, + }, + true + ); + cy.visit("/app/todo", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + }, + }); + cy.get("@consoleLog").should("be.calledWith", "todo list script"); + }); + + it("should not run disabled scripts", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo disabled list", + dt: "ToDo", + view: "List", + enabled: 0, + script: `console.log('todo disabled script')`, + }, + true + ); + cy.visit("/app/todo", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + }, + }); + cy.get("@consoleLog").should("not.be.calledWith", "todo disabled script"); + }); + + it("should run multiple scripts", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo form script 1", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script 1')`, + }, + true + ); + cy.insert_doc( + "Client Script", + { + name: "Todo form script 2", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script 2')`, + }, + true + ); + cy.visit("/app/todo/new", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + }, + }); + cy.get("@consoleLog").should("be.calledWith", "todo form script 1"); + cy.get("@consoleLog").should("be.calledWith", "todo form script 2"); + }); +}); diff --git a/xhiveframework/custom/doctype/custom_field/README.md b/xhiveframework/custom/doctype/custom_field/README.md new file mode 100644 index 0000000..5d6bf14 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/README.md @@ -0,0 +1 @@ +Custom Field added by the user (not part of DocType but part of table). Custom fields are automatically added to the DocType when they are loaded as metadata. \ No newline at end of file diff --git a/xhiveframework/custom/doctype/custom_field/__init__.py b/xhiveframework/custom/doctype/custom_field/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/custom/doctype/custom_field/custom_field.js b/xhiveframework/custom/doctype/custom_field/custom_field.js new file mode 100644 index 0000000..c00ede7 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/custom_field.js @@ -0,0 +1,152 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +// Refresh +// -------- + +xhiveframework.ui.form.on("Custom Field", { + setup: function (frm) { + frm.set_query("dt", function (doc) { + var filters = [ + ["DocType", "issingle", "=", 0], + ["DocType", "custom", "=", 0], + ["DocType", "name", "not in", xhiveframework.model.core_doctypes_list], + ["DocType", "restrict_to_domain", "in", xhiveframework.boot.active_domains], + ]; + if (xhiveframework.session.user !== "Administrator") { + filters.push(["DocType", "module", "not in", ["Core", "Custom"]]); + } + return { + filters: filters, + }; + }); + }, + refresh: function (frm) { + frm.toggle_enable("dt", frm.doc.__islocal); + frm.trigger("dt"); + frm.toggle_reqd("label", !frm.doc.fieldname); + frm.trigger("add_rename_field"); + + if (frm.doc.is_system_generated) { + frm.dashboard.add_comment( + __( + "Warning: This field is system generated and may be overwritten by a future update. Modify it using {0} instead.", + [ + xhiveframework.utils.get_form_link( + "Customize Form", + "Customize Form", + true, + __("Customize Form"), + { + doc_type: frm.doc.dt, + } + ), + ] + ), + "yellow", + true + ); + } + }, + dt: function (frm) { + if (!frm.doc.dt) { + set_field_options("insert_after", ""); + return; + } + var insert_after = frm.doc.insert_after || null; + return xhiveframework.call({ + method: "xhiveframework.custom.doctype.custom_field.custom_field.get_fields_label", + args: { doctype: frm.doc.dt, fieldname: frm.doc.fieldname }, + callback: function (r) { + if (r) { + if (r._server_messages && r._server_messages.length) { + frm.set_value("dt", ""); + } else { + set_field_options("insert_after", r.message); + var fieldnames = $.map(r.message, function (v) { + return v.value; + }); + + if (insert_after == null || !fieldnames.includes(insert_after)) { + insert_after = fieldnames[-1]; + } + + frm.set_value("insert_after", insert_after); + } + } + }, + }); + }, + label: function (frm) { + if (frm.doc.label && xhiveframework.utils.has_special_chars(frm.doc.label)) { + frm.fields_dict["label_help"].disp_area.innerHTML = + '' + __("Special Characters are not allowed") + ""; + frm.set_value("label", ""); + } else { + frm.fields_dict["label_help"].disp_area.innerHTML = ""; + } + }, + fieldtype: function (frm) { + if (frm.doc.fieldtype == "Link") { + frm.fields_dict["options_help"].disp_area.innerHTML = __( + "Name of the Document Type (DocType) you want this field to be linked to. e.g. Customer" + ); + } else if (frm.doc.fieldtype == "Select") { + frm.fields_dict["options_help"].disp_area.innerHTML = + __("Options for select. Each option on a new line.") + + " " + + __("e.g.:") + + "
    " + + __("Option 1") + + "
    " + + __("Option 2") + + "
    " + + __("Option 3") + + "
    "; + } else if (frm.doc.fieldtype == "Dynamic Link") { + frm.fields_dict["options_help"].disp_area.innerHTML = __( + "Fieldname which will be the DocType for this link field." + ); + } else { + frm.fields_dict["options_help"].disp_area.innerHTML = ""; + } + }, + add_rename_field(frm) { + if (!frm.is_new()) { + frm.add_custom_button(__("Rename Fieldname"), () => { + xhiveframework.prompt( + { + fieldtype: "Data", + label: __("Fieldname"), + fieldname: "fieldname", + reqd: 1, + default: frm.doc.fieldname, + }, + function (data) { + xhiveframework + .xcall( + "xhiveframework.custom.doctype.custom_field.custom_field.rename_fieldname", + { + custom_field: frm.doc.name, + fieldname: data.fieldname, + } + ) + .then(() => frm.reload()); + }, + __("Rename Fieldname"), + __("Rename") + ); + }); + } + }, +}); + +xhiveframework.utils.has_special_chars = function (t) { + var iChars = "!@#$%^&*()+=-[]\\';,./{}|\":<>?"; + for (var i = 0; i < t.length; i++) { + if (iChars.indexOf(t.charAt(i)) != -1) { + return true; + } + } + return false; +}; diff --git a/xhiveframework/custom/doctype/custom_field/custom_field.json b/xhiveframework/custom/doctype/custom_field/custom_field.json new file mode 100644 index 0000000..32c4552 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/custom_field.json @@ -0,0 +1,494 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2024-02-21 18:14:42.281748", + "description": "Adds a custom field to a DocType", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "is_system_generated", + "dt", + "module", + "label", + "label_help", + "fieldname", + "insert_after", + "length", + "column_break_6", + "fieldtype", + "precision", + "hide_seconds", + "hide_days", + "options", + "sort_options", + "fetch_from", + "fetch_if_empty", + "options_help", + "section_break_11", + "collapsible", + "collapsible_depends_on", + "default", + "depends_on", + "mandatory_depends_on", + "read_only_depends_on", + "properties", + "non_negative", + "reqd", + "unique", + "is_virtual", + "read_only", + "ignore_user_permissions", + "hidden", + "print_hide", + "print_hide_if_no_value", + "print_width", + "no_copy", + "allow_on_submit", + "in_list_view", + "in_standard_filter", + "in_global_search", + "in_preview", + "bold", + "report_hide", + "search_index", + "allow_in_quick_entry", + "ignore_xss_filter", + "translatable", + "hide_border", + "show_dashboard", + "description", + "permlevel", + "width", + "columns" + ], + "fields": [ + { + "bold": 1, + "fieldname": "dt", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "DocType", + "oldfieldname": "dt", + "oldfieldtype": "Link", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "bold": 1, + "fieldname": "label", + "fieldtype": "Data", + "in_filter": 1, + "label": "Label", + "no_copy": 1, + "oldfieldname": "label", + "oldfieldtype": "Data" + }, + { + "fieldname": "label_help", + "fieldtype": "HTML", + "label": "Label Help", + "oldfieldtype": "HTML" + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Fieldname", + "no_copy": 1, + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "description": "Select the label after which you want to insert new field.", + "fieldname": "insert_after", + "fieldtype": "Select", + "label": "Insert After", + "no_copy": 1, + "oldfieldname": "insert_after", + "oldfieldtype": "Select" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "default": "Data", + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_filter": 1, + "in_list_view": 1, + "label": "Field Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nJSON\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nPhone\nRead Only\nRating\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime", + "reqd": 1, + "sort_options": 1 + }, + { + "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", + "description": "Set non-standard precision for a Float or Currency field", + "fieldname": "precision", + "fieldtype": "Select", + "label": "Precision", + "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" + }, + { + "fieldname": "options", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text" + }, + { + "fieldname": "fetch_from", + "fieldtype": "Small Text", + "label": "Fetch From" + }, + { + "default": "0", + "description": "If unchecked, the value will always be re-fetched on save.", + "fieldname": "fetch_if_empty", + "fieldtype": "Check", + "label": "Fetch on Save if Empty" + }, + { + "fieldname": "options_help", + "fieldtype": "HTML", + "label": "Options Help", + "oldfieldtype": "HTML" + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible", + "fieldtype": "Check", + "label": "Collapsible" + }, + { + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible_depends_on", + "fieldtype": "Code", + "label": "Collapsible Depends On" + }, + { + "fieldname": "default", + "fieldtype": "Text", + "label": "Default Value", + "oldfieldname": "default", + "oldfieldtype": "Text" + }, + { + "fieldname": "depends_on", + "fieldtype": "Code", + "label": "Depends On", + "length": 255 + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Field Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "default": "0", + "fieldname": "permlevel", + "fieldtype": "Int", + "label": "Permission Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int" + }, + { + "fieldname": "width", + "fieldtype": "Data", + "label": "Width", + "oldfieldname": "width", + "oldfieldtype": "Data" + }, + { + "description": "Number of columns for a field in a List View or a Grid (Total Columns should be less than 11)", + "fieldname": "columns", + "fieldtype": "Int", + "label": "Columns" + }, + { + "fieldname": "properties", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, + { + "default": "0", + "fieldname": "reqd", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Mandatory Field", + "oldfieldname": "reqd", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "unique", + "fieldtype": "Check", + "label": "Unique" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype===\"Link\"", + "fieldname": "ignore_user_permissions", + "fieldtype": "Check", + "label": "Ignore User Permissions" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "default": "0", + "fieldname": "print_hide", + "fieldtype": "Check", + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", + "fieldname": "print_hide_if_no_value", + "fieldtype": "Check", + "label": "Print Hide If No Value" + }, + { + "fieldname": "print_width", + "fieldtype": "Data", + "hidden": 1, + "label": "Print Width", + "no_copy": 1, + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy", + "oldfieldname": "no_copy", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View" + }, + { + "default": "0", + "fieldname": "in_standard_filter", + "fieldtype": "Check", + "label": "In Standard Filter" + }, + { + "default": "0", + "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", + "fieldname": "in_global_search", + "fieldtype": "Check", + "label": "In Global Search" + }, + { + "default": "0", + "fieldname": "bold", + "fieldtype": "Check", + "label": "Bold" + }, + { + "default": "0", + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "search_index", + "fieldtype": "Check", + "hidden": 1, + "label": "Index", + "no_copy": 1, + "print_hide": 1 + }, + { + "default": "0", + "description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", + "fieldname": "ignore_xss_filter", + "fieldtype": "Check", + "label": "Ignore XSS Filter" + }, + { + "default": "0", + "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", + "fieldname": "translatable", + "fieldtype": "Check", + "label": "Translatable" + }, + { + "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image', 'Int'], doc.fieldtype)", + "fieldname": "length", + "fieldtype": "Int", + "label": "Length" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "length": 255 + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "length": 255 + }, + { + "default": "0", + "fieldname": "allow_in_quick_entry", + "fieldtype": "Check", + "label": "Allow in Quick Entry" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", + "fieldname": "in_preview", + "fieldtype": "Check", + "label": "In Preview" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_seconds", + "fieldtype": "Check", + "label": "Hide Seconds" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_days", + "fieldtype": "Check", + "label": "Hide Days" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Section Break'", + "fieldname": "hide_border", + "fieldtype": "Check", + "label": "Hide Border" + }, + { + "default": "0", + "depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)", + "fieldname": "non_negative", + "fieldtype": "Check", + "label": "Non Negative" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module (for export)", + "options": "Module Def" + }, + { + "default": "0", + "fieldname": "is_system_generated", + "fieldtype": "Check", + "label": "Is System Generated", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval: doc.fieldtype === 'Select'", + "fieldname": "sort_options", + "fieldtype": "Check", + "label": "Sort Options" + }, + { + "default": "0", + "fieldname": "show_dashboard", + "fieldtype": "Check", + "label": "Show Dashboard" + } + ], + "icon": "fa fa-glass", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-03-07 17:34:47.167183", + "modified_by": "Administrator", + "module": "Custom", + "name": "Custom Field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "dt,label,fieldtype,options", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/custom/doctype/custom_field/custom_field.py b/xhiveframework/custom/doctype/custom_field/custom_field.py new file mode 100644 index 0000000..d143d10 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/custom_field.py @@ -0,0 +1,391 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import core_doctypes_list +from xhiveframework.model.docfield import supports_translation +from xhiveframework.model.document import Document +from xhiveframework.query_builder.functions import IfNull +from xhiveframework.utils import cstr, random_string + + +class CustomField(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow_in_quick_entry: DF.Check + allow_on_submit: DF.Check + bold: DF.Check + collapsible: DF.Check + collapsible_depends_on: DF.Code | None + columns: DF.Int + default: DF.Text | None + depends_on: DF.Code | None + description: DF.Text | None + dt: DF.Link + fetch_from: DF.SmallText | None + fetch_if_empty: DF.Check + fieldname: DF.Data | None + fieldtype: DF.Literal[ + "Autocomplete", + "Attach", + "Attach Image", + "Barcode", + "Button", + "Check", + "Code", + "Color", + "Column Break", + "Currency", + "Data", + "Date", + "Datetime", + "Duration", + "Dynamic Link", + "Float", + "Fold", + "Geolocation", + "Heading", + "HTML", + "HTML Editor", + "Icon", + "Image", + "Int", + "JSON", + "Link", + "Long Text", + "Markdown Editor", + "Password", + "Percent", + "Phone", + "Read Only", + "Rating", + "Section Break", + "Select", + "Signature", + "Small Text", + "Tab Break", + "Table", + "Table MultiSelect", + "Text", + "Text Editor", + "Time", + ] + hidden: DF.Check + hide_border: DF.Check + hide_days: DF.Check + hide_seconds: DF.Check + ignore_user_permissions: DF.Check + ignore_xss_filter: DF.Check + in_global_search: DF.Check + in_list_view: DF.Check + in_preview: DF.Check + in_standard_filter: DF.Check + insert_after: DF.Literal[None] + is_system_generated: DF.Check + is_virtual: DF.Check + label: DF.Data | None + length: DF.Int + mandatory_depends_on: DF.Code | None + module: DF.Link | None + no_copy: DF.Check + non_negative: DF.Check + options: DF.SmallText | None + permlevel: DF.Int + precision: DF.Literal["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + print_hide: DF.Check + print_hide_if_no_value: DF.Check + print_width: DF.Data | None + read_only: DF.Check + read_only_depends_on: DF.Code | None + report_hide: DF.Check + reqd: DF.Check + search_index: DF.Check + show_dashboard: DF.Check + sort_options: DF.Check + translatable: DF.Check + unique: DF.Check + width: DF.Data | None + + # end: auto-generated types + def autoname(self): + self.set_fieldname() + self.name = self.dt + "-" + self.fieldname + + def set_fieldname(self): + restricted = ( + "name", + "parent", + "creation", + "modified", + "modified_by", + "parentfield", + "parenttype", + "file_list", + "flags", + "docstatus", + ) + if not self.fieldname: + label = self.label + if not label: + if self.fieldtype in ["Section Break", "Column Break", "Tab Break"]: + label = self.fieldtype + "_" + str(random_string(5)) + else: + xhiveframework.throw(_("Label is mandatory")) + + # remove special characters from fieldname + self.fieldname = "".join( + [c for c in cstr(label).replace(" ", "_") if c.isdigit() or c.isalpha() or c == "_"] + ) + self.fieldname = f"custom_{self.fieldname}" + + # fieldnames should be lowercase + self.fieldname = self.fieldname.lower() + + if self.fieldname in restricted: + self.fieldname = self.fieldname + "1" + + def before_insert(self): + self.set_fieldname() + + def validate(self): + # these imports have been added to avoid cyclical import, should fix in future + from xhiveframework.core.doctype.doctype.doctype import check_fieldname_conflicts + from xhiveframework.custom.doctype.customize_form.customize_form import CustomizeForm + + # don't always get meta to improve performance + # setting idx is just an improvement, not a requirement + if self.is_new() or self.insert_after == "append": + meta = xhiveframework.get_meta(self.dt, cached=False) + fieldnames = [df.fieldname for df in meta.get("fields")] + + if self.is_new() and self.fieldname in fieldnames: + xhiveframework.throw( + _("A field with the name {0} already exists in {1}").format( + xhiveframework.bold(self.fieldname), self.dt + ) + ) + + if self.insert_after == "append": + self.insert_after = fieldnames[-1] + + if self.insert_after and self.insert_after in fieldnames: + self.idx = fieldnames.index(self.insert_after) + 1 + + if ( + not self.is_virtual + and (doc_before_save := self.get_doc_before_save()) + and (old_fieldtype := doc_before_save.fieldtype) != self.fieldtype + and not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype) + ): + xhiveframework.throw( + _("Fieldtype cannot be changed from {0} to {1}").format(old_fieldtype, self.fieldtype) + ) + + if not self.fieldname: + xhiveframework.throw(_("Fieldname not set for Custom Field")) + + if self.get("translatable", 0) and not supports_translation(self.fieldtype): + self.translatable = 0 + + check_fieldname_conflicts(self) + + def on_update(self): + # validate field + if not self.flags.ignore_validate: + from xhiveframework.core.doctype.doctype.doctype import validate_fields_for_doctype + + validate_fields_for_doctype(self.dt) + + # clear cache and update the schema + if not xhiveframework.flags.in_create_custom_fields: + xhiveframework.clear_cache(doctype=self.dt) + xhiveframework.db.updatedb(self.dt) + + def on_trash(self): + # check if Admin owned field + if self.owner == "Administrator" and xhiveframework.session.user != "Administrator": + xhiveframework.throw( + _( + "Custom Field {0} is created by the Administrator and can only be deleted through the Administrator account." + ).format(xhiveframework.bold(self.label)) + ) + + # delete property setter entries + xhiveframework.db.delete("Property Setter", {"doc_type": self.dt, "field_name": self.fieldname}) + + # update doctype layouts + doctype_layouts = xhiveframework.get_all("DocType Layout", filters={"document_type": self.dt}, pluck="name") + + for layout in doctype_layouts: + layout_doc = xhiveframework.get_doc("DocType Layout", layout) + for field in layout_doc.fields: + if field.fieldname == self.fieldname: + layout_doc.remove(field) + layout_doc.save() + break + + xhiveframework.clear_cache(doctype=self.dt) + + def validate_insert_after(self, meta): + if not meta.get_field(self.insert_after): + xhiveframework.throw( + _( + "Insert After field '{0}' mentioned in Custom Field '{1}', with label '{2}', does not exist" + ).format(self.insert_after, self.name, self.label), + xhiveframework.DoesNotExistError, + ) + + if self.fieldname == self.insert_after: + xhiveframework.throw(_("Insert After cannot be set as {0}").format(meta.get_label(self.insert_after))) + + +@xhiveframework.whitelist() +def get_fields_label(doctype=None): + meta = xhiveframework.get_meta(doctype) + + if doctype in core_doctypes_list: + return xhiveframework.msgprint(_("Custom Fields cannot be added to core DocTypes.")) + + if meta.custom: + return xhiveframework.msgprint(_("Custom Fields can only be added to a standard DocType.")) + + return [ + {"value": df.fieldname or "", "label": _(df.label, context=df.parent) if df.label else ""} + for df in xhiveframework.get_meta(doctype).get("fields") + ] + + +def create_custom_field_if_values_exist(doctype, df): + df = xhiveframework._dict(df) + if df.fieldname in xhiveframework.db.get_table_columns(doctype) and xhiveframework.db.count( + dt=doctype, filters=IfNull(df.fieldname, "") != "" + ): + create_custom_field(doctype, df) + + +def create_custom_field(doctype, df, ignore_validate=False, is_system_generated=True): + df = xhiveframework._dict(df) + if not df.fieldname and df.label: + df.fieldname = xhiveframework.scrub(df.label) + if not xhiveframework.db.get_value("Custom Field", {"dt": doctype, "fieldname": df.fieldname}): + custom_field = xhiveframework.get_doc( + { + "doctype": "Custom Field", + "dt": doctype, + "permlevel": 0, + "fieldtype": "Data", + "hidden": 0, + "is_system_generated": is_system_generated, + } + ) + custom_field.update(df) + custom_field.flags.ignore_validate = ignore_validate + custom_field.insert() + return custom_field + + +def create_custom_fields(custom_fields: dict, ignore_validate=False, update=True): + """Add / update multiple custom fields + + :param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`""" + + try: + xhiveframework.flags.in_create_custom_fields = True + doctypes_to_update = set() + + if xhiveframework.flags.in_setup_wizard: + ignore_validate = True + + for doctypes, fields in custom_fields.items(): + if isinstance(fields, dict): + # only one field + fields = [fields] + + if isinstance(doctypes, str): + # only one doctype + doctypes = (doctypes,) + + for doctype in doctypes: + doctypes_to_update.add(doctype) + + for df in fields: + field = xhiveframework.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) + if not field: + try: + df = df.copy() + df["owner"] = "Administrator" + create_custom_field(doctype, df, ignore_validate=ignore_validate) + + except xhiveframework.exceptions.DuplicateEntryError: + pass + + elif update: + custom_field = xhiveframework.get_doc("Custom Field", field) + custom_field.flags.ignore_validate = ignore_validate + custom_field.update(df) + custom_field.save() + + for doctype in doctypes_to_update: + xhiveframework.clear_cache(doctype=doctype) + xhiveframework.db.updatedb(doctype) + + finally: + xhiveframework.flags.in_create_custom_fields = False + + +@xhiveframework.whitelist() +def rename_fieldname(custom_field: str, fieldname: str): + xhiveframework.only_for("System Manager") + + field: CustomField = xhiveframework.get_doc("Custom Field", custom_field) + parent_doctype = field.dt + old_fieldname = field.fieldname + field.fieldname = fieldname + field.set_fieldname() + new_fieldname = field.fieldname + + if field.is_system_generated: + xhiveframework.throw(_("System Generated Fields can not be renamed")) + if xhiveframework.db.has_column(parent_doctype, fieldname): + xhiveframework.throw(_("Can not rename as column {0} is already present on DocType.").format(fieldname)) + if old_fieldname == new_fieldname: + xhiveframework.msgprint(_("Old and new fieldnames are same."), alert=True) + return + + if xhiveframework.db.has_column(field.dt, old_fieldname): + xhiveframework.db.rename_column(parent_doctype, old_fieldname, new_fieldname) + + # Update in DB after alter column is successful, alter column will implicitly commit, so it's + # best to commit change on field too to avoid any possible mismatch between two. + field.db_set("fieldname", field.fieldname, notify=True) + _update_fieldname_references(field, old_fieldname, new_fieldname) + + xhiveframework.msgprint(_("Custom field renamed to {0} successfully.").format(fieldname), alert=True) + xhiveframework.db.commit() + xhiveframework.clear_cache() + + +def _update_fieldname_references(field: CustomField, old_fieldname: str, new_fieldname: str) -> None: + # Passwords are stored in auth table, so column name needs to be updated there. + if field.fieldtype == "Password": + Auth = xhiveframework.qb.Table("__Auth") + xhiveframework.qb.update(Auth).set(Auth.fieldname, new_fieldname).where( + (Auth.doctype == field.dt) & (Auth.fieldname == old_fieldname) + ).run() + + # Update ordering reference. + xhiveframework.db.set_value( + "Custom Field", + {"insert_after": old_fieldname, "dt": field.dt}, + "insert_after", + new_fieldname, + ) diff --git a/xhiveframework/custom/doctype/custom_field/test_custom_field.py b/xhiveframework/custom/doctype/custom_field/test_custom_field.py new file mode 100644 index 0000000..67f84e9 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/test_custom_field.py @@ -0,0 +1,107 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.custom.doctype.custom_field.custom_field import ( + create_custom_field, + create_custom_fields, + rename_fieldname, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Custom Field") + + +class TestCustomField(XhiveFrameworkTestCase): + def test_create_custom_fields(self): + create_custom_fields( + { + "Address": [ + { + "fieldname": "_test_custom_field_1", + "label": "_Test Custom Field 1", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + ("Address", "Contact"): [ + { + "fieldname": "_test_custom_field_2", + "label": "_Test Custom Field 2", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + } + ) + + xhiveframework.db.commit() + + self.assertTrue(xhiveframework.db.exists("Custom Field", "Address-_test_custom_field_1")) + self.assertTrue(xhiveframework.db.exists("Custom Field", "Address-_test_custom_field_2")) + self.assertTrue(xhiveframework.db.exists("Custom Field", "Contact-_test_custom_field_2")) + + def test_custom_field_sorting(self): + try: + custom_fields = { + "ToDo": [ + {"fieldname": "a_test_field", "insert_after": "b_test_field"}, + {"fieldname": "b_test_field", "insert_after": "status"}, + {"fieldname": "c_test_field", "insert_after": "unknown_custom_field"}, + {"fieldname": "d_test_field", "insert_after": "status"}, + ] + } + + create_custom_fields(custom_fields, ignore_validate=True) + + fields = xhiveframework.get_meta("ToDo", cached=False).fields + + for i, field in enumerate(fields): + if field.fieldname == "b_test_field": + self.assertEqual(fields[i - 1].fieldname, "status") + + if field.fieldname == "d_test_field": + self.assertEqual(fields[i - 1].fieldname, "a_test_field") + + self.assertEqual(fields[-1].fieldname, "c_test_field") + + finally: + xhiveframework.db.delete( + "Custom Field", + { + "dt": "ToDo", + "fieldname": ( + "in", + ( + "a_test_field", + "b_test_field", + "c_test_field", + "d_test_field", + ), + ), + }, + ) + + # undo changes commited by DDL + # nosemgrep + xhiveframework.db.commit() + + def test_custom_field_renaming(self): + def gen_fieldname(): + return "test_" + xhiveframework.generate_hash() + + field = create_custom_field("ToDo", {"label": gen_fieldname()}, is_system_generated=False) + old = field.fieldname + new = gen_fieldname() + data = xhiveframework.generate_hash() + doc = xhiveframework.get_doc({"doctype": "ToDo", old: data, "description": "Something"}).insert() + + rename_fieldname(field.name, new) + field.reload() + self.assertEqual(field.fieldname, new) + + doc = xhiveframework.get_doc("ToDo", doc.name) # doc.reload doesn't clear old fields. + self.assertEqual(doc.get(new), data) + self.assertFalse(doc.get(old)) + + field.delete() diff --git a/xhiveframework/custom/doctype/custom_field/test_records.json b/xhiveframework/custom/doctype/custom_field/test_records.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/xhiveframework/custom/doctype/custom_field/test_records.json @@ -0,0 +1 @@ +[] diff --git a/xhiveframework/custom/doctype/customize_form/README.md b/xhiveframework/custom/doctype/customize_form/README.md new file mode 100644 index 0000000..280b66d --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/README.md @@ -0,0 +1 @@ +Create a DocType like fields table and allow user to set field properties for which **Property Setter** will be created when updated. \ No newline at end of file diff --git a/xhiveframework/custom/doctype/customize_form/__init__.py b/xhiveframework/custom/doctype/customize_form/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/custom/doctype/customize_form/customize_form.js b/xhiveframework/custom/doctype/customize_form/customize_form.js new file mode 100644 index 0000000..8875f58 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/customize_form.js @@ -0,0 +1,445 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.provide("xhiveframework.customize_form"); + +xhiveframework.ui.form.on("Customize Form", { + setup: function (frm) { + // save the last setting if refreshing + window.addEventListener("beforeunload", () => { + if (frm.doc.doc_type && frm.doc.doc_type != "undefined") { + localStorage["customize_doctype"] = frm.doc.doc_type; + } + }); + }, + + onload: function (frm) { + frm.set_query("doc_type", function () { + return { + filters: [ + ["DocType", "issingle", "=", 0], + ["DocType", "custom", "=", 0], + ["DocType", "name", "not in", xhiveframework.model.core_doctypes_list], + ["DocType", "restrict_to_domain", "in", xhiveframework.boot.active_domains], + ], + }; + }); + + frm.set_query("default_print_format", function () { + return { + filters: { + print_format_type: ["!=", "JS"], + doc_type: ["=", frm.doc.doc_type], + }, + }; + }); + + $(frm.wrapper).on("grid-row-render", function (e, grid_row) { + if (grid_row.doc && grid_row.doc.fieldtype == "Section Break") { + $(grid_row.row).css({ "font-weight": "bold" }); + } + + grid_row.row.removeClass("highlight"); + + if ( + grid_row.doc.is_custom_field && + !grid_row.row.hasClass("highlight") && + !grid_row.doc.is_system_generated + ) { + grid_row.row.addClass("highlight"); + } + }); + }, + + doc_type: function (frm) { + if (frm.doc.doc_type) { + return frm.call({ + method: "fetch_to_customize", + doc: frm.doc, + freeze: true, + callback: function (r) { + if (r) { + if (r._server_messages && r._server_messages.length) { + frm.set_value("doc_type", ""); + } else { + frm.refresh(); + frm.trigger("add_customize_child_table_button"); + frm.trigger("setup_default_views"); + } + } + localStorage["customize_doctype"] = frm.doc.doc_type; + }, + }); + } else { + frm.refresh(); + } + }, + + is_calendar_and_gantt: function (frm) { + frm.trigger("setup_default_views"); + }, + + add_customize_child_table_button: function (frm) { + frm.doc.fields.forEach(function (f) { + if (!["Table", "Table MultiSelect"].includes(f.fieldtype)) return; + + frm.add_custom_button( + __(f.options), + () => frm.set_value("doc_type", f.options), + __("Customize Child Table") + ); + }); + }, + + refresh: function (frm) { + frm.disable_save(true); + frm.page.clear_icons(); + + if (frm.doc.doc_type) { + xhiveframework.model.with_doctype(frm.doc.doc_type).then(() => { + frm.page.set_title(__("Customize Form - {0}", [__(frm.doc.doc_type)])); + xhiveframework.customize_form.set_primary_action(frm); + + frm.add_custom_button( + __("Go to {0} List", [__(frm.doc.doc_type)]), + function () { + xhiveframework.set_route("List", frm.doc.doc_type); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Set Permissions"), + function () { + xhiveframework.set_route("permission-manager", frm.doc.doc_type); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Reload"), + function () { + frm.script_manager.trigger("doc_type"); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Reset Layout"), + () => { + frm.trigger("reset_layout"); + }, + __("Actions") + ); + + frm.add_custom_button( + __("Reset All Customizations"), + function () { + xhiveframework.customize_form.confirm(__("Remove all customizations?"), frm); + }, + __("Actions") + ); + + const is_autoname_autoincrement = frm.doc.autoname === "autoincrement"; + frm.set_df_property("naming_rule", "hidden", is_autoname_autoincrement); + frm.set_df_property("autoname", "read_only", is_autoname_autoincrement); + frm.toggle_display( + ["queue_in_background"], + xhiveframework.get_meta(frm.doc.doc_type).is_submittable || 0 + ); + + render_form_builder(frm); + frm.get_field("form_builder").tab.set_active(); + }); + } + + frm.events.setup_export(frm); + frm.events.setup_sort_order(frm); + frm.events.set_default_doc_type(frm); + }, + + set_default_doc_type(frm) { + let doc_type; + if (xhiveframework.route_options && xhiveframework.route_options.doc_type) { + doc_type = xhiveframework.route_options.doc_type; + xhiveframework.route_options = null; + localStorage.removeItem("customize_doctype"); + } + if (!doc_type) { + doc_type = localStorage.getItem("customize_doctype"); + } + if (doc_type) { + setTimeout(() => frm.set_value("doc_type", doc_type, false, true), 1000); + } + }, + + reset_layout(frm) { + xhiveframework.confirm( + __("Layout will be reset to standard layout, are you sure you want to do this?"), + () => { + return frm.call({ + doc: frm.doc, + method: "reset_layout", + callback: function (r) { + if (!r.exc) { + xhiveframework.show_alert({ + message: __("Layout Reset"), + indicator: "green", + }); + xhiveframework.customize_form.clear_locals_and_refresh(frm); + } + }, + }); + } + ); + }, + + setup_export(frm) { + if (xhiveframework.boot.developer_mode) { + frm.add_custom_button( + __("Export Customizations"), + function () { + xhiveframework.prompt( + [ + { + fieldtype: "Link", + fieldname: "module", + options: "Module Def", + label: __("Module to Export"), + reqd: 1, + }, + { + fieldtype: "Check", + fieldname: "sync_on_migrate", + label: __("Sync on Migrate"), + default: 1, + }, + { + fieldtype: "Check", + fieldname: "with_permissions", + label: __("Export Custom Permissions"), + default: 1, + }, + ], + function (data) { + xhiveframework.call({ + method: "xhiveframework.modules.utils.export_customizations", + args: { + doctype: frm.doc.doc_type, + module: data.module, + sync_on_migrate: data.sync_on_migrate, + with_permissions: data.with_permissions, + }, + }); + }, + __("Select Module") + ); + }, + __("Actions") + ); + } + }, + + setup_sort_order(frm) { + // sort order select + if (frm.doc.doc_type) { + var fields = $.map(frm.doc.fields, function (df) { + return xhiveframework.model.is_value_type(df.fieldtype) ? df.fieldname : null; + }); + fields = ["", "name", "modified"].concat(fields); + frm.set_df_property("sort_field", "options", fields); + } + }, + + setup_default_views(frm) { + xhiveframework.model.set_default_views_for_doctype(frm.doc.doc_type, frm); + }, +}); + +// can't delete standard fields +xhiveframework.ui.form.on("Customize Form Field", { + before_fields_remove: function (frm, doctype, name) { + const row = xhiveframework.get_doc(doctype, name); + + if (row.is_system_generated) { + xhiveframework.throw( + __( + "Cannot delete system generated field {0}. You can hide it instead.", + [__(row.label) || row.fieldname] + ) + ); + } + + if (!(row.is_custom_field || row.__islocal)) { + xhiveframework.throw( + __("Cannot delete standard field {0}. You can hide it instead.", [ + __(row.label) || row.fieldname, + ]) + ); + } + }, + fields_add: function (frm, cdt, cdn) { + var f = xhiveframework.model.get_doc(cdt, cdn); + f.is_system_generated = false; + f.is_custom_field = true; + frm.trigger("setup_default_views"); + }, + + form_render(frm, doctype, docname) { + frm.trigger("setup_fetch_from_fields", doctype, docname); + }, +}); + +// can't delete standard links +xhiveframework.ui.form.on("DocType Link", { + before_links_remove: function (frm, doctype, name) { + let row = xhiveframework.get_doc(doctype, name); + if (!(row.custom || row.__islocal)) { + xhiveframework.msgprint(__("Cannot delete standard link. You can hide it if you want")); + throw "cannot delete standard link"; + } + }, + links_add: function (frm, cdt, cdn) { + let f = xhiveframework.model.get_doc(cdt, cdn); + f.custom = 1; + }, +}); + +// can't delete standard actions +xhiveframework.ui.form.on("DocType Action", { + before_actions_remove: function (frm, doctype, name) { + let row = xhiveframework.get_doc(doctype, name); + if (!(row.custom || row.__islocal)) { + xhiveframework.msgprint(__("Cannot delete standard action. You can hide it if you want")); + throw "cannot delete standard action"; + } + }, + actions_add: function (frm, cdt, cdn) { + let f = xhiveframework.model.get_doc(cdt, cdn); + f.custom = 1; + }, +}); + +// can't delete standard states +xhiveframework.ui.form.on("DocType State", { + before_states_remove: function (frm, doctype, name) { + let row = xhiveframework.get_doc(doctype, name); + if (!(row.custom || row.__islocal)) { + xhiveframework.msgprint(__("Cannot delete standard document state.")); + throw "cannot delete standard document state"; + } + }, + states_add: function (frm, cdt, cdn) { + let f = xhiveframework.model.get_doc(cdt, cdn); + f.custom = 1; + }, +}); + +xhiveframework.customize_form.save_customization = function (frm) { + if (frm.doc.doc_type) { + return frm.call({ + doc: frm.doc, + freeze: true, + freeze_message: __("Saving Customization..."), + btn: frm.page.btn_primary, + method: "save_customization", + callback: function (r) { + if (!r.exc) { + xhiveframework.customize_form.clear_locals_and_refresh(frm); + frm.script_manager.trigger("doc_type"); + } + }, + }); + } +}; + +xhiveframework.customize_form.update_fields_from_form_builder = function (frm) { + let form_builder = xhiveframework.form_builder; + if (form_builder?.store) { + let fields = form_builder.store.update_fields(); + + // if fields is a string, it means there is an error + if (typeof fields === "string") { + xhiveframework.throw(fields); + } + frm.refresh_fields(); + } +}; + +xhiveframework.customize_form.set_primary_action = function (frm) { + frm.page.set_primary_action(__("Update"), () => { + this.update_fields_from_form_builder(frm); + this.save_customization(frm); + }); +}; + +xhiveframework.customize_form.confirm = function (msg, frm) { + if (!frm.doc.doc_type) return; + + var d = new xhiveframework.ui.Dialog({ + title: "Reset To Defaults", + fields: [ + { + fieldtype: "HTML", + options: __("All customizations will be removed. Please confirm."), + }, + ], + primary_action: function () { + return frm.call({ + doc: frm.doc, + method: "reset_to_defaults", + callback: function (r) { + if (r.exc) { + xhiveframework.msgprint(r.exc); + } else { + d.hide(); + xhiveframework.show_alert({ + message: __("Customizations Reset"), + indicator: "green", + }); + xhiveframework.customize_form.clear_locals_and_refresh(frm); + } + }, + }); + }, + }); + + xhiveframework.customize_form.confirm.dialog = d; + d.show(); +}; + +xhiveframework.customize_form.clear_locals_and_refresh = function (frm) { + delete frm.doc.__unsaved; + // clear doctype from locals + xhiveframework.model.clear_doc("DocType", frm.doc.doc_type); + delete xhiveframework.meta.docfield_copy[frm.doc.doc_type]; + frm.refresh(); +}; + +function render_form_builder(frm) { + if (xhiveframework.form_builder && xhiveframework.form_builder.doctype === frm.doc.doc_type) { + xhiveframework.form_builder.setup_page_actions(); + xhiveframework.form_builder.store.fetch(); + return; + } + + if (xhiveframework.form_builder) { + xhiveframework.form_builder.wrapper = $(frm.fields_dict["form_builder"].wrapper); + xhiveframework.form_builder.frm = frm; + xhiveframework.form_builder.doctype = frm.doc.doc_type; + xhiveframework.form_builder.customize = true; + xhiveframework.form_builder.init(true); + xhiveframework.form_builder.store.fetch(); + } else { + xhiveframework.require("form_builder.bundle.js").then(() => { + xhiveframework.form_builder = new xhiveframework.ui.FormBuilder({ + wrapper: $(frm.fields_dict["form_builder"].wrapper), + frm: frm, + doctype: frm.doc.doc_type, + customize: true, + }); + }); + } +} + +extend_cscript(cur_frm.cscript, new xhiveframework.model.DocTypeController({ frm: cur_frm })); diff --git a/xhiveframework/custom/doctype/customize_form/customize_form.json b/xhiveframework/custom/doctype/customize_form/customize_form.json new file mode 100644 index 0000000..b3bb3ee --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/customize_form.json @@ -0,0 +1,426 @@ +{ + "actions": [], + "autoname": "DL.####", + "creation": "2013-01-29 17:55:08", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "details_tab", + "doc_type", + "properties", + "label", + "search_fields", + "column_break_5", + "istable", + "is_calendar_and_gantt", + "editable_grid", + "quick_entry", + "track_changes", + "track_views", + "allow_auto_repeat", + "allow_import", + "queue_in_background", + "naming_section", + "naming_rule", + "autoname", + "form_settings_section", + "image_field", + "max_attachments", + "column_break_21", + "allow_copy", + "make_attachments_public", + "view_settings_section", + "title_field", + "show_title_field_in_link", + "translated_doctype", + "default_print_format", + "default_view", + "force_re_route_to_default_view", + "column_break_29", + "show_preview_popup", + "email_settings_section", + "default_email_template", + "column_break_26", + "email_append_to", + "sender_field", + "sender_name_field", + "subject_field", + "section_break_8", + "sort_field", + "column_break_10", + "sort_order", + "document_actions_section", + "actions", + "document_links_section", + "links", + "document_states_section", + "states", + "fields_section_break", + "fields", + "form_tab", + "form_builder" + ], + "fields": [ + { + "fieldname": "naming_rule", + "fieldtype": "Select", + "label": "Naming Rule", + "length": 40, + "options": "\nSet by user\nBy fieldname\nBy \"Naming Series\" field\nExpression\nExpression (old style)\nRandom\nBy script" + }, + { + "fieldname": "doc_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Enter Form Type", + "options": "DocType" + }, + { + "depends_on": "doc_type", + "fieldname": "properties", + "fieldtype": "Section Break" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "label": "Change Label (via Custom Translation)" + }, + { + "fieldname": "default_print_format", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Print Format", + "options": "Print Format" + }, + { + "fieldname": "max_attachments", + "fieldtype": "Int", + "label": "Max Attachments" + }, + { + "default": "0", + "fieldname": "allow_copy", + "fieldtype": "Check", + "label": "Hide Copy" + }, + { + "default": "0", + "fieldname": "istable", + "fieldtype": "Check", + "label": "Is Table", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "istable", + "fieldname": "editable_grid", + "fieldtype": "Check", + "label": "Editable Grid" + }, + { + "default": "1", + "fieldname": "quick_entry", + "fieldtype": "Check", + "label": "Quick Entry" + }, + { + "default": "0", + "fieldname": "track_changes", + "fieldtype": "Check", + "label": "Track Changes" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "description": "Use this fieldname to generate title", + "fieldname": "title_field", + "fieldtype": "Data", + "label": "Title Field" + }, + { + "description": "Must be of type \"Attach Image\"", + "fieldname": "image_field", + "fieldtype": "Data", + "label": "Image Field" + }, + { + "description": "Fields separated by comma (,) will be included in the \"Search By\" list of Search dialog box", + "fieldname": "search_fields", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Search Fields" + }, + { + "collapsible": 1, + "depends_on": "doc_type", + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "List Settings" + }, + { + "fieldname": "sort_field", + "fieldtype": "Select", + "label": "Sort Field" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "sort_order", + "fieldtype": "Select", + "label": "Sort Order", + "options": "ASC\nDESC" + }, + { + "collapsible": 1, + "depends_on": "doc_type", + "fieldname": "fields_section_break", + "fieldtype": "Section Break", + "label": "Fields" + }, + { + "allow_bulk_edit": 1, + "fieldname": "fields", + "fieldtype": "Table", + "label": "Fields", + "options": "Customize Form Field", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "track_views", + "fieldtype": "Check", + "label": "Track Views" + }, + { + "default": "0", + "fieldname": "allow_auto_repeat", + "fieldtype": "Check", + "label": "Allow Auto Repeat" + }, + { + "default": "0", + "fieldname": "allow_import", + "fieldtype": "Check", + "label": "Allow Import (via Data Import Tool)" + }, + { + "depends_on": "email_append_to", + "fieldname": "subject_field", + "fieldtype": "Data", + "label": "Subject Field" + }, + { + "depends_on": "email_append_to", + "fieldname": "sender_field", + "fieldtype": "Data", + "label": "Sender Email Field", + "mandatory_depends_on": "email_append_to" + }, + { + "default": "0", + "fieldname": "email_append_to", + "fieldtype": "Check", + "label": "Allow document creation via Email" + }, + { + "default": "0", + "fieldname": "show_preview_popup", + "fieldtype": "Check", + "label": "Show Preview Popup" + }, + { + "collapsible": 1, + "depends_on": "doc_type", + "fieldname": "view_settings_section", + "fieldtype": "Section Break", + "label": "View Settings" + }, + { + "fieldname": "column_break_29", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "email_append_to", + "depends_on": "doc_type", + "fieldname": "email_settings_section", + "fieldtype": "Section Break", + "label": "Email Settings" + }, + { + "collapsible": 1, + "collapsible_depends_on": "links", + "depends_on": "doc_type", + "fieldname": "document_links_section", + "fieldtype": "Section Break", + "label": "Document Links" + }, + { + "fieldname": "links", + "fieldtype": "Table", + "label": "Links", + "options": "DocType Link" + }, + { + "collapsible": 1, + "collapsible_depends_on": "actions", + "depends_on": "doc_type", + "fieldname": "document_actions_section", + "fieldtype": "Section Break", + "label": "Document Actions" + }, + { + "fieldname": "actions", + "fieldtype": "Table", + "label": "Actions", + "options": "DocType Action" + }, + { + "fieldname": "default_email_template", + "fieldtype": "Link", + "label": "Default Email Template", + "options": "Email Template" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "depends_on": "doc_type", + "fieldname": "naming_section", + "fieldtype": "Section Break", + "label": "Naming" + }, + { + "description": "Naming Options:\n
    1. field:[fieldname] - By Field
    2. naming_series: - By Naming Series (field called naming_series must be present)
    3. Prompt - Prompt user for a name
    4. [series] - Series by prefix (separated by a dot); for example PRE.#####
    5. \n
    6. format:EXAMPLE-{MM}morewords{fieldname1}-{fieldname2}-{#####} - Replace all braced words (fieldnames, date words (DD, MM, YY), series) with their value. Outside braces, any characters can be used.
    ", + "fieldname": "autoname", + "fieldtype": "Data", + "label": "Auto Name" + }, + { + "collapsible": 1, + "collapsible_depends_on": "states", + "depends_on": "doc_type", + "fieldname": "document_states_section", + "fieldtype": "Section Break", + "label": "Document States" + }, + { + "fieldname": "states", + "fieldtype": "Table", + "label": "States", + "options": "DocType State" + }, + { + "default": "0", + "fieldname": "show_title_field_in_link", + "fieldtype": "Check", + "label": "Show Title in Link Fields" + }, + { + "default": "0", + "fieldname": "translated_doctype", + "fieldtype": "Check", + "label": "Translate Link Fields" + }, + { + "collapsible": 1, + "fieldname": "form_settings_section", + "fieldtype": "Section Break", + "label": "Form Settings" + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "make_attachments_public", + "fieldtype": "Check", + "label": "Make Attachments Public by Default" + }, + { + "default": "0", + "description": "Enabling this will submit documents in background", + "fieldname": "queue_in_background", + "fieldtype": "Check", + "label": "Queue in Background (BETA)" + }, + { + "fieldname": "default_view", + "fieldtype": "Select", + "label": "Default View" + }, + { + "default": "0", + "depends_on": "default_view", + "fieldname": "force_re_route_to_default_view", + "fieldtype": "Check", + "label": "Force Re-route to Default View" + }, + { + "default": "0", + "description": "Enables Calendar and Gantt views.", + "fieldname": "is_calendar_and_gantt", + "fieldtype": "Check", + "label": "Is Calendar and Gantt" + }, + { + "fieldname": "form_builder", + "fieldtype": "HTML", + "label": "Form Builder" + }, + { + "fieldname": "form_tab", + "fieldtype": "Tab Break", + "label": "Form" + }, + { + "fieldname": "details_tab", + "fieldtype": "Tab Break", + "label": "Details" + }, + { + "depends_on": "email_append_to", + "fieldname": "sender_name_field", + "fieldtype": "Data", + "label": "Sender Name Field" + } + ], + "hide_toolbar": 1, + "icon": "fa fa-glass", + "idx": 1, + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2023-12-01 18:18:23.086134", + "modified_by": "Administrator", + "module": "Custom", + "name": "Customize Form", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "doc_type", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/custom/doctype/customize_form/customize_form.py b/xhiveframework/custom/doctype/customize_form/customize_form.py new file mode 100644 index 0000000..3e48965 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/customize_form.py @@ -0,0 +1,794 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE + +""" + Customize Form is a Single DocType used to mask the Property Setter + Thus providing a better UI from user perspective +""" +import json + +import xhiveframework +import xhiveframework.translate +from xhiveframework import _ +from xhiveframework.core.doctype.doctype.doctype import ( + check_email_append_to, + validate_autoincrement_autoname, + validate_fields_for_doctype, + validate_series, +) +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_field +from xhiveframework.custom.doctype.property_setter.property_setter import delete_property_setter +from xhiveframework.model import core_doctypes_list, no_value_fields +from xhiveframework.model.docfield import supports_translation +from xhiveframework.model.document import Document +from xhiveframework.utils import cint + + +class CustomizeForm(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.doctype_action.doctype_action import DocTypeAction + from xhiveframework.core.doctype.doctype_link.doctype_link import DocTypeLink + from xhiveframework.core.doctype.doctype_state.doctype_state import DocTypeState + from xhiveframework.custom.doctype.customize_form_field.customize_form_field import CustomizeFormField + from xhiveframework.types import DF + + actions: DF.Table[DocTypeAction] + allow_auto_repeat: DF.Check + allow_copy: DF.Check + allow_import: DF.Check + autoname: DF.Data | None + default_email_template: DF.Link | None + default_print_format: DF.Link | None + default_view: DF.Literal[None] + doc_type: DF.Link | None + editable_grid: DF.Check + email_append_to: DF.Check + fields: DF.Table[CustomizeFormField] + force_re_route_to_default_view: DF.Check + image_field: DF.Data | None + is_calendar_and_gantt: DF.Check + istable: DF.Check + label: DF.Data | None + links: DF.Table[DocTypeLink] + make_attachments_public: DF.Check + max_attachments: DF.Int + naming_rule: DF.Literal[ + "", + "Set by user", + "By fieldname", + 'By "Naming Series" field', + "Expression", + "Expression (old style)", + "Random", + "By script", + ] + queue_in_background: DF.Check + quick_entry: DF.Check + search_fields: DF.Data | None + sender_field: DF.Data | None + sender_name_field: DF.Data | None + show_preview_popup: DF.Check + show_title_field_in_link: DF.Check + sort_field: DF.Literal[None] + sort_order: DF.Literal["ASC", "DESC"] + states: DF.Table[DocTypeState] + subject_field: DF.Data | None + title_field: DF.Data | None + track_changes: DF.Check + track_views: DF.Check + translated_doctype: DF.Check + # end: auto-generated types + + def on_update(self): + xhiveframework.db.delete("Singles", {"doctype": "Customize Form"}) + xhiveframework.db.delete("Customize Form Field") + + @xhiveframework.whitelist() + def fetch_to_customize(self): + self.clear_existing_doc() + if not self.doc_type: + return + + meta = xhiveframework.get_meta(self.doc_type, cached=False) + + self.validate_doctype(meta) + + # load the meta properties on the customize (self) object + self.load_properties(meta) + + # load custom translation + translation = self.get_name_translation() + self.label = translation.translated_text if translation else "" + + self.create_auto_repeat_custom_field_if_required(meta) + + # NOTE doc (self) is sent to clientside by run_method + + def validate_doctype(self, meta): + """ + Check if the doctype is allowed to be customized. + """ + if self.doc_type in core_doctypes_list: + xhiveframework.throw(_("Core DocTypes cannot be customized.")) + + if meta.issingle: + xhiveframework.throw(_("Single DocTypes cannot be customized.")) + + if meta.custom: + xhiveframework.throw(_("Only standard DocTypes are allowed to be customized from Customize Form.")) + + def load_properties(self, meta): + """ + Load the customize object (this) with the metadata properties + """ + # doctype properties + for prop in doctype_properties: + self.set(prop, meta.get(prop)) + + for d in meta.get("fields"): + new_d = { + "fieldname": d.fieldname, + "is_custom_field": d.get("is_custom_field"), + "is_system_generated": d.get("is_system_generated"), + "name": d.name, + } + for prop in docfield_properties: + new_d[prop] = d.get(prop) + self.append("fields", new_d) + + for fieldname in ("links", "actions", "states"): + for d in meta.get(fieldname): + self.append(fieldname, d) + + def create_auto_repeat_custom_field_if_required(self, meta): + """ + Create auto repeat custom field if it's not already present + """ + if self.allow_auto_repeat: + all_fields = [df.fieldname for df in meta.fields] + + if "auto_repeat" in all_fields: + return + + insert_after = self.fields[len(self.fields) - 1].fieldname + create_custom_field( + self.doc_type, + dict( + fieldname="auto_repeat", + label="Auto Repeat", + fieldtype="Link", + options="Auto Repeat", + insert_after=insert_after, + read_only=1, + no_copy=1, + print_hide=1, + ), + ) + + def get_name_translation(self): + """Get translation object if exists of current doctype name in the default language""" + return xhiveframework.get_value( + "Translation", + {"source_text": self.doc_type, "language": xhiveframework.local.lang or "en"}, + ["name", "translated_text"], + as_dict=True, + ) + + def set_name_translation(self): + """Create, update custom translation for this doctype""" + current = self.get_name_translation() + if not self.label: + if current: + # clear translation + xhiveframework.delete_doc("Translation", current.name) + return + + if not current: + xhiveframework.get_doc( + { + "doctype": "Translation", + "source_text": self.doc_type, + "translated_text": self.label, + "language_code": xhiveframework.local.lang or "en", + } + ).insert() + return + + if self.label != current.translated_text: + xhiveframework.db.set_value("Translation", current.name, "translated_text", self.label) + xhiveframework.translate.clear_cache() + + def clear_existing_doc(self): + doc_type = self.doc_type + + for fieldname in self.meta.get_valid_columns(): + self.set(fieldname, None) + + for df in self.meta.get_table_fields(): + self.set(df.fieldname, []) + + self.doc_type = doc_type + self.name = "Customize Form" + + @xhiveframework.whitelist() + def save_customization(self): + if not self.doc_type: + return + + validate_series(self, self.autoname, self.doc_type) + validate_autoincrement_autoname(self) + self.flags.update_db = False + self.flags.rebuild_doctype_for_global_search = False + self.set_property_setters() + self.update_custom_fields() + self.set_name_translation() + validate_fields_for_doctype(self.doc_type) + check_email_append_to(self) + + if self.flags.update_db: + try: + xhiveframework.db.updatedb(self.doc_type) + except Exception as e: + if xhiveframework.db.is_db_table_size_limit(e): + xhiveframework.throw( + _("You have hit the row size limit on database table: {0}").format( + "" + "Maximum Number of Fields in a Form" + ), + title=_("Database Table Row Size Limit"), + ) + raise + + if not hasattr(self, "hide_success") or not self.hide_success: + xhiveframework.msgprint(_("{0} updated").format(_(self.doc_type)), alert=True) + xhiveframework.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() + + if self.flags.rebuild_doctype_for_global_search: + xhiveframework.enqueue( + "xhiveframework.utils.global_search.rebuild_for_doctype", + doctype=self.doc_type, + enqueue_after_commit=True, + ) + + def set_property_setters(self): + meta = xhiveframework.get_meta(self.doc_type) + + # doctype + self.set_property_setters_for_doctype(meta) + + # docfield + for df in self.get("fields"): + meta_df = meta.get("fields", {"fieldname": df.fieldname}) + if not meta_df or not is_standard_or_system_generated_field(meta_df[0]): + continue + + self.set_property_setters_for_docfield(meta, df, meta_df) + + # action and links + self.set_property_setters_for_actions_and_links(meta) + + def set_property_setter_for_field_order(self, meta): + new_order = [df.fieldname for df in self.fields] + existing_order = getattr(meta, "field_order", None) + default_order = [ + fieldname for fieldname, df in meta._fields.items() if not getattr(df, "is_custom_field", False) + ] + + if new_order == default_order: + if existing_order: + delete_property_setter(self.doc_type, "field_order") + + return + + if existing_order and new_order == json.loads(existing_order): + return + + xhiveframework.make_property_setter( + { + "doctype": self.doc_type, + "doctype_or_field": "DocType", + "property": "field_order", + "value": json.dumps(new_order), + }, + is_system_generated=False, + ) + + def set_property_setters_for_doctype(self, meta): + for prop, prop_type in doctype_properties.items(): + if self.get(prop) != meta.get(prop): + self.make_property_setter(prop, self.get(prop), prop_type) + + self.set_property_setter_for_field_order(meta) + + def set_property_setters_for_docfield(self, meta, df, meta_df): + for prop, prop_type in docfield_properties.items(): + if prop != "idx" and (df.get(prop) or "") != (meta_df[0].get(prop) or ""): + if not self.allow_property_change(prop, meta_df, df): + continue + + self.make_property_setter(prop, df.get(prop), prop_type, fieldname=df.fieldname) + + def allow_property_change(self, prop, meta_df, df): + if prop == "fieldtype": + self.validate_fieldtype_change(df, meta_df[0].get(prop), df.get(prop)) + + elif prop == "length": + old_value_length = cint(meta_df[0].get(prop)) + new_value_length = cint(df.get(prop)) + + if new_value_length and (old_value_length > new_value_length): + self.check_length_for_fieldtypes.append({"df": df, "old_value": meta_df[0].get(prop)}) + self.validate_fieldtype_length() + else: + self.flags.update_db = True + + elif prop == "allow_on_submit" and df.get(prop): + if not xhiveframework.db.get_value( + "DocField", {"parent": self.doc_type, "fieldname": df.fieldname}, "allow_on_submit" + ): + xhiveframework.msgprint( + _("Row {0}: Not allowed to enable Allow on Submit for standard fields").format(df.idx) + ) + return False + + elif prop == "reqd" and ( + ( + xhiveframework.db.get_value("DocField", {"parent": self.doc_type, "fieldname": df.fieldname}, "reqd") + == 1 + ) + and (df.get(prop) == 0) + ): + xhiveframework.msgprint(_("Row {0}: Not allowed to disable Mandatory for standard fields").format(df.idx)) + return False + + elif ( + prop == "in_list_view" + and df.get(prop) + and df.fieldtype != "Attach Image" + and df.fieldtype in no_value_fields + ): + xhiveframework.msgprint( + _("'In List View' not allowed for type {0} in row {1}").format(df.fieldtype, df.idx) + ) + return False + + elif ( + prop == "precision" + and cint(df.get("precision")) > 6 + and cint(df.get("precision")) > cint(meta_df[0].get("precision")) + ): + self.flags.update_db = True + + elif prop == "unique": + self.flags.update_db = True + + elif ( + prop == "read_only" + and cint(df.get("read_only")) == 0 + and xhiveframework.db.get_value( + "DocField", {"parent": self.doc_type, "fieldname": df.fieldname}, "read_only" + ) + == 1 + ): + # if docfield has read_only checked and user is trying to make it editable, don't allow it + xhiveframework.msgprint(_("You cannot unset 'Read Only' for field {0}").format(df.label)) + return False + + elif prop == "options" and df.get("fieldtype") not in ALLOWED_OPTIONS_CHANGE: + xhiveframework.msgprint(_("You can't set 'Options' for field {0}").format(df.label)) + return False + + elif prop == "translatable" and not supports_translation(df.get("fieldtype")): + xhiveframework.msgprint(_("You can't set 'Translatable' for field {0}").format(df.label)) + return False + + elif prop == "in_global_search" and df.in_global_search != meta_df[0].get("in_global_search"): + self.flags.rebuild_doctype_for_global_search = True + + return True + + def set_property_setters_for_actions_and_links(self, meta): + """ + Apply property setters or create custom records for DocType Action and DocType Link + """ + for doctype, fieldname, field_map in ( + ("DocType Link", "links", doctype_link_properties), + ("DocType Action", "actions", doctype_action_properties), + ("DocType State", "states", doctype_state_properties), + ): + has_custom = False + items = [] + for i, d in enumerate(self.get(fieldname) or []): + d.idx = i + if xhiveframework.db.exists(doctype, d.name) and not d.custom: + # check property and apply property setter + original = xhiveframework.get_doc(doctype, d.name) + for prop, prop_type in field_map.items(): + if d.get(prop) != original.get(prop): + self.make_property_setter( + prop, d.get(prop), prop_type, apply_on=doctype, row_name=d.name + ) + items.append(d.name) + else: + # custom - just insert/update + d.parent = self.doc_type + d.custom = 1 + d.save(ignore_permissions=True) + has_custom = True + items.append(d.name) + + self.update_order_property_setter(has_custom, fieldname) + self.clear_removed_items(doctype, items) + + def update_order_property_setter(self, has_custom, fieldname): + """ + We need to maintain the order of the link/actions if the user has shuffled them. + So we create a new property (ex `links_order`) to keep a list of items. + """ + property_name = f"{fieldname}_order" + if has_custom: + # save the order of the actions and links + self.make_property_setter( + property_name, json.dumps([d.name for d in self.get(fieldname)]), "Small Text" + ) + else: + xhiveframework.db.delete("Property Setter", dict(property=property_name, doc_type=self.doc_type)) + + def clear_removed_items(self, doctype, items): + """ + Clear rows that do not appear in `items`. These have been removed by the user. + """ + if items: + xhiveframework.db.delete(doctype, dict(parent=self.doc_type, custom=1, name=("not in", items))) + else: + xhiveframework.db.delete(doctype, dict(parent=self.doc_type, custom=1)) + + def update_custom_fields(self): + for i, df in enumerate(self.get("fields")): + if is_standard_or_system_generated_field(df): + continue + + if not xhiveframework.db.exists("Custom Field", {"dt": self.doc_type, "fieldname": df.fieldname}): + self.add_custom_field(df, i) + self.flags.update_db = True + else: + self.update_in_custom_field(df, i) + + self.delete_custom_fields() + + def add_custom_field(self, df, i): + d = xhiveframework.new_doc("Custom Field") + + d.dt = self.doc_type + + for prop in docfield_properties: + d.set(prop, df.get(prop)) + + if i != 0: + d.insert_after = self.fields[i - 1].fieldname + d.idx = i + + d.insert() + df.fieldname = d.fieldname + + if df.get("in_global_search"): + self.flags.rebuild_doctype_for_global_search = True + + def update_in_custom_field(self, df, i): + meta = xhiveframework.get_meta(self.doc_type) + meta_df = meta.get("fields", {"fieldname": df.fieldname}) + if not meta_df or is_standard_or_system_generated_field(meta_df[0]): + # not a custom field + return + + custom_field = xhiveframework.get_doc("Custom Field", meta_df[0].name) + changed = False + for prop in docfield_properties: + if df.get(prop) != custom_field.get(prop): + if prop == "fieldtype": + self.validate_fieldtype_change(df, meta_df[0].get(prop), df.get(prop)) + if prop == "in_global_search": + self.flags.rebuild_doctype_for_global_search = True + + custom_field.set(prop, df.get(prop)) + changed = True + + # check and update `insert_after` property + if i != 0: + insert_after = self.fields[i - 1].fieldname + if custom_field.insert_after != insert_after: + custom_field.insert_after = insert_after + custom_field.idx = i + changed = True + + if changed: + custom_field.db_update() + self.flags.update_db = True + # custom_field.save() + + def delete_custom_fields(self): + meta = xhiveframework.get_meta(self.doc_type) + fields_to_remove = {df.fieldname for df in meta.get("fields")} - { + df.fieldname for df in self.get("fields") + } + for fieldname in fields_to_remove: + df = meta.get("fields", {"fieldname": fieldname})[0] + if not is_standard_or_system_generated_field(df): + xhiveframework.delete_doc("Custom Field", df.name) + + def make_property_setter(self, prop, value, property_type, fieldname=None, apply_on=None, row_name=None): + delete_property_setter(self.doc_type, prop, fieldname, row_name) + + property_value = self.get_existing_property_value(prop, fieldname) + + if property_value == value: + return + + if not apply_on: + apply_on = "DocField" if fieldname else "DocType" + + # create a new property setter + xhiveframework.make_property_setter( + { + "doctype": self.doc_type, + "doctype_or_field": apply_on, + "fieldname": fieldname, + "row_name": row_name, + "property": prop, + "value": value, + "property_type": property_type, + }, + is_system_generated=False, + ) + + def get_existing_property_value(self, property_name, fieldname=None): + # check if there is any need to make property setter! + if fieldname: + property_value = xhiveframework.db.get_value( + "DocField", {"parent": self.doc_type, "fieldname": fieldname}, property_name + ) + else: + if xhiveframework.db.has_column("DocType", property_name): + property_value = xhiveframework.db.get_value("DocType", self.doc_type, property_name) + else: + property_value = None + + return property_value + + def validate_fieldtype_change(self, df, old_value, new_value): + if df.is_virtual: + return + + allowed = self.allow_fieldtype_change(old_value, new_value) + if allowed: + old_value_length = cint(xhiveframework.db.type_map.get(old_value)[1]) + new_value_length = cint(xhiveframework.db.type_map.get(new_value)[1]) + + # Ignore fieldtype check validation if new field type has unspecified maxlength + # Changes like DATA to TEXT, where new_value_lenth equals 0 will not be validated + if new_value_length and (old_value_length > new_value_length): + self.check_length_for_fieldtypes.append({"df": df, "old_value": old_value}) + self.validate_fieldtype_length() + else: + self.flags.update_db = True + + else: + xhiveframework.throw( + _("Fieldtype cannot be changed from {0} to {1} in row {2}").format( + old_value, new_value, df.idx + ) + ) + + def validate_fieldtype_length(self): + for field in self.check_length_for_fieldtypes: + df = field.get("df") + max_length = cint(xhiveframework.db.type_map.get(df.fieldtype)[1]) + fieldname = df.fieldname + docs = xhiveframework.db.sql( + f""" + SELECT name, {fieldname}, LENGTH({fieldname}) AS len + FROM `tab{self.doc_type}` + WHERE LENGTH({fieldname}) > {max_length} + """, + as_dict=True, + ) + label = df.label + links_str = ", ".join(xhiveframework.utils.get_link_to_form(self.doc_type, doc.name) for doc in docs) + + if docs: + xhiveframework.throw( + _( + "Value for field {0} is too long in {1}. Length should be lesser than {2} characters" + ).format(xhiveframework.bold(label), links_str, xhiveframework.bold(max_length)), + title=_("Data Too Long"), + is_minimizable=len(docs) > 1, + ) + + self.flags.update_db = True + + @xhiveframework.whitelist() + def reset_to_defaults(self): + if not self.doc_type: + return + + reset_customization(self.doc_type) + self.fetch_to_customize() + + @xhiveframework.whitelist() + def reset_layout(self): + if not self.doc_type: + return + + property_setters = xhiveframework.get_all( + "Property Setter", + filters={"doc_type": self.doc_type, "property": ("in", ("field_order", "insert_after"))}, + pluck="name", + ) + + if not property_setters: + return + + xhiveframework.db.delete("Property Setter", {"name": ("in", property_setters)}) + xhiveframework.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() + + @classmethod + def allow_fieldtype_change(self, old_type: str, new_type: str) -> bool: + """allow type change, if both old_type and new_type are in same field group. + field groups are defined in ALLOWED_FIELDTYPE_CHANGE variables. + """ + + def in_field_group(group): + return (old_type in group) and (new_type in group) + + return any(map(in_field_group, ALLOWED_FIELDTYPE_CHANGE)) + + +def reset_customization(doctype): + setters = xhiveframework.get_all( + "Property Setter", + filters={ + "doc_type": doctype, + "field_name": ["!=", "naming_series"], + "property": ["!=", "options"], + "is_system_generated": False, + }, + pluck="name", + ) + + for setter in setters: + xhiveframework.delete_doc("Property Setter", setter) + + custom_fields = xhiveframework.get_all( + "Custom Field", filters={"dt": doctype, "is_system_generated": False}, pluck="name" + ) + + for field in custom_fields: + xhiveframework.delete_doc("Custom Field", field) + + xhiveframework.clear_cache(doctype=doctype) + + +def is_standard_or_system_generated_field(df): + return not df.get("is_custom_field") or df.get("is_system_generated") + + +doctype_properties = { + "search_fields": "Data", + "title_field": "Data", + "image_field": "Data", + "sort_field": "Data", + "sort_order": "Data", + "default_print_format": "Data", + "allow_copy": "Check", + "istable": "Check", + "quick_entry": "Check", + "queue_in_background": "Check", + "editable_grid": "Check", + "max_attachments": "Int", + "make_attachments_public": "Check", + "track_changes": "Check", + "track_views": "Check", + "allow_auto_repeat": "Check", + "allow_import": "Check", + "show_preview_popup": "Check", + "default_email_template": "Data", + "email_append_to": "Check", + "subject_field": "Data", + "sender_field": "Data", + "naming_rule": "Data", + "autoname": "Data", + "show_title_field_in_link": "Check", + "is_calendar_and_gantt": "Check", + "default_view": "Select", + "force_re_route_to_default_view": "Check", + "translated_doctype": "Check", +} + +docfield_properties = { + "idx": "Int", + "label": "Data", + "fieldtype": "Select", + "options": "Text", + "sort_options": "Check", + "fetch_from": "Small Text", + "fetch_if_empty": "Check", + "show_dashboard": "Check", + "permlevel": "Int", + "width": "Data", + "print_width": "Data", + "non_negative": "Check", + "reqd": "Check", + "unique": "Check", + "ignore_user_permissions": "Check", + "in_list_view": "Check", + "in_standard_filter": "Check", + "in_global_search": "Check", + "in_preview": "Check", + "bold": "Check", + "no_copy": "Check", + "ignore_xss_filter": "Check", + "hidden": "Check", + "collapsible": "Check", + "collapsible_depends_on": "Data", + "print_hide": "Check", + "print_hide_if_no_value": "Check", + "report_hide": "Check", + "allow_on_submit": "Check", + "translatable": "Check", + "mandatory_depends_on": "Data", + "read_only_depends_on": "Data", + "depends_on": "Data", + "description": "Text", + "default": "Text", + "precision": "Select", + "read_only": "Check", + "length": "Int", + "columns": "Int", + "remember_last_selected_value": "Check", + "allow_bulk_edit": "Check", + "auto_repeat": "Link", + "allow_in_quick_entry": "Check", + "hide_border": "Check", + "hide_days": "Check", + "hide_seconds": "Check", + "is_virtual": "Check", +} + +doctype_link_properties = { + "link_doctype": "Link", + "link_fieldname": "Data", + "group": "Data", + "hidden": "Check", +} + +doctype_action_properties = { + "label": "Link", + "action_type": "Select", + "action": "Small Text", + "group": "Data", + "hidden": "Check", +} + +doctype_state_properties = {"title": "Data", "color": "Select"} + + +ALLOWED_FIELDTYPE_CHANGE = ( + ("Currency", "Float", "Percent"), + ("Small Text", "Data"), + ("Text", "Data"), + ("Text", "Text Editor", "Code", "Signature", "HTML Editor"), + ("Data", "Select"), + ("Text", "Small Text", "Long Text"), + ("Text", "Data", "Barcode"), + ("Code", "Geolocation"), + ("Table", "Table MultiSelect"), +) + +ALLOWED_OPTIONS_CHANGE = ("Read Only", "HTML", "Data") diff --git a/xhiveframework/custom/doctype/customize_form/test_customize_form.py b/xhiveframework/custom/doctype/customize_form/test_customize_form.py new file mode 100644 index 0000000..29c18d8 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form/test_customize_form.py @@ -0,0 +1,427 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.core.doctype.doctype.doctype import InvalidFieldNameError +from xhiveframework.core.doctype.doctype.test_doctype import new_doctype +from xhiveframework.test_runner import make_test_records_for_doctype +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["Custom Field", "Property Setter"] + + +class TestCustomizeForm(XhiveFrameworkTestCase): + def insert_custom_field(self): + xhiveframework.delete_doc_if_exists("Custom Field", "Event-custom_test_field") + self.field = xhiveframework.get_doc( + { + "doctype": "Custom Field", + "fieldname": "custom_test_field", + "dt": "Event", + "label": "Test Custom Field", + "description": "A Custom Field for Testing", + "fieldtype": "Select", + "in_list_view": 1, + "options": "\nCustom 1\nCustom 2\nCustom 3", + "default": "Custom 3", + "insert_after": xhiveframework.get_meta("Event").fields[-1].fieldname, + } + ).insert() + + def setUp(self): + self.insert_custom_field() + xhiveframework.db.delete("Property Setter", dict(doc_type="Event")) + xhiveframework.db.commit() + xhiveframework.clear_cache(doctype="Event") + + def tearDown(self): + xhiveframework.delete_doc("Custom Field", self.field.name) + xhiveframework.db.commit() + xhiveframework.clear_cache(doctype="Event") + + def get_customize_form(self, doctype=None): + d = xhiveframework.get_doc("Customize Form") + if doctype: + d.doc_type = doctype + d.run_method("fetch_to_customize") + return d + + def test_fetch_to_customize(self): + d = self.get_customize_form() + self.assertEqual(d.doc_type, None) + self.assertEqual(len(d.get("fields")), 0) + + d = self.get_customize_form("Event") + self.assertEqual(d.doc_type, "Event") + self.assertEqual(len(d.get("fields")), 38) + + d = self.get_customize_form("Event") + self.assertEqual(d.doc_type, "Event") + + self.assertEqual(len(d.get("fields")), len(xhiveframework.get_doc("DocType", d.doc_type).fields) + 1) + self.assertEqual(d.get("fields")[-1].fieldname, self.field.fieldname) + self.assertEqual(d.get("fields", {"fieldname": "event_type"})[0].in_list_view, 1) + + return d + + def test_save_customization_property(self): + d = self.get_customize_form("Event") + self.assertEqual( + xhiveframework.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), + None, + ) + + d.allow_copy = 1 + d.run_method("save_customization") + self.assertEqual( + xhiveframework.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), + "1", + ) + + d.allow_copy = 0 + d.run_method("save_customization") + self.assertEqual( + xhiveframework.db.get_value("Property Setter", {"doc_type": "Event", "property": "allow_copy"}, "value"), + None, + ) + + def test_save_customization_field_property(self): + d = self.get_customize_form("Event") + self.assertEqual( + xhiveframework.db.get_value( + "Property Setter", + {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, + "value", + ), + None, + ) + + repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0] + repeat_this_event_field.reqd = 1 + d.run_method("save_customization") + self.assertEqual( + xhiveframework.db.get_value( + "Property Setter", + {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, + "value", + ), + "1", + ) + + repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0] + repeat_this_event_field.reqd = 0 + d.run_method("save_customization") + self.assertEqual( + xhiveframework.db.get_value( + "Property Setter", + {"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, + "value", + ), + None, + ) + + def test_save_customization_custom_field_property(self): + d = self.get_customize_form("Event") + self.assertEqual(xhiveframework.db.get_value("Custom Field", self.field.name, "reqd"), 0) + + custom_field = d.get("fields", {"fieldname": self.field.fieldname})[0] + custom_field.reqd = 1 + custom_field.no_copy = 1 + d.run_method("save_customization") + self.assertEqual(xhiveframework.db.get_value("Custom Field", self.field.name, "reqd"), 1) + self.assertEqual(xhiveframework.db.get_value("Custom Field", self.field.name, "no_copy"), 1) + + custom_field = d.get("fields", {"is_custom_field": True})[0] + custom_field.reqd = 0 + custom_field.no_copy = 0 + d.run_method("save_customization") + self.assertEqual(xhiveframework.db.get_value("Custom Field", self.field.name, "reqd"), 0) + self.assertEqual(xhiveframework.db.get_value("Custom Field", self.field.name, "no_copy"), 0) + + def test_save_customization_new_field(self): + d = self.get_customize_form("Event") + last_fieldname = d.fields[-1].fieldname + d.append( + "fields", + { + "label": "Test Add Custom Field Via Customize Form", + "fieldtype": "Data", + "is_custom_field": 1, + }, + ) + d.run_method("save_customization") + + custom_field_name = "Event-custom_test_add_custom_field_via_customize_form" + self.assertEqual( + xhiveframework.db.get_value("Custom Field", custom_field_name, "fieldtype"), + "Data", + ) + + self.assertEqual( + xhiveframework.db.get_value("Custom Field", custom_field_name, "insert_after"), + last_fieldname, + ) + + xhiveframework.delete_doc("Custom Field", custom_field_name) + self.assertEqual(xhiveframework.db.get_value("Custom Field", custom_field_name), None) + + def test_save_customization_remove_field(self): + d = self.get_customize_form("Event") + custom_field = d.get("fields", {"fieldname": self.field.fieldname})[0] + d.get("fields").remove(custom_field) + d.run_method("save_customization") + + self.assertEqual(xhiveframework.db.get_value("Custom Field", custom_field.name), None) + + xhiveframework.local.test_objects["Custom Field"] = [] + make_test_records_for_doctype("Custom Field") + + def test_reset_to_defaults(self): + d = xhiveframework.get_doc("Customize Form") + d.doc_type = "Event" + d.run_method("reset_to_defaults") + + self.assertEqual(d.get("fields", {"fieldname": "repeat_this_event"})[0].in_list_view, 0) + + xhiveframework.local.test_objects["Property Setter"] = [] + make_test_records_for_doctype("Property Setter") + + def test_set_allow_on_submit(self): + d = self.get_customize_form("Event") + d.get("fields", {"fieldname": "subject"})[0].allow_on_submit = 1 + d.get("fields", {"fieldname": "custom_test_field"})[0].allow_on_submit = 1 + d.run_method("save_customization") + + d = self.get_customize_form("Event") + + # don't allow for standard fields + self.assertEqual(d.get("fields", {"fieldname": "subject"})[0].allow_on_submit or 0, 0) + + # allow for custom field + self.assertEqual(d.get("fields", {"fieldname": "custom_test_field"})[0].allow_on_submit, 1) + + def test_title_field_pattern(self): + d = self.get_customize_form("Web Form") + + df = d.get("fields", {"fieldname": "title"})[0] + + # invalid fieldname + df.default = """{doc_type} - {introduction_test}""" + self.assertRaises(InvalidFieldNameError, d.run_method, "save_customization") + + # space in formatter + df.default = """{doc_type} - {introduction text}""" + self.assertRaises(InvalidFieldNameError, d.run_method, "save_customization") + + # valid fieldname + df.default = """{doc_type} - {introduction_text}""" + d.run_method("save_customization") + + # valid fieldname with escaped curlies + df.default = """{{ {doc_type} }} - {introduction_text}""" + d.run_method("save_customization") + + # undo + df.default = None + d.run_method("save_customization") + + def test_core_doctype_customization(self): + self.assertRaises(xhiveframework.ValidationError, self.get_customize_form, "User") + + def test_save_customization_length_field_property(self): + # Using Notification Log doctype as it doesn't have any other custom fields + d = self.get_customize_form("Notification Log") + + new_document_length = 255 + document_name = d.get("fields", {"fieldname": "document_name"})[0] + document_name.length = new_document_length + d.run_method("save_customization") + + self.assertEqual( + xhiveframework.db.get_value( + "Property Setter", + {"doc_type": "Notification Log", "property": "length", "field_name": "document_name"}, + "value", + ), + str(new_document_length), + ) + + length = xhiveframework.db.sql( + """SELECT character_maximum_length + FROM information_schema.columns + WHERE table_name = 'tabNotification Log' + AND column_name = 'document_name'""" + )[0][0] + + self.assertEqual(length, new_document_length) + + def test_custom_link(self): + try: + # create a dummy doctype linked to Event + testdt_name = "Test Link for Event" + testdt = new_doctype( + testdt_name, fields=[dict(fieldtype="Link", fieldname="event", options="Event")] + ).insert() + + testdt_name1 = "Test Link for Event 1" + testdt1 = new_doctype( + testdt_name1, fields=[dict(fieldtype="Link", fieldname="event", options="Event")] + ).insert() + + # add a custom link + d = self.get_customize_form("Event") + + d.append("links", dict(link_doctype=testdt_name, link_fieldname="event", group="Tests")) + d.append("links", dict(link_doctype=testdt_name1, link_fieldname="event", group="Tests")) + + d.run_method("save_customization") + + xhiveframework.clear_cache() + event = xhiveframework.get_meta("Event") + + # check links exist + self.assertTrue([d.name for d in event.links if d.link_doctype == testdt_name]) + self.assertTrue([d.name for d in event.links if d.link_doctype == testdt_name1]) + + # check order + order = json.loads(event.links_order) + self.assertListEqual(order, [d.name for d in event.links]) + + # remove the link + d = self.get_customize_form("Event") + d.links = [] + d.run_method("save_customization") + + xhiveframework.clear_cache() + event = xhiveframework.get_meta("Event") + self.assertFalse([d.name for d in (event.links or []) if d.link_doctype == testdt_name]) + finally: + testdt.delete() + testdt1.delete() + + def test_custom_internal_links(self): + # add a custom internal link + xhiveframework.clear_cache() + d = self.get_customize_form("User Group") + + d.append( + "links", + dict( + link_doctype="User Group Member", + parent_doctype="User Group", + link_fieldname="user", + table_fieldname="user_group_members", + group="Tests", + custom=1, + ), + ) + + d.run_method("save_customization") + + xhiveframework.clear_cache() + user_group = xhiveframework.get_meta("User Group") + + # check links exist + self.assertTrue([d.name for d in user_group.links if d.link_doctype == "User Group Member"]) + self.assertTrue([d.name for d in user_group.links if d.parent_doctype == "User Group"]) + + # remove the link + d = self.get_customize_form("User Group") + d.links = [] + d.run_method("save_customization") + + xhiveframework.clear_cache() + user_group = xhiveframework.get_meta("Event") + self.assertFalse([d.name for d in (user_group.links or []) if d.link_doctype == "User Group Member"]) + + def test_custom_action(self): + test_route = "/app/List/DocType" + + # create a dummy action (route) + d = self.get_customize_form("Event") + d.append("actions", dict(label="Test Action", action_type="Route", action=test_route)) + d.run_method("save_customization") + + xhiveframework.clear_cache() + event = xhiveframework.get_meta("Event") + + # check if added to meta + action = [d for d in event.actions if d.label == "Test Action"] + self.assertEqual(len(action), 1) + self.assertEqual(action[0].action, test_route) + + # clear the action + d = self.get_customize_form("Event") + d.actions = [] + d.run_method("save_customization") + + xhiveframework.clear_cache() + event = xhiveframework.get_meta("Event") + + action = [d for d in event.actions if d.label == "Test Action"] + self.assertEqual(len(action), 0) + + def test_custom_label(self): + d = self.get_customize_form("Event") + + # add label + d.label = "Test Rename" + d.run_method("save_customization") + self.assertEqual(d.label, "Test Rename") + + # change label + d.label = "Test Rename 2" + d.run_method("save_customization") + self.assertEqual(d.label, "Test Rename 2") + + # saving again to make sure existing label persists + d.run_method("save_customization") + self.assertEqual(d.label, "Test Rename 2") + + # clear label + d.label = "" + d.run_method("save_customization") + self.assertEqual(d.label, "") + + def test_change_to_autoincrement_autoname(self): + d = self.get_customize_form("Event") + d.autoname = "autoincrement" + + with self.assertRaises(xhiveframework.ValidationError): + d.run_method("save_customization") + + def test_system_generated_fields(self): + doctype = "Event" + custom_field_name = "custom_test_field" + + custom_field = xhiveframework.get_doc("Custom Field", {"dt": doctype, "fieldname": custom_field_name}) + custom_field.is_system_generated = 1 + custom_field.save() + + d = self.get_customize_form(doctype) + custom_field = d.getone("fields", {"fieldname": custom_field_name}) + custom_field.description = "Test Description" + d.run_method("save_customization") + + property_setter_filters = { + "doc_type": doctype, + "field_name": custom_field_name, + "property": "description", + } + self.assertEqual( + xhiveframework.db.get_value("Property Setter", property_setter_filters, "value"), "Test Description" + ) + + def test_custom_field_order(self): + # shuffle fields + customize_form = self.get_customize_form(doctype="ToDo") + customize_form.fields.insert(0, customize_form.fields.pop()) + customize_form.save_customization() + + field_order_property = json.loads( + xhiveframework.db.get_value("Property Setter", {"doc_type": "ToDo", "property": "field_order"}, "value") + ) + + self.assertEqual(field_order_property, [df.fieldname for df in xhiveframework.get_meta("ToDo").fields]) diff --git a/xhiveframework/custom/doctype/customize_form_field/__init__.py b/xhiveframework/custom/doctype/customize_form_field/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form_field/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/custom/doctype/customize_form_field/customize_form_field.json b/xhiveframework/custom/doctype/customize_form_field/customize_form_field.json new file mode 100644 index 0000000..fd55963 --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form_field/customize_form_field.json @@ -0,0 +1,491 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-02-22 01:27:32", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_system_generated", + "label_and_type", + "label", + "fieldtype", + "fieldname", + "non_negative", + "reqd", + "unique", + "is_virtual", + "in_list_view", + "in_standard_filter", + "in_global_search", + "in_preview", + "bold", + "no_copy", + "allow_in_quick_entry", + "translatable", + "column_break_7", + "default", + "precision", + "length", + "options", + "sort_options", + "fetch_from", + "fetch_if_empty", + "show_dashboard", + "permissions", + "depends_on", + "permlevel", + "hidden", + "read_only", + "collapsible", + "allow_bulk_edit", + "collapsible_depends_on", + "column_break_14", + "ignore_user_permissions", + "allow_on_submit", + "report_hide", + "remember_last_selected_value", + "hide_border", + "ignore_xss_filter", + "property_depends_on_section", + "mandatory_depends_on", + "column_break_33", + "read_only_depends_on", + "display", + "in_filter", + "hide_seconds", + "hide_days", + "column_break_21", + "description", + "print_hide", + "print_hide_if_no_value", + "print_width", + "columns", + "width", + "is_custom_field" + ], + "fields": [ + { + "fieldname": "label_and_type", + "fieldtype": "Section Break", + "label": "Label and Type" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "oldfieldname": "label", + "oldfieldtype": "Data", + "search_index": 1 + }, + { + "default": "Data", + "fieldname": "fieldtype", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "oldfieldname": "fieldtype", + "oldfieldtype": "Select", + "options": "Autocomplete\nAttach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDuration\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nIcon\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nPhone\nRating\nRead Only\nSection Break\nSelect\nSignature\nSmall Text\nTab Break\nTable\nTable MultiSelect\nText\nText Editor\nTime", + "reqd": 1, + "search_index": 1, + "sort_options": 1 + }, + { + "fieldname": "fieldname", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "oldfieldname": "fieldname", + "oldfieldtype": "Data", + "read_only": 1, + "search_index": 1 + }, + { + "default": "0", + "depends_on": "eval:!in_list([\"Section Break\", \"Column Break\", \"Button\", \"HTML\"], doc.fieldtype)", + "fieldname": "reqd", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Mandatory", + "oldfieldname": "reqd", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "unique", + "fieldtype": "Check", + "label": "Unique" + }, + { + "default": "0", + "fieldname": "is_virtual", + "fieldtype": "Check", + "label": "Is Virtual" + }, + { + "default": "0", + "depends_on": "eval:!doc.is_virtual", + "fieldname": "in_list_view", + "fieldtype": "Check", + "label": "In List View" + }, + { + "default": "0", + "fieldname": "in_standard_filter", + "fieldtype": "Check", + "label": "In Standard Filter" + }, + { + "default": "0", + "depends_on": "eval:([\"Data\", \"Select\", \"Table\", \"Text\", \"Text Editor\", \"Link\", \"Small Text\", \"Long Text\", \"Read Only\", \"Heading\", \"Dynamic Link\"].indexOf(doc.fieldtype) !== -1)", + "fieldname": "in_global_search", + "fieldtype": "Check", + "label": "In Global Search" + }, + { + "default": "0", + "fieldname": "bold", + "fieldtype": "Check", + "label": "Bold" + }, + { + "default": "1", + "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", + "fieldname": "translatable", + "fieldtype": "Check", + "label": "Translatable" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)", + "description": "Set non-standard precision for a Float or Currency field", + "fieldname": "precision", + "fieldtype": "Select", + "label": "Precision", + "options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9" + }, + { + "depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)", + "fieldname": "length", + "fieldtype": "Int", + "label": "Length" + }, + { + "description": "For Links, enter the DocType as range.\nFor Select, enter list of Options, each on a new line.", + "fieldname": "options", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Options", + "oldfieldname": "options", + "oldfieldtype": "Text" + }, + { + "fieldname": "fetch_from", + "fieldtype": "Small Text", + "label": "Fetch From" + }, + { + "default": "0", + "description": "If unchecked, the value will always be re-fetched on save.", + "fieldname": "fetch_if_empty", + "fieldtype": "Check", + "label": "Fetch on Save if Empty" + }, + { + "fieldname": "permissions", + "fieldtype": "Section Break", + "label": "Permissions" + }, + { + "description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples):\nmyfield\neval:doc.myfield=='My Value'\neval:doc.age>18", + "fieldname": "depends_on", + "fieldtype": "Code", + "label": "Depends On", + "oldfieldname": "depends_on", + "oldfieldtype": "Data", + "options": "JS" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Section Break', 'Column Break', 'Tab Break'], doc.fieldtype)", + "fieldname": "permlevel", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Perm Level", + "oldfieldname": "permlevel", + "oldfieldtype": "Int" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden", + "oldfieldname": "hidden", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible", + "fieldtype": "Check", + "label": "Collapsible" + }, + { + "default": "0", + "depends_on": "eval: doc.fieldtype == \"Table\"", + "fieldname": "allow_bulk_edit", + "fieldtype": "Check", + "label": "Allow Bulk Edit" + }, + { + "depends_on": "eval:doc.fieldtype==\"Section Break\"", + "fieldname": "collapsible_depends_on", + "fieldtype": "Code", + "label": "Collapsible Depends On", + "options": "JS" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "ignore_user_permissions", + "fieldtype": "Check", + "label": "Ignore User Permissions" + }, + { + "default": "0", + "fieldname": "allow_on_submit", + "fieldtype": "Check", + "label": "Allow on Submit", + "oldfieldname": "allow_on_submit", + "oldfieldtype": "Check" + }, + { + "default": "0", + "fieldname": "report_hide", + "fieldtype": "Check", + "label": "Report Hide", + "oldfieldname": "report_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:(doc.fieldtype == 'Link')", + "fieldname": "remember_last_selected_value", + "fieldtype": "Check", + "label": "Remember Last Selected Value" + }, + { + "fieldname": "display", + "fieldtype": "Section Break", + "label": "Display" + }, + { + "fieldname": "default", + "fieldtype": "Small Text", + "label": "Default", + "oldfieldname": "default", + "oldfieldtype": "Text" + }, + { + "default": "0", + "fieldname": "in_filter", + "fieldtype": "Check", + "label": "In Filter", + "oldfieldname": "in_filter", + "oldfieldtype": "Check", + "print_width": "50px", + "width": "50px" + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "default": "0", + "fieldname": "print_hide", + "fieldtype": "Check", + "label": "Print Hide", + "oldfieldname": "print_hide", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1", + "fieldname": "print_hide_if_no_value", + "fieldtype": "Check", + "label": "Print Hide If No Value" + }, + { + "description": "Print Width of the field, if the field is a column in a table", + "fieldname": "print_width", + "fieldtype": "Data", + "label": "Print Width", + "print_width": "50px", + "width": "50px" + }, + { + "depends_on": "eval:parent.istable", + "description": "Number of columns for a field in a Grid (Total Columns in a grid should be less than 11)", + "fieldname": "columns", + "fieldtype": "Int", + "label": "Columns" + }, + { + "fieldname": "width", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Width", + "oldfieldname": "width", + "oldfieldtype": "Data", + "print_width": "50px", + "width": "50px" + }, + { + "default": "0", + "fieldname": "is_custom_field", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Custom Field", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "allow_in_quick_entry", + "fieldtype": "Check", + "label": "Allow in Quick Entry" + }, + { + "fieldname": "property_depends_on_section", + "fieldtype": "Section Break", + "label": "Property Depends On" + }, + { + "fieldname": "mandatory_depends_on", + "fieldtype": "Code", + "label": "Mandatory Depends On", + "options": "JS" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, + { + "fieldname": "read_only_depends_on", + "fieldtype": "Code", + "label": "Read Only Depends On", + "options": "JS" + }, + { + "default": "0", + "depends_on": "eval:!in_list(['Table', 'Table MultiSelect'], doc.fieldtype);", + "fieldname": "in_preview", + "fieldtype": "Check", + "label": "In Preview" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_seconds", + "fieldtype": "Check", + "label": "Hide Seconds" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Duration'", + "fieldname": "hide_days", + "fieldtype": "Check", + "label": "Hide Days" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Section Break'", + "fieldname": "hide_border", + "fieldtype": "Check", + "label": "Hide Border" + }, + { + "default": "0", + "depends_on": "eval:in_list([\"Int\", \"Float\", \"Currency\"], doc.fieldtype)", + "fieldname": "non_negative", + "fieldtype": "Check", + "label": "Non Negative" + }, + { + "default": "0", + "depends_on": "eval:doc.fieldtype=='Tab Break'", + "fieldname": "show_dashboard", + "fieldtype": "Check", + "label": "Show Dashboard" + }, + { + "default": "0", + "fieldname": "no_copy", + "fieldtype": "Check", + "label": "No Copy" + }, + { + "default": "0", + "fieldname": "is_system_generated", + "fieldtype": "Check", + "hidden": 1, + "label": "Is System Generated", + "read_only": 1 + }, + { + "default": "0", + "description": "Don't encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", + "fieldname": "ignore_xss_filter", + "fieldtype": "Check", + "label": "Ignore XSS Filter" + }, + { + "default": "0", + "depends_on": "eval: doc.fieldtype === 'Select'", + "fieldname": "sort_options", + "fieldtype": "Check", + "label": "Sort Options" + } + ], + "idx": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-02-01 15:56:39.171633", + "modified_by": "Administrator", + "module": "Custom", + "name": "Customize Form Field", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/custom/doctype/customize_form_field/customize_form_field.py b/xhiveframework/custom/doctype/customize_form_field/customize_form_field.py new file mode 100644 index 0000000..16f074f --- /dev/null +++ b/xhiveframework/custom/doctype/customize_form_field/customize_form_field.py @@ -0,0 +1,113 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class CustomizeFormField(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allow_bulk_edit: DF.Check + allow_in_quick_entry: DF.Check + allow_on_submit: DF.Check + bold: DF.Check + collapsible: DF.Check + collapsible_depends_on: DF.Code | None + columns: DF.Int + default: DF.SmallText | None + depends_on: DF.Code | None + description: DF.Text | None + fetch_from: DF.SmallText | None + fetch_if_empty: DF.Check + fieldname: DF.Data | None + fieldtype: DF.Literal[ + "Autocomplete", + "Attach", + "Attach Image", + "Barcode", + "Button", + "Check", + "Code", + "Color", + "Column Break", + "Currency", + "Data", + "Date", + "Datetime", + "Duration", + "Dynamic Link", + "Float", + "Fold", + "Geolocation", + "Heading", + "HTML", + "HTML Editor", + "Icon", + "Image", + "Int", + "Link", + "Long Text", + "Markdown Editor", + "Password", + "Percent", + "Phone", + "Rating", + "Read Only", + "Section Break", + "Select", + "Signature", + "Small Text", + "Tab Break", + "Table", + "Table MultiSelect", + "Text", + "Text Editor", + "Time", + ] + hidden: DF.Check + hide_border: DF.Check + hide_days: DF.Check + hide_seconds: DF.Check + ignore_user_permissions: DF.Check + ignore_xss_filter: DF.Check + in_filter: DF.Check + in_global_search: DF.Check + in_list_view: DF.Check + in_preview: DF.Check + in_standard_filter: DF.Check + is_custom_field: DF.Check + is_system_generated: DF.Check + is_virtual: DF.Check + label: DF.Data | None + length: DF.Int + mandatory_depends_on: DF.Code | None + no_copy: DF.Check + non_negative: DF.Check + options: DF.SmallText | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + permlevel: DF.Int + precision: DF.Literal["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + print_hide: DF.Check + print_hide_if_no_value: DF.Check + print_width: DF.Data | None + read_only: DF.Check + read_only_depends_on: DF.Code | None + remember_last_selected_value: DF.Check + report_hide: DF.Check + reqd: DF.Check + show_dashboard: DF.Check + sort_options: DF.Check + translatable: DF.Check + unique: DF.Check + width: DF.Data | None + # end: auto-generated types + + pass diff --git a/xhiveframework/custom/doctype/doctype_layout/__init__.py b/xhiveframework/custom/doctype/doctype_layout/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/doctype/doctype_layout/doctype_layout.js b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.js new file mode 100644 index 0000000..236e73d --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.js @@ -0,0 +1,105 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("DocType Layout", { + onload_post_render(frm) { + // disallow users from manually adding/deleting rows; this doctype should only + // be used for managing layout, and docfields and custom fields should be used + // to manage other field metadata (hidden, etc.) + frm.set_df_property("fields", "cannot_add_rows", true); + frm.set_df_property("fields", "cannot_delete_rows", true); + + $(frm.wrapper).on("grid-move-row", (e, frm) => { + // refresh the layout after moving a row + frm.dirty(); + }); + }, + + refresh(frm) { + frm.events.add_buttons(frm); + }, + + async document_type(frm) { + if (frm.doc.document_type) { + // refreshing the doctype fields resets the new name input field; + // once the fields are set, reset the name to the original input + if (frm.is_new()) { + const document_name = frm.doc.__newname || frm.doc.name; + } + + frm.set_value("fields", []); + await frm.events.sync_fields(frm, false); + + if (frm.is_new()) { + frm.doc.__newname = document_name; // eslint-disable-line + frm.refresh_field("__newname"); + } + } + }, + + add_buttons(frm) { + if (!frm.is_new()) { + frm.add_custom_button(__("Go to {0} List", [frm.doc.name]), () => { + window.open(`/app/${xhiveframework.router.slug(frm.doc.name)}`); + }); + + frm.add_custom_button(__("Sync {0} Fields", [frm.doc.name]), async () => { + await frm.events.sync_fields(frm, true); + }); + } + }, + + async sync_fields(frm, notify) { + xhiveframework.dom.freeze("Fetching fields..."); + const response = await frm.call({ doc: frm.doc, method: "sync_fields" }); + frm.refresh_field("fields"); + xhiveframework.dom.unfreeze(); + + if (!response.message) { + xhiveframework.msgprint(__("No changes to sync")); + return; + } + + frm.dirty(); + if (notify) { + const addedFields = response.message.added; + const removedFields = response.message.removed; + + const getChangedMessage = (fields) => { + let changes = ""; + for (const field of fields) { + if (field.label) { + changes += `
  7. Row #${field.idx}: ${field.fieldname.bold()} (${ + field.label + })
  8. `; + } else { + changes += `
  9. Row #${field.idx}: ${field.fieldname.bold()}
  10. `; + } + } + return changes; + }; + + let message = ""; + + if (addedFields.length) { + message += `The following fields have been added:

      ${getChangedMessage( + addedFields + )}
    `; + } + + if (removedFields.length) { + message += `The following fields have been removed:

      ${getChangedMessage( + removedFields + )}
    `; + } + + if (message) { + xhiveframework.msgprint({ + message: __(message), + indicator: "green", + title: __("Synced Fields"), + }); + } + } + }, +}); diff --git a/xhiveframework/custom/doctype/doctype_layout/doctype_layout.json b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.json new file mode 100644 index 0000000..5237e76 --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "prompt", + "creation": "2020-11-16 17:05:35.306846", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "route", + "fields", + "client_script" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "fields", + "fieldtype": "Table", + "label": "Fields", + "options": "DocType Layout Field", + "reqd": 1 + }, + { + "fieldname": "client_script", + "fieldtype": "Code", + "label": "Client Script" + }, + { + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 22:24:08.103972", + "modified_by": "Administrator", + "module": "Custom", + "name": "DocType Layout", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Desk User" + } + ], + "route": "doctype-layout", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/custom/doctype/doctype_layout/doctype_layout.py b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.py new file mode 100644 index 0000000..9de73ed --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout/doctype_layout.py @@ -0,0 +1,92 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework.desk.utils import slug +from xhiveframework.model.document import Document + +if TYPE_CHECKING: + from xhiveframework.core.doctype.docfield.docfield import DocField + + +class DocTypeLayout(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.custom.doctype.doctype_layout_field.doctype_layout_field import DocTypeLayoutField + from xhiveframework.types import DF + + client_script: DF.Code | None + document_type: DF.Link + fields: DF.Table[DocTypeLayoutField] + route: DF.Data + + # end: auto-generated types + def validate(self): + if not self.route: + self.route = slug(self.name) + + @xhiveframework.whitelist() + def sync_fields(self): + doctype_fields = xhiveframework.get_meta(self.document_type, cached=False).fields + + if self.is_new(): + added_fields = [field.fieldname for field in doctype_fields] + removed_fields = [] + else: + doctype_fieldnames = {field.fieldname for field in doctype_fields} + layout_fieldnames = {field.fieldname for field in self.fields} + added_fields = list(doctype_fieldnames - layout_fieldnames) + removed_fields = list(layout_fieldnames - doctype_fieldnames) + + if not (added_fields or removed_fields): + return + + added = self.add_fields(added_fields, doctype_fields) + removed = self.remove_fields(removed_fields) + + for index, field in enumerate(self.fields): + field.idx = index + 1 + + return {"added": added, "removed": removed} + + def add_fields(self, added_fields: list[str], doctype_fields: list["DocField"]) -> list[dict]: + added = [] + for field in added_fields: + field_details = next((f for f in doctype_fields if f.fieldname == field), None) + if not field_details: + continue + + # remove 'doctype' data from the DocField to allow adding it to the layout + row = self.append("fields", field_details.as_dict(no_default_fields=True)) + row_data = row.as_dict() + + if field_details.get("insert_after"): + insert_after = next( + (f for f in self.fields if f.fieldname == field_details.insert_after), + None, + ) + + # initialize new row to just after the insert_after field + if insert_after: + self.fields.insert(insert_after.idx, row) + self.fields.pop() + + row_data = {"idx": insert_after.idx + 1, "fieldname": row.fieldname, "label": row.label} + + added.append(row_data) + return added + + def remove_fields(self, removed_fields: list[str]) -> list[dict]: + removed = [] + for field in removed_fields: + field_details = next((f for f in self.fields if f.fieldname == field), None) + if field_details: + self.remove(field_details) + removed.append(field_details.as_dict()) + return removed diff --git a/xhiveframework/custom/doctype/doctype_layout/patches/convert_web_forms_to_doctype_layout.py b/xhiveframework/custom/doctype/doctype_layout/patches/convert_web_forms_to_doctype_layout.py new file mode 100644 index 0000000..3345a0a --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout/patches/convert_web_forms_to_doctype_layout.py @@ -0,0 +1,20 @@ +import xhiveframework + + +def execute(): + for web_form_name in xhiveframework.get_all("Web Form", pluck="name"): + web_form = xhiveframework.get_doc("Web Form", web_form_name) + doctype_layout = xhiveframework.get_doc( + dict( + doctype="DocType Layout", + document_type=web_form.doc_type, + name=web_form.title, + route=web_form.route, + fields=[ + dict(fieldname=d.fieldname, label=d.label) + for d in web_form.web_form_fields + if d.fieldname + ], + ) + ).insert() + print(doctype_layout.name) diff --git a/xhiveframework/custom/doctype/doctype_layout/test_doctype_layout.py b/xhiveframework/custom/doctype/doctype_layout/test_doctype_layout.py new file mode 100644 index 0000000..1c5579e --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout/test_doctype_layout.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocTypeLayout(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/custom/doctype/doctype_layout_field/__init__.py b/xhiveframework/custom/doctype/doctype_layout_field/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.json b/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.json new file mode 100644 index 0000000..006c01a --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.json @@ -0,0 +1,38 @@ +{ + "actions": [], + "creation": "2020-11-16 16:03:43.771801", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label", + "fieldname" + ], + "fields": [ + { + "fieldname": "fieldname", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Fieldname", + "reqd": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-19 16:27:40.585865", + "modified_by": "Administrator", + "module": "Custom", + "name": "DocType Layout Field", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} diff --git a/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.py b/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.py new file mode 100644 index 0000000..d69bf5d --- /dev/null +++ b/xhiveframework/custom/doctype/doctype_layout_field/doctype_layout_field.py @@ -0,0 +1,23 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DocTypeLayoutField(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + fieldname: DF.Literal[None] + label: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/custom/doctype/property_setter/README.md b/xhiveframework/custom/doctype/property_setter/README.md new file mode 100644 index 0000000..65ece5e --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/README.md @@ -0,0 +1 @@ +Overrides standard DocType, DocField properties. The standard application is configured with properties for forms and fields (like, whether they are hidden or not). These can be overridden by a System Manager who can configure DocTypes based on custom requirements. \ No newline at end of file diff --git a/xhiveframework/custom/doctype/property_setter/__init__.py b/xhiveframework/custom/doctype/property_setter/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/custom/doctype/property_setter/patches/__init__.py b/xhiveframework/custom/doctype/property_setter/patches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py b/xhiveframework/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py new file mode 100644 index 0000000..41eae99 --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py @@ -0,0 +1,28 @@ +from contextlib import suppress + +import xhiveframework + + +def execute(): + """Remove invalid fetch from expressions""" + with suppress(Exception): + property_setters = xhiveframework.get_all( + "Property Setter", {"doctype_or_field": "DocField", "property": "fetch_from"}, ["name", "value"] + ) + for ps in property_setters: + if not is_valid_expression(ps.value): + xhiveframework.db.delete("Property Setter", {"name": ps.name}) + + custom_fields = xhiveframework.get_all("Custom Field", {"fetch_from": ("is", "set")}, ["name", "fetch_from"]) + for cf in custom_fields: + if not is_valid_expression(cf.fetch_from): + xhiveframework.db.set_value("Custom Field", cf.name, "fetch_from", "") + + +def is_valid_expression(expr) -> bool: + if not expr or "." not in expr: + return False + source_field, target_field = expr.split(".", maxsplit=1) + if not source_field or not target_field: + return False + return True diff --git a/xhiveframework/custom/doctype/property_setter/property_setter.js b/xhiveframework/custom/doctype/property_setter/property_setter.js new file mode 100644 index 0000000..08d6197 --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/property_setter.js @@ -0,0 +1,10 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.ui.form.on("Property Setter", { + validate: function (frm) { + if (frm.doc.property_type == "Check" && !["0", "1"].includes(frm.doc.value)) { + xhiveframework.throw(__("Value for a check field can be either 0 or 1")); + } + }, +}); diff --git a/xhiveframework/custom/doctype/property_setter/property_setter.json b/xhiveframework/custom/doctype/property_setter/property_setter.json new file mode 100644 index 0000000..039826b --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/property_setter.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "creation": "2013-01-10 16:34:04", + "description": "Property Setter overrides a standard DocType or Field property", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "is_system_generated", + "help", + "sb0", + "doctype_or_field", + "doc_type", + "field_name", + "row_name", + "column_break0", + "module", + "section_break_9", + "property", + "property_type", + "value", + "default_value" + ], + "fields": [ + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "
    Please don't update it as it can mess up your form. Use the Customize Form View and Custom Fields to set properties!
    " + }, + { + "fieldname": "sb0", + "fieldtype": "Section Break" + }, + { + "fieldname": "doctype_or_field", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Applied On", + "options": "\nDocField\nDocType\nDocType Link\nDocType Action\nDocType State", + "read_only_depends_on": "eval:!doc.__islocal", + "reqd": 1 + }, + { + "description": "New value to be set", + "fieldname": "value", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Set Value" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break" + }, + { + "fieldname": "doc_type", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:doc.doctype_or_field=='DocField'", + "description": "ID (name) of the entity whose property is to be set", + "fieldname": "field_name", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Field Name", + "search_index": 1 + }, + { + "fieldname": "property", + "fieldtype": "Data", + "in_standard_filter": 1, + "label": "Property", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "property_type", + "fieldtype": "Data", + "label": "Property Type" + }, + { + "fieldname": "default_value", + "fieldtype": "Data", + "label": "Default Value" + }, + { + "description": "For DocType Link / DocType Action", + "fieldname": "row_name", + "fieldtype": "Data", + "label": "Row Name" + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module (for export)", + "options": "Module Def" + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "is_system_generated", + "fieldtype": "Check", + "label": "Is System Generated", + "read_only": 1 + } + ], + "icon": "fa fa-glass", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2022-02-28 22:24:12.377693", + "modified_by": "Administrator", + "module": "Custom", + "name": "Property Setter", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "search_fields": "doc_type,property", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/custom/doctype/property_setter/property_setter.py b/xhiveframework/custom/doctype/property_setter/property_setter.py new file mode 100644 index 0000000..3ed499a --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/property_setter.py @@ -0,0 +1,98 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + +not_allowed_fieldtype_change = ["naming_series"] + + +class PropertySetter(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + default_value: DF.Data | None + doc_type: DF.Link + doctype_or_field: DF.Literal[ + "", "DocField", "DocType", "DocType Link", "DocType Action", "DocType State" + ] + field_name: DF.Data | None + is_system_generated: DF.Check + module: DF.Link | None + property: DF.Data + property_type: DF.Data | None + row_name: DF.Data | None + value: DF.SmallText | None + + # end: auto-generated types + def autoname(self): + self.name = "{doctype}-{field}-{property}".format( + doctype=self.doc_type, field=self.field_name or self.row_name or "main", property=self.property + ) + + def validate(self): + self.validate_fieldtype_change() + + if self.is_new(): + delete_property_setter(self.doc_type, self.property, self.field_name, self.row_name) + xhiveframework.clear_cache(doctype=self.doc_type) + + def on_trash(self): + xhiveframework.clear_cache(doctype=self.doc_type) + + def validate_fieldtype_change(self): + if self.property == "fieldtype" and self.field_name in not_allowed_fieldtype_change: + xhiveframework.throw(_("Field type cannot be changed for {0}").format(self.field_name)) + + def on_update(self): + if xhiveframework.flags.in_patch: + self.flags.validate_fields_for_doctype = False + + if not self.flags.ignore_validate and self.flags.validate_fields_for_doctype: + from xhiveframework.core.doctype.doctype.doctype import validate_fields_for_doctype + + validate_fields_for_doctype(self.doc_type) + + +def make_property_setter( + doctype, + fieldname, + property, + value, + property_type, + for_doctype=False, + validate_fields_for_doctype=True, +): + # WARNING: Ignores Permissions + property_setter = xhiveframework.get_doc( + { + "doctype": "Property Setter", + "doctype_or_field": for_doctype and "DocType" or "DocField", + "doc_type": doctype, + "field_name": fieldname, + "property": property, + "value": value, + "property_type": property_type, + } + ) + property_setter.flags.ignore_permissions = True + property_setter.flags.validate_fields_for_doctype = validate_fields_for_doctype + property_setter.insert() + return property_setter + + +def delete_property_setter(doc_type, property, field_name=None, row_name=None): + """delete other property setters on this, if this is new""" + filters = dict(doc_type=doc_type, property=property) + if field_name: + filters["field_name"] = field_name + if row_name: + filters["row_name"] = row_name + + xhiveframework.db.delete("Property Setter", filters) diff --git a/xhiveframework/custom/doctype/property_setter/test_property_setter.py b/xhiveframework/custom/doctype/property_setter/test_property_setter.py new file mode 100644 index 0000000..705db3a --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/test_property_setter.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Property Setter') + + +class TestPropertySetter(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/custom/doctype/property_setter/test_records.json b/xhiveframework/custom/doctype/property_setter/test_records.json new file mode 100644 index 0000000..3c084b4 --- /dev/null +++ b/xhiveframework/custom/doctype/property_setter/test_records.json @@ -0,0 +1,10 @@ +[ + { + "doc_type": "User", + "doctype_or_field": "DocField", + "field_name": "location", + "property": "in_list_view", + "property_type": "Check", + "value": "1" + } +] \ No newline at end of file diff --git a/xhiveframework/custom/fixtures/temp_doctype.json b/xhiveframework/custom/fixtures/temp_doctype.json new file mode 100644 index 0000000..20b3d9c --- /dev/null +++ b/xhiveframework/custom/fixtures/temp_doctype.json @@ -0,0 +1,166 @@ +{ + "docstatus": 0, + "doctype": "DocType", + "name": "new-doctype-2", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "is_submittable": 0, + "istable": 0, + "issingle": 0, + "is_tree": 0, + "editable_grid": 1, + "quick_entry": 1, + "track_changes": 1, + "track_seen": 0, + "track_views": 0, + "custom": 1, + "beta": 0, + "is_virtual": 0, + "naming_rule": "", + "allow_rename": 1, + "hide_toolbar": 0, + "allow_copy": 0, + "allow_import": 0, + "allow_events_in_timeline": 0, + "allow_auto_repeat": 0, + "sort_field": "modified", + "sort_order": "DESC", + "document_type": "", + "show_preview_popup": 0, + "show_name_in_global_search": 0, + "email_append_to": 0, + "read_only": 0, + "in_create": 0, + "has_web_view": 0, + "allow_guest_to_view": 0, + "index_web_pages_for_search": 1, + "engine": "InnoDB", + "permissions": [ + { + "docstatus": 0, + "doctype": "DocPerm", + "name": "new-docperm-2", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "if_owner": 0, + "permlevel": 0, + "select": 0, + "read": 1, + "write": 1, + "create": 1, + "delete": 1, + "submit": 0, + "cancel": 0, + "amend": 0, + "report": 1, + "export": 1, + "import": 0, + "share": 1, + "print": 1, + "email": 1, + "parent": "new-doctype-2", + "parentfield": "permissions", + "parenttype": "DocType", + "idx": 1, + "role": "System Manager" + } + ], + "__newname": "temp_doctype", + "module": "Custom", + "fields": [ + { + "docstatus": 0, + "doctype": "DocField", + "name": "new-docfield-1", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "fieldtype": "Data", + "precision": "", + "non_negative": 0, + "hide_days": 0, + "hide_seconds": 0, + "reqd": 1, + "search_index": 0, + "fetch_if_empty": 0, + "hidden": 0, + "bold": 0, + "allow_in_quick_entry": 0, + "translatable": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "report_hide": 0, + "collapsible": 0, + "hide_border": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "in_preview": 0, + "in_filter": 0, + "in_global_search": 0, + "read_only": 0, + "allow_on_submit": 0, + "ignore_user_permissions": 0, + "allow_bulk_edit": 0, + "permlevel": 0, + "ignore_xss_filter": 0, + "unique": 0, + "no_copy": 0, + "set_only_once": 0, + "remember_last_selected_value": 0, + "parent": "new-doctype-2", + "parentfield": "fields", + "parenttype": "DocType", + "idx": 1, + "__unedited": false, + "label": "member_name" + }, + { + "docstatus": 0, + "doctype": "DocField", + "name": "new-docfield-2", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "fieldtype": "Data", + "precision": "", + "non_negative": 0, + "hide_days": 0, + "hide_seconds": 0, + "reqd": 0, + "search_index": 0, + "fetch_if_empty": 0, + "hidden": 0, + "bold": 0, + "allow_in_quick_entry": 0, + "translatable": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "report_hide": 0, + "collapsible": 0, + "hide_border": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "in_preview": 0, + "in_filter": 0, + "in_global_search": 0, + "read_only": 0, + "allow_on_submit": 0, + "ignore_user_permissions": 0, + "allow_bulk_edit": 0, + "permlevel": 0, + "ignore_xss_filter": 0, + "unique": 0, + "no_copy": 0, + "set_only_once": 0, + "remember_last_selected_value": 0, + "parent": "new-doctype-2", + "parentfield": "fields", + "parenttype": "DocType", + "idx": 2, + "__unedited": false, + "label": "email" + } + ] +} diff --git a/xhiveframework/custom/fixtures/temp_singles.json b/xhiveframework/custom/fixtures/temp_singles.json new file mode 100644 index 0000000..723f47d --- /dev/null +++ b/xhiveframework/custom/fixtures/temp_singles.json @@ -0,0 +1,166 @@ +{ + "docstatus": 0, + "doctype": "DocType", + "name": "new-doctype-1", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "is_submittable": 0, + "istable": 0, + "issingle": 1, + "is_tree": 0, + "editable_grid": 1, + "quick_entry": 0, + "track_changes": 1, + "track_seen": 0, + "track_views": 0, + "custom": 1, + "beta": 0, + "is_virtual": 0, + "naming_rule": "", + "allow_rename": 1, + "hide_toolbar": 0, + "allow_copy": 0, + "allow_import": 0, + "allow_events_in_timeline": 0, + "allow_auto_repeat": 0, + "sort_field": "modified", + "sort_order": "DESC", + "document_type": "", + "show_preview_popup": 0, + "show_name_in_global_search": 0, + "email_append_to": 0, + "read_only": 0, + "in_create": 0, + "has_web_view": 0, + "allow_guest_to_view": 0, + "index_web_pages_for_search": 1, + "engine": "InnoDB", + "permissions": [ + { + "docstatus": 0, + "doctype": "DocPerm", + "name": "new-docperm-1", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "if_owner": 0, + "permlevel": 0, + "select": 0, + "read": 1, + "write": 1, + "create": 1, + "delete": 1, + "submit": 0, + "cancel": 0, + "amend": 0, + "report": 1, + "export": 1, + "import": 0, + "share": 1, + "print": 1, + "email": 1, + "parent": "new-doctype-1", + "parentfield": "permissions", + "parenttype": "DocType", + "idx": 1, + "role": "System Manager" + } + ], + "__newname": "temp_singles", + "module": "Custom", + "fields": [ + { + "docstatus": 0, + "doctype": "DocField", + "name": "new-docfield-1", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "fieldtype": "Data", + "precision": "", + "non_negative": 0, + "hide_days": 0, + "hide_seconds": 0, + "reqd": 0, + "search_index": 0, + "fetch_if_empty": 0, + "hidden": 0, + "bold": 0, + "allow_in_quick_entry": 0, + "translatable": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "report_hide": 0, + "collapsible": 0, + "hide_border": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "in_preview": 0, + "in_filter": 0, + "in_global_search": 0, + "read_only": 0, + "allow_on_submit": 0, + "ignore_user_permissions": 0, + "allow_bulk_edit": 0, + "permlevel": 0, + "ignore_xss_filter": 0, + "unique": 0, + "no_copy": 0, + "set_only_once": 0, + "remember_last_selected_value": 0, + "parent": "new-doctype-1", + "parentfield": "fields", + "parenttype": "DocType", + "idx": 1, + "__unedited": false, + "label": "member_name" + }, + { + "docstatus": 0, + "doctype": "DocField", + "name": "new-docfield-2", + "__islocal": 1, + "__unsaved": 1, + "owner": "Administrator", + "fieldtype": "Data", + "precision": "", + "non_negative": 0, + "hide_days": 0, + "hide_seconds": 0, + "reqd": 0, + "search_index": 0, + "fetch_if_empty": 0, + "hidden": 0, + "bold": 0, + "allow_in_quick_entry": 0, + "translatable": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "report_hide": 0, + "collapsible": 0, + "hide_border": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "in_preview": 0, + "in_filter": 0, + "in_global_search": 0, + "read_only": 0, + "allow_on_submit": 0, + "ignore_user_permissions": 0, + "allow_bulk_edit": 0, + "permlevel": 0, + "ignore_xss_filter": 0, + "unique": 0, + "no_copy": 0, + "set_only_once": 0, + "remember_last_selected_value": 0, + "parent": "new-doctype-1", + "parentfield": "fields", + "parenttype": "DocType", + "idx": 2, + "__unedited": false, + "label": "email" + } + ] +} diff --git a/xhiveframework/custom/form_tour/custom_field/custom_field.json b/xhiveframework/custom/form_tour/custom_field/custom_field.json new file mode 100644 index 0000000..3279449 --- /dev/null +++ b/xhiveframework/custom/form_tour/custom_field/custom_field.json @@ -0,0 +1,79 @@ +{ + "creation": "2021-11-23 12:22:32.922700", + "docstatus": 0, + "doctype": "Form Tour", + "first_document": 0, + "idx": 0, + "include_name_field": 0, + "is_standard": 1, + "modified": "2021-11-24 19:15:34.244244", + "modified_by": "Administrator", + "module": "Custom", + "name": "Custom Field", + "owner": "Administrator", + "reference_doctype": "Custom Field", + "save_on_complete": 1, + "steps": [ + { + "description": "Select a Document for which you want the Custom Field", + "field": "", + "fieldname": "dt", + "fieldtype": "Link", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Document", + "parent_field": "", + "position": "Right", + "title": "Document" + }, + { + "description": "Enter a Label for this field", + "field": "", + "fieldname": "label", + "fieldtype": "Data", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Label", + "parent_field": "", + "position": "Right", + "title": "Label" + }, + { + "description": "Select the label after which you want to insert new field.", + "field": "", + "fieldname": "insert_after", + "fieldtype": "Select", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Insert After", + "parent_field": "", + "position": "Right", + "title": "Insert After" + }, + { + "description": "Select an appropriate Field Type that suits your requirements", + "field": "", + "fieldname": "fieldtype", + "fieldtype": "Select", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Field Type", + "parent_field": "", + "position": "Left", + "title": "Field Type" + }, + { + "description": "Check this to make it a mandatory field", + "field": "", + "fieldname": "reqd", + "fieldtype": "Check", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Is Mandatory Field", + "parent_field": "", + "position": "Left", + "title": "Is Mandatory Field" + } + ], + "title": "Custom Field" +} \ No newline at end of file diff --git a/xhiveframework/custom/module_onboarding/customization/customization.json b/xhiveframework/custom/module_onboarding/customization/customization.json new file mode 100644 index 0000000..74719f0 --- /dev/null +++ b/xhiveframework/custom/module_onboarding/customization/customization.json @@ -0,0 +1,44 @@ +{ + "allow_roles": [ + { + "role": "All" + } + ], + "creation": "2021-11-23 12:21:11.384229", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.xhiveerp.com/docs/v13/user/manual/en/customize-xhiveerp", + "idx": 0, + "is_complete": 0, + "modified": "2021-11-24 17:04:31.523715", + "modified_by": "Administrator", + "module": "Custom", + "name": "Customization", + "owner": "Administrator", + "steps": [ + { + "step": "Custom Field" + }, + { + "step": "Custom Doctype" + }, + { + "step": "Naming Series" + }, + { + "step": "Workflows" + }, + { + "step": "Role Permissions" + }, + { + "step": "Print Format" + }, + { + "step": "Report Builder" + } + ], + "subtitle": "Custom Field, Custom Doctype, Naming Series, Role Permission, Workflow, Print Formats, Reports", + "success_message": "Customization onboarding is all done!", + "title": "Customization" +} diff --git a/xhiveframework/custom/onboarding_step/custom_doctype/custom_doctype.json b/xhiveframework/custom/onboarding_step/custom_doctype/custom_doctype.json new file mode 100644 index 0000000..2a84d8b --- /dev/null +++ b/xhiveframework/custom/onboarding_step/custom_doctype/custom_doctype.json @@ -0,0 +1,21 @@ +{ + "action": "Create Entry", + "action_label": "Learn more about creating new DocTypes", + "creation": "2021-11-23 12:30:04.407568", + "description": "A DocType (Document Type) is used to insert forms in XhiveERP. Forms such as Customer, Orders, and Invoices are Doctypes in the backend. You can also create new DocTypes to create new forms in XhiveERP as per your business needs.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-23 12:30:04.407568", + "modified_by": "Administrator", + "name": "Custom Doctype", + "owner": "Administrator", + "reference_document": "DocType", + "show_form_tour": 1, + "show_full_form": 1, + "title": "Custom Document Types", + "validate_action": 1 +} diff --git a/xhiveframework/custom/onboarding_step/custom_field/custom_field.json b/xhiveframework/custom/onboarding_step/custom_field/custom_field.json new file mode 100644 index 0000000..f4e1e09 --- /dev/null +++ b/xhiveframework/custom/onboarding_step/custom_field/custom_field.json @@ -0,0 +1,21 @@ +{ + "action": "Create Entry", + "action_label": "Learn how to add Custom Fields", + "creation": "2021-11-23 12:21:09.479808", + "description": "Every form in XhiveERP has a standard set of fields. If you need to capture some information, but there is no standard Field available for it, you can insert Custom Field for it.\n\nOnce custom fields are added, you can use them for reports and analytics charts as well.\n", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-23 12:21:09.479808", + "modified_by": "Administrator", + "name": "Custom Field", + "owner": "Administrator", + "reference_document": "Custom Field", + "show_form_tour": 1, + "show_full_form": 1, + "title": "Create Custom Fields", + "validate_action": 1 +} diff --git a/xhiveframework/custom/onboarding_step/naming_series/naming_series.json b/xhiveframework/custom/onboarding_step/naming_series/naming_series.json new file mode 100644 index 0000000..8dc4c28 --- /dev/null +++ b/xhiveframework/custom/onboarding_step/naming_series/naming_series.json @@ -0,0 +1,20 @@ +{ + "action": "Watch Video", + "creation": "2021-11-23 13:57:45.091427", + "description": "Each document created in XhiveERP can have a unique ID generated for it, using a prefix defined for it. Though each document has some prefix pre-configured, you can further customize it using tools like Naming Series Tool and Document Naming Rule.\n", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-24 15:04:14.662684", + "modified_by": "Administrator", + "name": "Naming Series", + "owner": "Administrator", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Setup Naming Series", + "validate_action": 1, + "video_url": "https://youtu.be/IGyISSfI1qU" +} diff --git a/xhiveframework/custom/onboarding_step/print_format/print_format.json b/xhiveframework/custom/onboarding_step/print_format/print_format.json new file mode 100644 index 0000000..681ef85 --- /dev/null +++ b/xhiveframework/custom/onboarding_step/print_format/print_format.json @@ -0,0 +1,21 @@ +{ + "action": "Create Entry", + "action_label": "Learn about Standard and Custom Print Formats", + "creation": "2021-11-23 15:04:12.728513", + "description": "Print Formats allow you can define looks for documents when printed or converted to PDF. You can also create a custom Print Format using drag-and-drop tools.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-23 15:04:12.728513", + "modified_by": "Administrator", + "name": "Print Format", + "owner": "Administrator", + "reference_document": "Print Format", + "show_form_tour": 1, + "show_full_form": 1, + "title": "Customize Print Formats", + "validate_action": 1 +} \ No newline at end of file diff --git a/xhiveframework/custom/onboarding_step/report_builder/report_builder.json b/xhiveframework/custom/onboarding_step/report_builder/report_builder.json new file mode 100644 index 0000000..d98d3bf --- /dev/null +++ b/xhiveframework/custom/onboarding_step/report_builder/report_builder.json @@ -0,0 +1,22 @@ +{ + "action": "Watch Video", + "action_label": "Learn more about Report Builders", + "creation": "2021-11-24 17:04:18.762838", + "description": "In each module, you will find a host of single-click reports, ranging from financial statements to sales and purchase analytics and stock tracking reports. If a required new report is not available out-of-the-box, you can create custom reports in XhiveERP by pulling values from the same multiple XhiveERP tables.\n", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-24 17:04:18.762838", + "modified_by": "Administrator", + "name": "Report Builder", + "owner": "Administrator", + "reference_document": "Report", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Generate Custom Reports", + "validate_action": 1, + "video_url": "https://youtu.be/TxJGUNarcQs" +} diff --git a/xhiveframework/custom/onboarding_step/role_permissions/role_permissions.json b/xhiveframework/custom/onboarding_step/role_permissions/role_permissions.json new file mode 100644 index 0000000..2180690 --- /dev/null +++ b/xhiveframework/custom/onboarding_step/role_permissions/role_permissions.json @@ -0,0 +1,20 @@ +{ + "action": "Watch Video", + "creation": "2021-11-23 14:00:27.208500", + "description": "In XhiveERP, you can add your Employees as Users, and give them restricted access. Tools like Role Permission and User Permission allow you to define rules which give restricted access to the user to masters and transactions.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-24 15:04:14.615232", + "modified_by": "Administrator", + "name": "Role Permissions", + "owner": "Administrator", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Setup Limited Access for a User", + "validate_action": 1, + "video_url": "https://youtu.be/g3mk45o1zAg" +} diff --git a/xhiveframework/custom/onboarding_step/workflows/workflows.json b/xhiveframework/custom/onboarding_step/workflows/workflows.json new file mode 100644 index 0000000..9ee3ca0 --- /dev/null +++ b/xhiveframework/custom/onboarding_step/workflows/workflows.json @@ -0,0 +1,20 @@ +{ + "action": "Watch Video", + "creation": "2021-11-23 13:58:58.530044", + "description": "Workflows allow you to define custom rules for the approval process of a particular document in XhiveERP. You can also set complex Workflow Rules and set approval conditions.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2021-11-24 15:04:14.632144", + "modified_by": "Administrator", + "name": "Workflows", + "owner": "Administrator", + "show_form_tour": 0, + "show_full_form": 0, + "title": "Setup Approval Workflows", + "validate_action": 1, + "video_url": "https://youtu.be/yObJUg9FxFs" +} diff --git a/xhiveframework/custom/report/__init__.py b/xhiveframework/custom/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/report/audit_system_hooks/__init__.py b/xhiveframework/custom/report/audit_system_hooks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.js b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.js new file mode 100644 index 0000000..5433c99 --- /dev/null +++ b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.js @@ -0,0 +1,6 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["Audit System Hooks"] = { + filters: [], +}; diff --git a/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.json b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.json new file mode 100644 index 0000000..b13a43a --- /dev/null +++ b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-01-25 15:02:21.896117", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2023-01-31 14:53:37.778576", + "modified_by": "Administrator", + "module": "Custom", + "name": "Audit System Hooks", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Property Setter", + "report_name": "Audit System Hooks", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.py b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.py new file mode 100644 index 0000000..5f78040 --- /dev/null +++ b/xhiveframework/custom/report/audit_system_hooks/audit_system_hooks.py @@ -0,0 +1,68 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework + + +def execute(filters=None): + return get_columns(), get_data() + + +def get_columns(): + values_field_type = "Data" # TODO: better text wrapping in reportview + columns = [ + {"label": "Hook name", "fieldname": "hook_name", "fieldtype": "Data", "width": 200}, + {"label": "Hook key (optional)", "fieldname": "hook_key", "fieldtype": "Data", "width": 200}, + {"label": "Hook Values (resolved)", "fieldname": "hook_values", "fieldtype": values_field_type}, + ] + + # Each app is shown in order as a column + installed_apps = xhiveframework.get_installed_apps(_ensure_on_bench=True) + columns += [{"label": app, "fieldname": app, "fieldtype": values_field_type} for app in installed_apps] + + return columns + + +def get_data(): + hooks = xhiveframework.get_hooks() + installed_apps = xhiveframework.get_installed_apps(_ensure_on_bench=True) + + def fmt_hook_values(v): + """Improve readability by discarding falsy values and removing containers when only 1 + value is in container""" + if not v: + return "" + + v = delist(v) + + if isinstance(v, dict | list): + try: + return xhiveframework.as_json(v) + except Exception: + pass + + return str(v) + + data = [] + for hook, values in hooks.items(): + if isinstance(values, dict): + for k, v in values.items(): + row = {"hook_name": hook, "hook_key": fmt_hook_values(k), "hook_values": fmt_hook_values(v)} + for app in installed_apps: + if app_hooks := delist(xhiveframework.get_hooks(hook, app_name=app)): + row[app] = fmt_hook_values(app_hooks.get(k)) + data.append(row) + else: + row = {"hook_name": hook, "hook_values": fmt_hook_values(values)} + for app in installed_apps: + row[app] = fmt_hook_values(xhiveframework.get_hooks(hook, app_name=app)) + + data.append(row) + + return data + + +def delist(val): + if isinstance(val, list) and len(val) == 1: + return val[0] + return val diff --git a/xhiveframework/custom/report/audit_system_hooks/test_audit_system_hooks.py b/xhiveframework/custom/report/audit_system_hooks/test_audit_system_hooks.py new file mode 100644 index 0000000..a822692 --- /dev/null +++ b/xhiveframework/custom/report/audit_system_hooks/test_audit_system_hooks.py @@ -0,0 +1,17 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + + +from xhiveframework.custom.report.audit_system_hooks.audit_system_hooks import execute +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestAuditSystemHooksReport(XhiveFrameworkTestCase): + def test_basic_query(self): + _, data = execute() + for row in data: + if row.get("hook_name") == "app_name": + self.assertEqual(row.get("hook_values"), "xhiveframework") + break + else: + self.fail("Failed to generate hooks report") diff --git a/xhiveframework/data/google_fonts.json b/xhiveframework/data/google_fonts.json new file mode 100644 index 0000000..232e509 --- /dev/null +++ b/xhiveframework/data/google_fonts.json @@ -0,0 +1,56 @@ +[ + "Alegreya Sans", + "Alegreya", + "Andada Pro", + "Anton", + "Archivo Narrow", + "Archivo", + "BioRhyme", + "Cardo", + "Chivo", + "Cormorant", + "Crimson Text", + "DM Sans", + "Eczar", + "Encode Sans", + "Epilogue ", + "Fira Sans", + "Hahmlet", + "IBM Plex Sans", + "Inconsolata", + "Inknut Antiqua", + "Inter", + "JetBrains Mono", + "Karla", + "Lato", + "Libre Baskerville", + "Libre Franklin", + "Lora", + "Manrope", + "Merriweather", + "Montserrat", + "Neuton", + "Nunito", + "Old Standard TT", + "Open Sans", + "Oswald", + "Oxygen", + "Playfair Display", + "Poppins", + "Proza Libre", + "PT Sans", + "PT Serif", + "Raleway", + "Roboto Slab", + "Roboto", + "Rubik", + "Sora", + "Source Sans Pro", + "Source Serif Pro", + "Space Grotesk", + "Space Mono", + "Spectral", + "Syne", + "Work Sans" +] + diff --git a/xhiveframework/database/__init__.py b/xhiveframework/database/__init__.py new file mode 100644 index 0000000..0c08a1f --- /dev/null +++ b/xhiveframework/database/__init__.py @@ -0,0 +1,108 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# Database Module +# -------------------- +from shutil import which + +from xhiveframework.database.database import savepoint + + +def setup_database(force, source_sql=None, verbose=None, no_mariadb_socket=False): + import xhiveframework + + if xhiveframework.conf.db_type == "postgres": + import xhiveframework.database.postgres.setup_db + + return xhiveframework.database.postgres.setup_db.setup_database(force, source_sql, verbose) + else: + import xhiveframework.database.mariadb.setup_db + + return xhiveframework.database.mariadb.setup_db.setup_database( + force, source_sql, verbose, no_mariadb_socket=no_mariadb_socket + ) + + +def drop_user_and_database(db_name, root_login=None, root_password=None): + import xhiveframework + + if xhiveframework.conf.db_type == "postgres": + import xhiveframework.database.postgres.setup_db + + return xhiveframework.database.postgres.setup_db.drop_user_and_database(db_name, root_login, root_password) + else: + import xhiveframework.database.mariadb.setup_db + + return xhiveframework.database.mariadb.setup_db.drop_user_and_database(db_name, root_login, root_password) + + +def get_db(host=None, user=None, password=None, port=None): + import xhiveframework + + if xhiveframework.conf.db_type == "postgres": + import xhiveframework.database.postgres.database + + return xhiveframework.database.postgres.database.PostgresDatabase(host, user, password, port=port) + else: + import xhiveframework.database.mariadb.database + + return xhiveframework.database.mariadb.database.MariaDBDatabase(host, user, password, port=port) + + +def get_command(host=None, port=None, user=None, password=None, db_name=None, extra=None, dump=False): + import xhiveframework + + if xhiveframework.conf.db_type == "postgres": + if dump: + bin, bin_name = which("pg_dump"), "pg_dump" + else: + bin, bin_name = which("psql"), "psql" + + if password: + conn_string = f"postgresql://{user}:{password}@{host}:{port}/{db_name}" + else: + conn_string = f"postgresql://{user}@{host}:{port}/{db_name}" + + command = [conn_string] + + if extra: + command.extend(extra) + + else: + if dump: + bin, bin_name = which("mariadb-dump") or which("mysqldump"), "mariadb-dump" + else: + bin, bin_name = which("mariadb") or which("mysql"), "mariadb" + + command = [ + f"--user={user}", + f"--host={host}", + f"--port={port}", + ] + + if password: + command.append(f"--password={password}") + + if dump: + command.extend( + [ + "--single-transaction", + "--quick", + "--lock-tables=false", + ] + ) + else: + command.extend( + [ + "--pager=less -SFX", + "--safe-updates", + "--no-auto-rehash", + ] + ) + + command.append(db_name) + + if extra: + command.extend(extra) + + return bin, command, bin_name diff --git a/xhiveframework/database/database.py b/xhiveframework/database/database.py new file mode 100644 index 0000000..711144f --- /dev/null +++ b/xhiveframework/database/database.py @@ -0,0 +1,1431 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import datetime +import itertools +import json +import random +import re +import string +import traceback +from collections.abc import Iterable, Sequence +from contextlib import contextmanager, suppress +from time import time +from typing import TYPE_CHECKING, Any, Union + +from pypika.dialects import MySQLQueryBuilder, PostgreSQLQueryBuilder +from pypika.terms import Criterion, NullValue + +import xhiveframework +import xhiveframework.defaults +from xhiveframework import _ +from xhiveframework.database.utils import ( + DefaultOrderBy, + EmptyQueryValues, + FallBackDateTimeStr, + LazyMogrify, + Query, + QueryValues, + is_query_type, +) +from xhiveframework.exceptions import DoesNotExistError, ImplicitCommitError +from xhiveframework.monitor import get_trace_id +from xhiveframework.query_builder.functions import Count +from xhiveframework.utils import CallbackManager, cint, get_datetime, get_table_name, getdate, now, sbool +from xhiveframework.utils import cast as cast_fieldtype +from xhiveframework.utils.deprecations import deprecation_warning + +if TYPE_CHECKING: + from psycopg2 import connection as PostgresConnection + from psycopg2 import cursor as PostgresCursor + from pymysql.connections import Connection as MariadbConnection + from pymysql.cursors import Cursor as MariadbCursor + + +IFNULL_PATTERN = re.compile(r"ifnull\(", flags=re.IGNORECASE) +INDEX_PATTERN = re.compile(r"\s*\([^)]+\)\s*") +SINGLE_WORD_PATTERN = re.compile(r'([`"]?)(tab([A-Z]\w+))\1') +MULTI_WORD_PATTERN = re.compile(r'([`"])(tab([A-Z]\w+)( [A-Z]\w+)+)\1') + +SQL_ITERATOR_BATCH_SIZE = 100 + + +class Database: + """ + Open a database connection with the given parmeters, if use_default is True, use the + login details from `conf.py`. This is called by the request handler and is accessible using + the `db` global variable. the `sql` method is also global to run queries + """ + + VARCHAR_LEN = 140 + MAX_COLUMN_LENGTH = 64 + + OPTIONAL_COLUMNS = ("_user_tags", "_comments", "_assign", "_liked_by") + DEFAULT_SHORTCUTS = ("_Login", "__user", "_Full Name", "Today", "__today", "now", "Now") + STANDARD_VARCHAR_COLUMNS = ("name", "owner", "modified_by") + DEFAULT_COLUMNS = ("name", "creation", "modified", "modified_by", "owner", "docstatus", "idx") + CHILD_TABLE_COLUMNS = ("parent", "parenttype", "parentfield") + MAX_WRITES_PER_TRANSACTION = 200_000 + + class InvalidColumnName(xhiveframework.ValidationError): + pass + + def __init__( + self, + host=None, + user=None, + password=None, + ac_name=None, + use_default=0, + port=None, + ): + self.setup_type_map() + self.host = host or xhiveframework.conf.db_host + self.port = port or xhiveframework.conf.db_port + self.user = user or xhiveframework.conf.db_name + self.db_name = xhiveframework.conf.db_name + self._conn = None + + if ac_name: + self.user = ac_name or xhiveframework.conf.db_name + + if use_default: + self.user = xhiveframework.conf.db_name + + self.transaction_writes = 0 + self.auto_commit_on_many_writes = 0 + + self.password = password or xhiveframework.conf.db_password + self.value_cache = {} + self.logger = xhiveframework.logger("database") + self.logger.setLevel("WARNING") + + self.before_commit = CallbackManager() + self.after_commit = CallbackManager() + self.before_rollback = CallbackManager() + self.after_rollback = CallbackManager() + + # self.db_type: str + # self.last_query (lazy) attribute of last sql query executed + + def setup_type_map(self): + pass + + def connect(self): + """Connects to a database as set in `site_config.json`.""" + self.cur_db_name = self.user + self._conn: "MariadbConnection" | "PostgresConnection" = self.get_connection() + self._cursor: "MariadbCursor" | "PostgresCursor" = self._conn.cursor() + + try: + if execution_timeout := get_query_execution_timeout(): + self.set_execution_timeout(execution_timeout) + except Exception as e: + self.logger.warning(f"Couldn't set execution timeout {e}") + + def set_execution_timeout(self, seconds: int): + """Set session speicifc timeout on exeuction of statements. + If any statement takes more time it will be killed along with entire transaction.""" + raise NotImplementedError + + def use(self, db_name): + """`USE` db_name.""" + self._conn.select_db(db_name) + + def get_connection(self): + """Returns a Database connection object that conforms with https://peps.python.org/pep-0249/#connection-objects""" + raise NotImplementedError + + def get_database_size(self): + raise NotImplementedError + + def _transform_query(self, query: Query, values: QueryValues) -> tuple: + return query, values + + def _transform_result(self, result: list[tuple]) -> list[tuple]: + return result + + def _clean_up(self): + pass + + def sql( + self, + query: Query, + values: QueryValues = EmptyQueryValues, + *, + as_dict=0, + as_list=0, + debug=0, + ignore_ddl=0, + auto_commit=0, + update=None, + explain=False, + run=True, + pluck=False, + as_iterator=False, + ): + """Execute a SQL query and fetch all rows. + + :param query: SQL query. + :param values: Tuple / List / Dict of values to be escaped and substituted in the query. + :param as_dict: Return as a dictionary. + :param as_list: Always return as a list. + :param debug: Print query and `EXPLAIN` in debug log. + :param ignore_ddl: Catch exception if table, column missing. + :param auto_commit: Commit after executing the query. + :param update: Update this dict to all rows (if returned `as_dict`). + :param run: Return query without executing it if False. + :param pluck: Get the plucked field only. + :param explain: Print `EXPLAIN` in error log. + :param as_iterator: Returns iterator over results instead of fetching all results at once. + This should be used with unbuffered cursor as default cursors used by pymysql and postgres + buffer the results internally. See `Database.unbuffered_cursor`. + Examples: + + # return customer names as dicts + xhiveframework.db.sql("select name from tabCustomer", as_dict=True) + + # return names beginning with a + xhiveframework.db.sql("select name from tabCustomer where name like %s", "a%") + + # values as dict + xhiveframework.db.sql("select name from tabCustomer where name like %(name)s and owner=%(owner)s", + {"name": "a%", "owner":"test@example.com"}) + + """ + if isinstance(query, MySQLQueryBuilder | PostgreSQLQueryBuilder): + xhiveframework.log("Use run method to execute SQL queries generated by Query Engine") + + debug = debug or getattr(self, "debug", False) + query = str(query) + if not run: + return query + + # remove whitespace / indentation from start and end of query + query = query.strip() + + # replaces ifnull in query with coalesce + query = IFNULL_PATTERN.sub("coalesce(", query) + + if not self._conn: + self.connect() + + # in transaction validations + self.check_transaction_status(query) + self.clear_db_table_cache(query) + + if auto_commit: + self.commit() + + if debug: + time_start = time() + + if values == EmptyQueryValues: + values = None + elif not isinstance(values, tuple | dict | list): + values = (values,) + + query, values = self._transform_query(query, values) + + if trace_id := get_trace_id(): + query += f" /* XHIVEFRAMEWORK_TRACE_ID: {trace_id} */" + + try: + self._cursor.execute(query, values) + except Exception as e: + if self.is_syntax_error(e): + xhiveframework.log(f"Syntax error in query:\n{query} {values or ''}") + + elif self.is_deadlocked(e): + raise xhiveframework.QueryDeadlockError(e) from e + + elif self.is_timedout(e): + raise xhiveframework.QueryTimeoutError(e) from e + + elif self.is_read_only_mode_error(e): + xhiveframework.throw( + _( + "Site is running in read only mode for maintenance or site update, this action can not be performed right now. Please try again later." + ), + title=_("In Read Only Mode"), + exc=xhiveframework.InReadOnlyMode, + ) + + # TODO: added temporarily + elif self.db_type == "postgres": + traceback.print_stack() + xhiveframework.log(f"Error in query:\n{e}") + raise + + elif isinstance(e, self.ProgrammingError): + if xhiveframework.conf.developer_mode: + traceback.print_stack() + xhiveframework.log(f"Error in query:\n{query, values}") + raise + + if not ( + ignore_ddl + and (self.is_missing_column(e) or self.is_table_missing(e) or self.cant_drop_field_or_key(e)) + ): + raise + + if debug: + time_end = time() + xhiveframework.log(f"Execution time: {time_end - time_start:.2f} sec") + + self.log_query(query, values, debug, explain) + + if auto_commit: + self.commit() + + if not self._cursor.description: + return () + + if as_iterator: + return self._return_as_iterator(pluck=pluck, as_dict=as_dict, as_list=as_list, update=update) + + last_result = self._transform_result(self._cursor.fetchall()) + if pluck: + last_result = [r[0] for r in last_result] + self._clean_up() + return last_result + + # scrub output if required + if as_dict: + last_result = self.fetch_as_dict(last_result) + if update: + for r in last_result: + r.update(update) + + elif as_list: + last_result = self.convert_to_lists(last_result) + + self._clean_up() + return last_result + + def _return_as_iterator(self, *, pluck, as_dict, as_list, update): + while result := self._transform_result(self._cursor.fetchmany(SQL_ITERATOR_BATCH_SIZE)): + if pluck: + for row in result: + yield row[0] + + elif as_dict: + keys = [column[0] for column in self._cursor.description] + for row in result: + row = xhiveframework._dict(zip(keys, row, strict=False)) + if update: + row.update(update) + yield row + + elif as_list: + for row in result: + yield list(row) + else: + xhiveframework.throw(_("`as_iterator` only works with `as_list=True` or `as_dict=True`")) + + self._clean_up() + + def _log_query( + self, + mogrified_query: str, + debug: bool = False, + explain: bool = False, + unmogrified_query: str = "", + ) -> None: + """Takes the query and logs it to various interfaces according to the settings.""" + _query = None + + if xhiveframework.conf.allow_tests and xhiveframework.cache.get_value("flag_print_sql"): + _query = _query or str(mogrified_query) + print(_query) + + if debug: + _query = _query or str(mogrified_query) + if explain and is_query_type(_query, "select"): + self.explain_query(_query) + xhiveframework.log(_query) + + if xhiveframework.conf.logging == 2: + _query = _query or str(mogrified_query) + xhiveframework.log(f"#### query\n{_query}\n####") + + if unmogrified_query and is_query_type( + unmogrified_query, ("alter", "drop", "create", "truncate", "rename") + ): + _query = _query or str(mogrified_query) + self.logger.warning("DDL Query made to DB:\n" + _query) + + if xhiveframework.flags.in_migrate: + _query = _query or str(mogrified_query) + self.log_touched_tables(_query) + + def log_query( + self, query: str, values: QueryValues = None, debug: bool = False, explain: bool = False + ) -> str: + # TODO: Use mogrify until MariaDB Connector/C 1.1 is released and we can fetch something + # like cursor._transformed_statement from the cursor object. We can also avoid setting + # mogrified_query if we don't need to log it. + mogrified_query = self.lazy_mogrify(query, values) + self._log_query(mogrified_query, debug, explain, unmogrified_query=query) + return mogrified_query + + def mogrify(self, query: Query, values: QueryValues): + """build the query string with values""" + if not values: + return query + + try: + return self._cursor.mogrify(query, values) + except AttributeError: + if isinstance(values, dict): + return query % { + k: xhiveframework.db.escape(v) if isinstance(v, str) else v for k, v in values.items() + } + elif isinstance(values, list | tuple): + return query % tuple(xhiveframework.db.escape(v) if isinstance(v, str) else v for v in values) + return query, values + + def lazy_mogrify(self, query: Query, values: QueryValues) -> LazyMogrify: + """Wrap the object with str to generate mogrified query.""" + return LazyMogrify(query, values) + + def explain_query(self, query, values=None): + """Print `EXPLAIN` in error log.""" + xhiveframework.log("--- query explain ---") + try: + self._cursor.execute(f"EXPLAIN {query}", values) + except Exception as e: + xhiveframework.log(f"error in query explain: {e}") + else: + xhiveframework.log(json.dumps(self.fetch_as_dict(), indent=1)) + xhiveframework.log("--- query explain end ---") + + def sql_list(self, query, values=(), debug=False, **kwargs): + """Return data as list of single elements (first column). + + Example: + + # doctypes = ["DocType", "DocField", "User", ...] + doctypes = xhiveframework.db.sql_list("select name from DocType") + """ + return self.sql(query, values, **kwargs, debug=debug, pluck=True) + + def sql_ddl(self, query, debug=False): + """Commit and execute a query. DDL (Data Definition Language) queries that alter schema + autocommit in MariaDB.""" + self.commit() + self.sql(query, debug=debug) + + def check_transaction_status(self, query): + """Raises exception if more than 200,000 `INSERT`, `UPDATE` queries are + executed in one transaction. This is to ensure that writes are always flushed otherwise this + could cause the system to hang.""" + self.check_implicit_commit(query) + + if query and is_query_type(query, ("commit", "rollback")): + self.transaction_writes = 0 + + if query[:6].lower() in ("update", "insert", "delete"): + self.transaction_writes += 1 + if self.transaction_writes > self.MAX_WRITES_PER_TRANSACTION: + if self.auto_commit_on_many_writes: + self.commit() + else: + msg = "

    " + _("Too many changes to database in single action.") + "
    " + msg += _("The changes have been reverted.") + "
    " + raise xhiveframework.TooManyWritesError(msg) + + def check_implicit_commit(self, query): + if ( + self.transaction_writes + and query + and is_query_type(query, ("start", "alter", "drop", "create", "begin", "truncate")) + ): + raise ImplicitCommitError("This statement can cause implicit commit") + + def fetch_as_dict(self, result) -> list[xhiveframework._dict]: + """Internal. Convert results to dict.""" + if result: + keys = [column[0] for column in self._cursor.description] + + return [xhiveframework._dict(zip(keys, row, strict=False)) for row in result] + + @staticmethod + def clear_db_table_cache(query): + if query and is_query_type(query, ("drop", "create")): + xhiveframework.cache.delete_key("db_tables") + + def get_description(self): + """Returns result metadata.""" + return self._cursor.description + + @staticmethod + def convert_to_lists(res): + """Convert tuple output to lists (internal).""" + return [[value for value in row] for row in res] + + def get(self, doctype, filters=None, as_dict=True, cache=False): + """Returns `get_value` with fieldname='*'""" + return self.get_value(doctype, filters, "*", as_dict=as_dict, cache=cache) + + def get_value( + self, + doctype, + filters=None, + fieldname="name", + ignore=None, + as_dict=False, + debug=False, + order_by=DefaultOrderBy, + cache=False, + for_update=False, + *, + run=True, + pluck=False, + distinct=False, + skip_locked=False, + wait=True, + ): + """Returns a document property or list of properties. + + :param doctype: DocType name. + :param filters: Filters like `{"x":"y"}` or name of the document. `None` if Single DocType. + :param fieldname: Column name. + :param ignore: Don't raise exception if table, column is missing. + :param as_dict: Return values as dict. + :param debug: Print query in error log. + :param order_by: Column to order by + :param cache: Use cached results fetched during current job/request + :param pluck: pluck first column instead of returning as nested list or dict. + :param for_update: All the affected/read rows will be locked. + :param skip_locked: Skip selecting currently locked rows. + :param wait: Wait for aquiring lock + + Example: + + # return first customer starting with a + xhiveframework.db.get_value("Customer", {"name": ("like a%")}) + + # return last login of **User** `test@example.com` + xhiveframework.db.get_value("User", "test@example.com", "last_login") + + last_login, last_ip = xhiveframework.db.get_value("User", "test@example.com", + ["last_login", "last_ip"]) + + # returns default date_format + xhiveframework.db.get_value("System Settings", None, "date_format") + """ + + result = self.get_values( + doctype, + filters, + fieldname, + ignore, + as_dict, + debug, + order_by, + cache=cache, + for_update=for_update, + run=run, + pluck=pluck, + distinct=distinct, + limit=1, + skip_locked=skip_locked, + wait=wait, + ) + + if not run: + return result + + if not result: + return None + + row = result[0] + + if len(row) > 1 or as_dict: + return row + # single field is requested, send it without wrapping in containers + return row[0] + + def get_values( + self, + doctype, + filters=None, + fieldname="name", + ignore=None, + as_dict=False, + debug=False, + order_by=DefaultOrderBy, + update=None, + cache=False, + for_update=False, + *, + run=True, + pluck=False, + distinct=False, + limit=None, + skip_locked=False, + wait=True, + ): + """Returns multiple document properties. + + :param doctype: DocType name. + :param filters: Filters like `{"x":"y"}` or name of the document. + :param fieldname: Column name. + :param ignore: Don't raise exception if table, column is missing. + :param as_dict: Return values as dict. + :param debug: Print query in error log. + :param order_by: Column to order by, + :param distinct: Get Distinct results. + + Example: + + # return first customer starting with a + customers = xhiveframework.db.get_values("Customer", {"name": ("like a%")}) + + # return last login of **User** `test@example.com` + user = xhiveframework.db.get_values("User", "test@example.com", "*")[0] + """ + out = None + if cache and isinstance(filters, str) and (doctype, filters, fieldname) in self.value_cache: + return self.value_cache[(doctype, filters, fieldname)] + + if distinct: + order_by = None + + if isinstance(filters, list): + out = self._get_value_for_many_names( + doctype=doctype, + names=filters, + field=fieldname, + order_by=order_by, + debug=debug, + run=run, + pluck=pluck, + distinct=distinct, + limit=limit, + as_dict=as_dict, + skip_locked=skip_locked, + wait=True, + for_update=for_update, + ) + + else: + fields = fieldname + if fieldname != "*": + if isinstance(fieldname, str): + fields = [fieldname] + + if (filters is not None) and (filters != doctype or doctype == "DocType"): + try: + if order_by: + order_by = "modified" if order_by == DefaultOrderBy else order_by + out = self._get_values_from_table( + fields=fields, + filters=filters, + doctype=doctype, + as_dict=as_dict, + debug=debug, + order_by=order_by, + update=update, + run=run, + pluck=pluck, + distinct=distinct, + limit=limit, + for_update=for_update, + skip_locked=skip_locked, + wait=wait, + ) + except Exception as e: + if ignore and ( + xhiveframework.db.is_missing_column(e) + or xhiveframework.db.is_table_missing(e) + or str(e).startswith("Invalid DocType") + ): + out = None + elif (not ignore) and xhiveframework.db.is_table_missing(e): + # table not found, look in singles + out = self.get_values_from_single( + fields, filters, doctype, as_dict, debug, update, run=run, distinct=distinct + ) + + else: + raise + else: + out = self.get_values_from_single( + fields, filters, doctype, as_dict, debug, update, run=run, pluck=pluck, distinct=distinct + ) + + if cache and isinstance(filters, str): + self.value_cache[(doctype, filters, fieldname)] = out + + return out + + def get_values_from_single( + self, + fields, + filters, + doctype, + as_dict=False, + debug=False, + update=None, + *, + run=True, + pluck=False, + distinct=False, + ): + """Get values from `tabSingles` (Single DocTypes) (internal). + + :param fields: List of fields, + :param filters: Filters (dict). + :param doctype: DocType name. + """ + if fields == "*" or isinstance(filters, dict): + # check if single doc matches with filters + values = self.get_singles_dict(doctype) + if isinstance(filters, dict): + for key, value in filters.items(): + if values.get(key) != value: + return [] + + if as_dict: + return [values] if values else [] + + if isinstance(fields, list): + return [list(map(values.get, fields))] + + else: + r = xhiveframework.qb.get_query( + "Singles", + filters={"field": ("in", tuple(fields)), "doctype": doctype}, + fields=["field", "value"], + distinct=distinct, + ).run(pluck=pluck, debug=debug, as_dict=False) + + if not run: + return r + + if not r: + return [] + + r = xhiveframework._dict(r) + if update: + r.update(update) + + if not as_dict: + return [[r.get(field) for field in fields]] + + return [r] + + def get_singles_dict(self, doctype, debug=False, *, for_update=False, cast=False): + """Get Single DocType as dict. + + :param doctype: DocType of the single object whose value is requested + :param debug: Execute query in debug mode - print to STDOUT + :param for_update: Take `FOR UPDATE` lock on the records + :param cast: Cast values to Python data types based on field type + + Example: + + # Get coulmn and value of the single doctype Accounts Settings + account_settings = xhiveframework.db.get_singles_dict("Accounts Settings") + """ + queried_result = xhiveframework.qb.get_query( + "Singles", + filters={"doctype": doctype}, + fields=["field", "value"], + for_update=for_update, + ).run(debug=debug) + + if not cast: + return xhiveframework._dict(queried_result) + + try: + meta = xhiveframework.get_meta(doctype) + except DoesNotExistError: + return xhiveframework._dict(queried_result) + + return_value = xhiveframework._dict() + + for fieldname, value in queried_result: + if df := meta.get_field(fieldname): + casted_value = cast_fieldtype(df.fieldtype, value) + else: + casted_value = value + return_value[fieldname] = casted_value + + return return_value + + @staticmethod + def get_all(*args, **kwargs): + return xhiveframework.get_all(*args, **kwargs) + + @staticmethod + def get_list(*args, **kwargs): + return xhiveframework.get_list(*args, **kwargs) + + @staticmethod + def _get_update_dict( + fieldname: str | dict, value: Any, *, modified: str, modified_by: str, update_modified: bool + ) -> dict[str, Any]: + """Create update dict that represents column-values to be updated.""" + update_dict = fieldname if isinstance(fieldname, dict) else {fieldname: value} + + if update_modified: + modified = modified or now() + modified_by = modified_by or xhiveframework.session.user + update_dict.update({"modified": modified, "modified_by": modified_by}) + + return update_dict + + def set_single_value( + self, + doctype: str, + fieldname: str | dict, + value: str | int | None = None, + *, + modified=None, + modified_by=None, + update_modified=True, + debug=False, + ): + """Set field value of Single DocType. + + :param doctype: DocType of the single object + :param fieldname: `fieldname` of the property + :param value: `value` of the property + + Example: + + # Update the `deny_multiple_sessions` field in System Settings DocType. + xhiveframework.db.set_single_value("System Settings", "deny_multiple_sessions", True) + """ + + to_update = self._get_update_dict( + fieldname, value, modified=modified, modified_by=modified_by, update_modified=update_modified + ) + + xhiveframework.db.delete( + "Singles", filters={"field": ("in", tuple(to_update)), "doctype": doctype}, debug=debug + ) + + singles_data = ((doctype, key, sbool(value)) for key, value in to_update.items()) + xhiveframework.qb.into("Singles").columns("doctype", "field", "value").insert(*singles_data).run(debug=debug) + xhiveframework.clear_document_cache(doctype, doctype) + + if doctype in self.value_cache: + del self.value_cache[doctype] + + def get_single_value(self, doctype, fieldname, cache=True): + """Get property of Single DocType. Cache locally by default + + :param doctype: DocType of the single object whose value is requested + :param fieldname: `fieldname` of the property whose value is requested + + Example: + + # Get the default value of the company from the Global Defaults doctype. + company = xhiveframework.db.get_single_value('Global Defaults', 'default_company') + """ + + if doctype not in self.value_cache: + self.value_cache[doctype] = {} + + if cache and fieldname in self.value_cache[doctype]: + return self.value_cache[doctype][fieldname] + + val = xhiveframework.qb.get_query( + table="Singles", + filters={"doctype": doctype, "field": fieldname}, + fields="value", + ).run() + val = val[0][0] if val else None + + df = xhiveframework.get_meta(doctype).get_field(fieldname) + + if not df: + xhiveframework.throw( + _("Field {0} does not exist on {1}").format( + xhiveframework.bold(fieldname), xhiveframework.bold(doctype), self.InvalidColumnName + ) + ) + + val = cast_fieldtype(df.fieldtype, val) + + self.value_cache[doctype][fieldname] = val + + return val + + def get_singles_value(self, *args, **kwargs): + """Alias for get_single_value""" + return self.get_single_value(*args, **kwargs) + + def _get_values_from_table( + self, + fields, + filters, + doctype, + as_dict, + *, + debug=False, + order_by=None, + update=None, + for_update=False, + skip_locked=False, + wait=True, + run=True, + pluck=False, + distinct=False, + limit=None, + ): + query = xhiveframework.qb.get_query( + table=doctype, + filters=filters, + order_by=order_by, + for_update=for_update, + skip_locked=skip_locked, + wait=wait, + fields=fields, + distinct=distinct, + limit=limit, + validate_filters=True, + ) + if isinstance(fields, str) and fields == "*": + as_dict = True + + return query.run(as_dict=as_dict, debug=debug, update=update, run=run, pluck=pluck) + + def _get_value_for_many_names( + self, + doctype, + names, + field, + order_by, + *, + debug=False, + run=True, + pluck=False, + distinct=False, + limit=None, + as_dict=False, + for_update=False, + skip_locked=False, + wait=True, + ): + if names := list(filter(None, names)): + return xhiveframework.qb.get_query( + doctype, + fields=field, + filters=names, + order_by=order_by, + distinct=distinct, + limit=limit, + validate_filters=True, + for_update=for_update, + skip_locked=skip_locked, + wait=wait, + ).run(debug=debug, run=run, as_dict=as_dict, pluck=pluck) + return {} + + def set_value( + self, + dt, + dn, + field, + val=None, + modified=None, + modified_by=None, + update_modified=True, + debug=False, + ): + """Set a single value in the database, do not call the ORM triggers + but update the modified timestamp (unless specified not to). + + **Warning:** this function will not call Document events and should be avoided in normal cases. + + :param dt: DocType name. + :param dn: Document name for updating single record or filters for updating many records. + :param field: Property / field name or dictionary of values to be updated + :param value: Value to be updated. + :param modified: Use this as the `modified` timestamp. + :param modified_by: Set this user as `modified_by`. + :param update_modified: default True. Set as false, if you don't want to update the timestamp. + :param debug: Print the query in the developer / js console. + """ + from xhiveframework.model.utils import is_single_doctype + + if dn is None or dt == dn: + if not is_single_doctype(dt): + return + deprecation_warning( + "Calling db.set_value on single doctype is deprecated. This behaviour will be removed in future. Use db.set_single_value instead." + ) + self.set_single_value( + doctype=dt, + fieldname=field, + value=val, + debug=debug, + update_modified=update_modified, + modified=modified, + modified_by=modified_by, + ) + return + + to_update = self._get_update_dict( + field, val, modified=modified, modified_by=modified_by, update_modified=update_modified + ) + + query = xhiveframework.qb.get_query( + table=dt, + filters=dn, + update=True, + validate_filters=True, + ) + + if isinstance(dn, str): + xhiveframework.clear_document_cache(dt, dn) + else: + # No way to guess which documents are modified, clear all of them + xhiveframework.clear_document_cache(dt) + + for column, value in to_update.items(): + query = query.set(column, value) + + query.run(debug=debug) + + if dt in self.value_cache: + del self.value_cache[dt] + + def set_global(self, key, val, user="__global"): + """Save a global key value. Global values will be automatically set if they match fieldname.""" + self.set_default(key, val, user) + + def get_global(self, key, user="__global"): + """Returns a global key value.""" + return self.get_default(key, user) + + def get_default(self, key, parent="__default"): + """Returns default value as a list if multiple or single""" + d = self.get_defaults(key, parent) + return isinstance(d, list) and d[0] or d + + @staticmethod + def set_default(key, val, parent="__default", parenttype=None): + """Sets a global / user default value.""" + xhiveframework.defaults.set_default(key, val, parent, parenttype) + + @staticmethod + def add_default(key, val, parent="__default", parenttype=None): + """Append a default value for a key, there can be multiple default values for a particular key.""" + xhiveframework.defaults.add_default(key, val, parent, parenttype) + + @staticmethod + def get_defaults(key=None, parent="__default"): + """Get all defaults""" + defaults = xhiveframework.defaults.get_defaults_for(parent) + if not key: + return defaults + + if key in defaults: + return defaults[key] + + return defaults.get(xhiveframework.scrub(key)) + + def begin(self, *, read_only=False): + read_only = read_only or xhiveframework.flags.read_only + mode = "READ ONLY" if read_only else "" + self.sql(f"START TRANSACTION {mode}") + + def commit(self): + """Commit current transaction. Calls SQL `COMMIT`.""" + self.before_rollback.reset() + self.after_rollback.reset() + + self.before_commit.run() + + self.sql("commit") + self.begin() # explicitly start a new transaction + + self.after_commit.run() + + def rollback(self, *, save_point=None): + """`ROLLBACK` current transaction. Optionally rollback to a known save_point.""" + if save_point: + self.sql(f"rollback to savepoint {save_point}") + else: + self.before_commit.reset() + self.after_commit.reset() + + self.before_rollback.run() + + self.sql("rollback") + self.begin() + + self.after_rollback.run() + + def savepoint(self, save_point): + """Savepoints work as a nested transaction. + + Changes can be undone to a save point by doing xhiveframework.db.rollback(save_point) + + Note: rollback watchers can not work with save points. + so only changes to database are undone when rolling back to a savepoint. + Avoid using savepoints when writing to filesystem.""" + self.sql(f"savepoint {save_point}") + + def release_savepoint(self, save_point): + self.sql(f"release savepoint {save_point}") + + def field_exists(self, dt, fn): + """Return true of field exists.""" + return self.exists("DocField", {"fieldname": fn, "parent": dt}) + + def table_exists(self, doctype, cached=True): + """Returns True if table for given doctype exists.""" + return f"tab{doctype}" in self.get_tables(cached=cached) + + def has_table(self, doctype): + return self.table_exists(doctype) + + def get_tables(self, cached=True): + raise NotImplementedError + + def a_row_exists(self, doctype): + """Returns True if atleast one row exists.""" + return xhiveframework.get_all(doctype, limit=1, order_by=None, as_list=True) + + def exists(self, dt, dn=None, cache=False): + """Return the document name of a matching document, or None. + + Note: `cache` only works if `dt` and `dn` are of type `str`. + + ## Examples + + Pass doctype and docname (only in this case we can cache the result) + + ``` + exists("User", "jane@example.org", cache=True) + ``` + + Pass a dict of filters including the `"doctype"` key: + + ``` + exists({"doctype": "User", "full_name": "Jane Doe"}) + ``` + + Pass the doctype and a dict of filters: + + ``` + exists("User", {"full_name": "Jane Doe"}) + ``` + """ + if dt != "DocType" and dt == dn: + # single always exists (!) + return dn + + if isinstance(dt, dict): + dt = dt.copy() # don't modify the original dict + dt, dn = dt.pop("doctype"), dt + + return self.get_value(dt, dn, ignore=True, cache=cache, order_by=None) + + def count(self, dt, filters=None, debug=False, cache=False, distinct: bool = True): + """Returns `COUNT(*)` for given DocType and filters.""" + if cache and not filters: + cache_count = xhiveframework.cache.get_value(f"doctype:count:{dt}") + if cache_count is not None: + return cache_count + count = xhiveframework.qb.get_query( + table=dt, + filters=filters, + fields=Count("*"), + distinct=distinct, + validate_filters=True, + ).run(debug=debug)[0][0] + if not filters and cache: + xhiveframework.cache.set_value(f"doctype:count:{dt}", count, expires_in_sec=86400) + return count + + @staticmethod + def format_date(date): + return getdate(date).strftime("%Y-%m-%d") + + @staticmethod + def format_datetime(datetime): # noqa: F811 + if not datetime: + return FallBackDateTimeStr + + return get_datetime(datetime).strftime("%Y-%m-%d %H:%M:%S.%f") + + def get_creation_count(self, doctype, minutes): + """Get count of records created in the last x minutes""" + from dateutil.relativedelta import relativedelta + + from xhiveframework.utils import now_datetime + + Table = xhiveframework.qb.DocType(doctype) + + return ( + xhiveframework.qb.from_(Table) + .select(Count(Table.name)) + .where(Table.creation >= now_datetime() - relativedelta(minutes=minutes)) + .run()[0][0] + ) + + def get_db_table_columns(self, table) -> list[str]: + """Returns list of column names from given table.""" + columns = xhiveframework.cache.hget("table_columns", table) + if columns is None: + information_schema = xhiveframework.qb.Schema("information_schema") + + columns = ( + xhiveframework.qb.from_(information_schema.columns) + .select(information_schema.columns.column_name) + .where(information_schema.columns.table_name == table) + .run(pluck=True) + ) + + if columns: + xhiveframework.cache.hset("table_columns", table, columns) + + return columns + + def get_table_columns(self, doctype): + """Returns list of column names from given doctype.""" + columns = self.get_db_table_columns("tab" + doctype) + if not columns: + raise self.TableMissingError("DocType", doctype) + return columns + + def has_column(self, doctype, column): + """Returns True if column exists in database.""" + return column in self.get_table_columns(doctype) + + def has_index(self, table_name, index_name): + raise NotImplementedError + + def add_index(self, doctype, fields, index_name=None): + raise NotImplementedError + + def add_unique(self, doctype, fields, constraint_name=None): + raise NotImplementedError + + @staticmethod + def get_index_name(fields): + index_name = "_".join(fields) + "_index" + # remove index length if present e.g. (10) from index name + return INDEX_PATTERN.sub(r"", index_name) + + def get_system_setting(self, key): + return xhiveframework.get_system_settings(key) + + def close(self): + """Close database connection.""" + if self._conn: + self._conn.close() + self._cursor = None + self._conn = None + + @staticmethod + def escape(s, percent=True): + """Excape quotes and percent in given string.""" + # implemented in specific class + raise NotImplementedError + + @staticmethod + def is_column_missing(e): + return xhiveframework.db.is_missing_column(e) + + def get_descendants(self, doctype, name): + """Return descendants of the group node in tree""" + from xhiveframework.utils.nestedset import get_descendants_of + + try: + return get_descendants_of(doctype, name, ignore_permissions=True) + except Exception: + # Can only happen if document doesn't exists - kept for backward compatibility + return [] + + def is_missing_table_or_column(self, e): + return self.is_missing_column(e) or self.is_table_missing(e) + + def multisql(self, sql_dict, values=(), **kwargs): + current_dialect = self.db_type or "mariadb" + query = sql_dict.get(current_dialect) + return self.sql(query, values, **kwargs) + + def delete(self, doctype: str, filters: dict | list | None = None, debug=False, **kwargs): + """Delete rows from a table in site which match the passed filters. This + does trigger DocType hooks. Simply runs a DELETE query in the database. + + Doctype name can be passed directly, it will be pre-pended with `tab`. + """ + filters = filters or kwargs.get("conditions") + query = xhiveframework.qb.get_query( + table=doctype, + filters=filters, + delete=True, + validate_filters=True, + ) + if "debug" not in kwargs: + kwargs["debug"] = debug + return query.run(**kwargs) + + def truncate(self, doctype: str): + """Truncate a table in the database. This runs a DDL command `TRUNCATE TABLE`. + This cannot be rolled back. + + Doctype name can be passed directly, it will be pre-pended with `tab`. + """ + return self.sql_ddl(f"truncate `{get_table_name(doctype)}`") + + def get_last_created(self, doctype): + last_record = self.get_all(doctype, ("creation"), limit=1, order_by="creation desc") + if last_record: + return get_datetime(last_record[0].creation) + else: + return None + + def log_touched_tables(self, query): + if is_query_type(query, ("insert", "delete", "update", "alter", "drop", "rename")): + # single_word_regex is designed to match following patterns + # `tabXxx`, tabXxx and "tabXxx" + + # multi_word_regex is designed to match following patterns + # `tabXxx Xxx` and "tabXxx Xxx" + + # ([`"]?) Captures " or ` at the beginning of the table name (if provided) + # \1 matches the first captured group (quote character) at the end of the table name + # multi word table name must have surrounding quotes. + + # (tab([A-Z]\w+)( [A-Z]\w+)*) Captures table names that start with "tab" + # and are continued with multiple words that start with a captital letter + # e.g. 'tabXxx' or 'tabXxx Xxx' or 'tabXxx Xxx Xxx' and so on + + tables = [] + for regex in (SINGLE_WORD_PATTERN, MULTI_WORD_PATTERN): + tables += [groups[1] for groups in regex.findall(query)] + + if xhiveframework.flags.touched_tables is None: + xhiveframework.flags.touched_tables = set() + xhiveframework.flags.touched_tables.update(tables) + + def bulk_insert( + self, + doctype: str, + fields: list[str], + values: Iterable[Sequence[Any]], + ignore_duplicates=False, + *, + chunk_size=10_000, + ): + """ + Insert multiple records at a time + + :param doctype: Doctype name + :param fields: list of fields + :params values: iterable of values + """ + table = xhiveframework.qb.DocType(doctype) + + query = xhiveframework.qb.into(table).columns(fields) + + if ignore_duplicates: + # Pypika does not have same api for ignoring duplicates + if xhiveframework.conf.db_type == "mariadb": + query = query.ignore() + elif xhiveframework.conf.db_type == "postgres": + query = query.on_conflict().do_nothing() + + value_iterator = iter(values) + while value_chunk := tuple(itertools.islice(value_iterator, chunk_size)): + query.insert(*value_chunk).run() + + def create_sequence(self, *args, **kwargs): + from xhiveframework.database.sequence import create_sequence + + return create_sequence(*args, **kwargs) + + def set_next_sequence_val(self, *args, **kwargs): + from xhiveframework.database.sequence import set_next_val + + set_next_val(*args, **kwargs) + + def get_next_sequence_val(self, *args, **kwargs): + from xhiveframework.database.sequence import get_next_val + + return get_next_val(*args, **kwargs) + + def get_row_size(self, doctype: str) -> int: + """Get estimated max row size of any table in bytes.""" + raise NotImplementedError + + def rename_column(self, doctype: str, old_column_name: str, new_column_name: str): + raise NotImplementedError + + @contextmanager + def unbuffered_cursor(self): + """Context manager to temporarily use unbuffered cursor. + + Using this with `as_iterator=True` provides O(1) memory usage while reading large result sets. + + NOTE: You MUST do entire result set processing in the context, otherwise underlying cursor + will be switched and you'll not get complete results. + + Usage: + with xhiveframework.db.unbuffered_cursor(): + for row in xhiveframework.db.sql("query with huge result", as_iterator=True): + continue # Do some processing. + """ + raise NotImplementedError + + +@contextmanager +def savepoint(catch: type | tuple[type, ...] = Exception): + """Wrapper for wrapping blocks of DB operations in a savepoint. + + as contextmanager: + + for doc in docs: + with savepoint(catch=DuplicateError): + doc.insert() + + as decorator (wraps FULL function call): + + @savepoint(catch=DuplicateError) + def process_doc(doc): + doc.insert() + """ + try: + savepoint = "".join(random.sample(string.ascii_lowercase, 10)) + xhiveframework.db.savepoint(savepoint) + yield # control back to calling function + except catch: + xhiveframework.db.rollback(save_point=savepoint) + else: + xhiveframework.db.release_savepoint(savepoint) + + +def get_query_execution_timeout() -> int: + """Get execution timeout based on current timeout in different contexts. + + HTTP requests: HTTP timeout or a default (300) + Background jobs: Job timeout + Console/Commands: No timeout = 0. + + Note: Timeout adds 1.5x as "safety factor" + """ + from rq import get_current_job + + if not xhiveframework.conf.get("enable_db_statement_timeout"): + return 0 + + # Zero means no timeout, which is the default value in db. + timeout = 0 + with suppress(Exception): + if getattr(xhiveframework.local, "request", None): + timeout = xhiveframework.conf.http_timeout or 300 + elif job := get_current_job(): + timeout = job.timeout + + return int(cint(timeout) * 1.5) diff --git a/xhiveframework/database/db_manager.py b/xhiveframework/database/db_manager.py new file mode 100644 index 0000000..47a7db4 --- /dev/null +++ b/xhiveframework/database/db_manager.py @@ -0,0 +1,88 @@ +import xhiveframework +from xhiveframework import _ + + +class DbManager: + def __init__(self, db): + """ + Pass root_conn here for access to all databases. + """ + if db: + self.db = db + + def get_current_host(self): + return self.db.sql("select user()")[0][0].split("@")[1] + + def create_user(self, user, password, host=None): + host = host or self.get_current_host() + password_predicate = f" IDENTIFIED BY '{password}'" if password else "" + self.db.sql(f"CREATE USER '{user}'@'{host}'{password_predicate}") + + def delete_user(self, target, host=None): + host = host or self.get_current_host() + self.db.sql(f"DROP USER IF EXISTS '{target}'@'{host}'") + + def create_database(self, target): + if target in self.get_database_list(): + self.drop_database(target) + self.db.sql(f"CREATE DATABASE `{target}`") + + def drop_database(self, target): + self.db.sql_ddl(f"DROP DATABASE IF EXISTS `{target}`") + + def grant_all_privileges(self, target, user, host=None): + host = host or self.get_current_host() + permissions = ( + ( + "SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, " + "CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW, " + "CREATE ROUTINE, ALTER ROUTINE, EXECUTE, LOCK TABLES" + ) + if xhiveframework.conf.rds_db + else "ALL PRIVILEGES" + ) + self.db.sql(f"GRANT {permissions} ON `{target}`.* TO '{user}'@'{host}'") + + def flush_privileges(self): + self.db.sql("FLUSH PRIVILEGES") + + def get_database_list(self): + return self.db.sql("SHOW DATABASES", pluck=True) + + @staticmethod + def restore_database(verbose, target, source, user, password): + import shlex + from shutil import which + + from xhiveframework.database import get_command + from xhiveframework.utils import execute_in_shell + + command = ["set -o pipefail;"] + + if source.endswith(".gz"): + if gzip := which("gzip"): + command.extend([gzip, "-cd", source, "|"]) + source = [] + else: + raise Exception("`gzip` not installed") + + else: + source = ["<", source] + + bin, args, bin_name = get_command( + host=xhiveframework.conf.db_host, + port=xhiveframework.conf.db_port, + user=user, + password=password, + db_name=target, + ) + if not bin: + xhiveframework.throw( + _("{} not found in PATH! This is required to restore the database.").format(bin_name), + exc=xhiveframework.ExecutableNotFound, + ) + command.append(bin) + command.append(shlex.join(args)) + command.extend(source) + execute_in_shell(" ".join(command), check_exit_code=True, verbose=verbose) + xhiveframework.cache.delete_keys("") # Delete all keys associated with this site. diff --git a/xhiveframework/database/mariadb/__init__.py b/xhiveframework/database/mariadb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/database/mariadb/database.py b/xhiveframework/database/mariadb/database.py new file mode 100644 index 0000000..58c8b55 --- /dev/null +++ b/xhiveframework/database/mariadb/database.py @@ -0,0 +1,529 @@ +import re +from contextlib import contextmanager + +import pymysql +from pymysql.constants import ER, FIELD_TYPE +from pymysql.converters import conversions, escape_string + +import xhiveframework +from xhiveframework.database.database import Database +from xhiveframework.database.mariadb.schema import MariaDBTable +from xhiveframework.utils import UnicodeWithAttrs, cstr, get_datetime, get_table_name + +_PARAM_COMP = re.compile(r"%\([\w]*\)s") + + +class MariaDBExceptionUtil: + ProgrammingError = pymysql.ProgrammingError + TableMissingError = pymysql.ProgrammingError + OperationalError = pymysql.OperationalError + InternalError = pymysql.InternalError + SQLError = pymysql.ProgrammingError + DataError = pymysql.DataError + + # match ER_SEQUENCE_RUN_OUT - https://mariadb.com/kb/en/mariadb-error-codes/ + SequenceGeneratorLimitExceeded = pymysql.OperationalError + SequenceGeneratorLimitExceeded.errno = 4084 + + @staticmethod + def is_deadlocked(e: pymysql.Error) -> bool: + return e.args[0] == ER.LOCK_DEADLOCK + + @staticmethod + def is_timedout(e: pymysql.Error) -> bool: + return e.args[0] == ER.LOCK_WAIT_TIMEOUT + + @staticmethod + def is_read_only_mode_error(e: pymysql.Error) -> bool: + return e.args[0] == 1792 + + @staticmethod + def is_table_missing(e: pymysql.Error) -> bool: + return e.args[0] == ER.NO_SUCH_TABLE + + @staticmethod + def is_missing_table(e: pymysql.Error) -> bool: + return MariaDBDatabase.is_table_missing(e) + + @staticmethod + def is_missing_column(e: pymysql.Error) -> bool: + return e.args[0] == ER.BAD_FIELD_ERROR + + @staticmethod + def is_duplicate_fieldname(e: pymysql.Error) -> bool: + return e.args[0] == ER.DUP_FIELDNAME + + @staticmethod + def is_duplicate_entry(e: pymysql.Error) -> bool: + return e.args[0] == ER.DUP_ENTRY + + @staticmethod + def is_access_denied(e: pymysql.Error) -> bool: + return e.args[0] == ER.ACCESS_DENIED_ERROR + + @staticmethod + def cant_drop_field_or_key(e: pymysql.Error) -> bool: + return e.args[0] == ER.CANT_DROP_FIELD_OR_KEY + + @staticmethod + def is_syntax_error(e: pymysql.Error) -> bool: + return e.args[0] == ER.PARSE_ERROR + + @staticmethod + def is_statement_timeout(e: pymysql.Error) -> bool: + return e.args[0] == 1969 + + @staticmethod + def is_data_too_long(e: pymysql.Error) -> bool: + return e.args[0] == ER.DATA_TOO_LONG + + @staticmethod + def is_db_table_size_limit(e: pymysql.Error) -> bool: + return e.args[0] == ER.TOO_BIG_ROWSIZE + + @staticmethod + def is_primary_key_violation(e: pymysql.Error) -> bool: + return ( + MariaDBDatabase.is_duplicate_entry(e) + and "PRIMARY" in cstr(e.args[1]) + and isinstance(e, pymysql.IntegrityError) + ) + + @staticmethod + def is_unique_key_violation(e: pymysql.Error) -> bool: + return ( + MariaDBDatabase.is_duplicate_entry(e) + and "Duplicate" in cstr(e.args[1]) + and isinstance(e, pymysql.IntegrityError) + ) + + +class MariaDBConnectionUtil: + def get_connection(self): + conn = self._get_connection() + conn.auto_reconnect = True + return conn + + def _get_connection(self): + """Return MariaDB connection object.""" + return self.create_connection() + + def create_connection(self): + return pymysql.connect(**self.get_connection_settings()) + + def set_execution_timeout(self, seconds: int): + self.sql("set session max_statement_time = %s", int(seconds)) + + def get_connection_settings(self) -> dict: + conn_settings = { + "host": self.host, + "user": self.user, + "password": self.password, + "conv": self.CONVERSION_MAP, + "charset": "utf8mb4", + "use_unicode": True, + } + + if self.user not in (xhiveframework.flags.root_login, "root"): + conn_settings["database"] = self.user + + if self.port: + conn_settings["port"] = int(self.port) + + if xhiveframework.conf.local_infile: + conn_settings["local_infile"] = xhiveframework.conf.local_infile + + if xhiveframework.conf.db_ssl_ca and xhiveframework.conf.db_ssl_cert and xhiveframework.conf.db_ssl_key: + conn_settings["ssl"] = { + "ca": xhiveframework.conf.db_ssl_ca, + "cert": xhiveframework.conf.db_ssl_cert, + "key": xhiveframework.conf.db_ssl_key, + } + return conn_settings + + +class MariaDBDatabase(MariaDBConnectionUtil, MariaDBExceptionUtil, Database): + REGEX_CHARACTER = "regexp" + CONVERSION_MAP = conversions | { + FIELD_TYPE.NEWDECIMAL: float, + FIELD_TYPE.DATETIME: get_datetime, + UnicodeWithAttrs: escape_string, + } + default_port = "3306" + MAX_ROW_SIZE_LIMIT = 65_535 # bytes + + def setup_type_map(self): + self.db_type = "mariadb" + self.type_map = { + "Currency": ("decimal", "21,9"), + "Int": ("int", "11"), + "Long Int": ("bigint", "20"), + "Float": ("decimal", "21,9"), + "Percent": ("decimal", "21,9"), + "Check": ("int", "1"), + "Small Text": ("text", ""), + "Long Text": ("longtext", ""), + "Code": ("longtext", ""), + "Text Editor": ("longtext", ""), + "Markdown Editor": ("longtext", ""), + "HTML Editor": ("longtext", ""), + "Date": ("date", ""), + "Datetime": ("datetime", "6"), + "Time": ("time", "6"), + "Text": ("text", ""), + "Data": ("varchar", self.VARCHAR_LEN), + "Link": ("varchar", self.VARCHAR_LEN), + "Dynamic Link": ("varchar", self.VARCHAR_LEN), + "Password": ("text", ""), + "Select": ("varchar", self.VARCHAR_LEN), + "Rating": ("decimal", "3,2"), + "Read Only": ("varchar", self.VARCHAR_LEN), + "Attach": ("text", ""), + "Attach Image": ("text", ""), + "Signature": ("longtext", ""), + "Color": ("varchar", self.VARCHAR_LEN), + "Barcode": ("longtext", ""), + "Geolocation": ("longtext", ""), + "Duration": ("decimal", "21,9"), + "Icon": ("varchar", self.VARCHAR_LEN), + "Phone": ("varchar", self.VARCHAR_LEN), + "Autocomplete": ("varchar", self.VARCHAR_LEN), + "JSON": ("json", ""), + } + + def get_database_size(self): + """'Returns database size in MB""" + db_size = self.sql( + """ + SELECT `table_schema` as `database_name`, + SUM(`data_length` + `index_length`) / 1024 / 1024 AS `database_size` + FROM information_schema.tables WHERE `table_schema` = %s GROUP BY `table_schema` + """, + self.db_name, + as_dict=True, + ) + + return db_size[0].get("database_size") + + def log_query(self, query, values, debug, explain): + self.last_query = self._cursor._executed + self._log_query(self.last_query, debug, explain, query) + return self.last_query + + def _clean_up(self): + # PERF: Erase internal references of pymysql to trigger GC as soon as + # results are consumed. + self._cursor._result = None + self._cursor._rows = None + self._cursor.connection._result = None + + @staticmethod + def escape(s, percent=True): + """Excape quotes and percent in given string.""" + # Update: We've scrapped PyMySQL in favour of MariaDB's official Python client + # Also, given we're promoting use of the PyPika builder via xhiveframework.qb, the use + # of this method should be limited. + + # pymysql expects unicode argument to escape_string with Python 3 + s = xhiveframework.as_unicode(escape_string(xhiveframework.as_unicode(s)), "utf-8").replace("`", "\\`") + + # NOTE separating % escape, because % escape should only be done when using LIKE operator + # or when you use python format string to generate query that already has a %s + # for example: sql("select name from `tabUser` where name=%s and {0}".format(conditions), something) + # defaulting it to True, as this is the most frequent use case + # ideally we shouldn't have to use ESCAPE and strive to pass values via the values argument of sql + if percent: + s = s.replace("%", "%%") + + return "'" + s + "'" + + # column type + @staticmethod + def is_type_number(code): + return code == pymysql.NUMBER + + @staticmethod + def is_type_datetime(code): + return code == pymysql.DATETIME + + def rename_table(self, old_name: str, new_name: str) -> list | tuple: + old_name = get_table_name(old_name) + new_name = get_table_name(new_name) + return self.sql(f"RENAME TABLE `{old_name}` TO `{new_name}`") + + def describe(self, doctype: str) -> list | tuple: + table_name = get_table_name(doctype) + return self.sql(f"DESC `{table_name}`") + + def change_column_type( + self, doctype: str, column: str, type: str, nullable: bool = False + ) -> list | tuple: + table_name = get_table_name(doctype) + null_constraint = "NOT NULL" if not nullable else "" + return self.sql_ddl(f"ALTER TABLE `{table_name}` MODIFY `{column}` {type} {null_constraint}") + + def rename_column(self, doctype: str, old_column_name, new_column_name): + current_data_type = self.get_column_type(doctype, old_column_name) + + table_name = get_table_name(doctype) + + xhiveframework.db.sql_ddl( + f"""ALTER TABLE `{table_name}` + CHANGE COLUMN `{old_column_name}` + `{new_column_name}` + {current_data_type}""" + # ^ Mariadb requires passing current data type again even if there's no change + # This requirement is gone from v10.5 + ) + + def create_auth_table(self): + self.sql_ddl( + """create table if not exists `__Auth` ( + `doctype` VARCHAR(140) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `fieldname` VARCHAR(140) NOT NULL, + `password` TEXT NOT NULL, + `encrypted` INT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`doctype`, `name`, `fieldname`) + ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci""" + ) + + def create_global_search_table(self): + if "__global_search" not in self.get_tables(): + self.sql( + f"""create table __global_search( + doctype varchar(100), + name varchar({self.VARCHAR_LEN}), + title varchar({self.VARCHAR_LEN}), + content text, + fulltext(content), + route varchar({self.VARCHAR_LEN}), + published int(1) not null default 0, + unique `doctype_name` (doctype, name)) + COLLATE=utf8mb4_unicode_ci + ENGINE=MyISAM + CHARACTER SET=utf8mb4""" + ) + + def create_user_settings_table(self): + self.sql_ddl( + """create table if not exists __UserSettings ( + `user` VARCHAR(180) NOT NULL, + `doctype` VARCHAR(180) NOT NULL, + `data` TEXT, + UNIQUE(user, doctype) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8""" + ) + + @staticmethod + def get_on_duplicate_update(key=None): + return "ON DUPLICATE key UPDATE " + + def get_table_columns_description(self, table_name): + """Returns list of column and its description""" + return self.sql( + f"""select + column_name as 'name', + column_type as 'type', + column_default as 'default', + COALESCE( + (select 1 + from information_schema.statistics + where table_name="{table_name}" + and column_name=columns.column_name + and NON_UNIQUE=1 + and Seq_in_index = 1 + limit 1 + ), 0) as 'index', + column_key = 'UNI' as 'unique' + from information_schema.columns as columns + where table_name = '{table_name}' """, + as_dict=1, + ) + + def get_column_type(self, doctype, column): + """Returns column type from database.""" + information_schema = xhiveframework.qb.Schema("information_schema") + table = get_table_name(doctype) + + return ( + xhiveframework.qb.from_(information_schema.columns) + .select(information_schema.columns.column_type) + .where( + (information_schema.columns.table_name == table) + & (information_schema.columns.column_name == column) + ) + .run(pluck=True)[0] + ) + + def has_index(self, table_name, index_name): + return self.sql( + f"""SHOW INDEX FROM `{table_name}` + WHERE Key_name='{index_name}'""" + ) + + def get_column_index(self, table_name: str, fieldname: str, unique: bool = False) -> xhiveframework._dict | None: + """Check if column exists for a specific fields in specified order. + + This differs from db.has_index because it doesn't rely on index name but columns inside an + index. + """ + + indexes = self.sql( + f"""SHOW INDEX FROM `{table_name}` + WHERE Column_name = "{fieldname}" + AND Seq_in_index = 1 + AND Non_unique={int(not unique)} + AND Index_type != 'FULLTEXT' + """, + as_dict=True, + ) + + # Same index can be part of clustered index which contains more fields + # We don't want those. + for index in indexes: + clustered_index = self.sql( + f"""SHOW INDEX FROM `{table_name}` + WHERE Key_name = "{index.Key_name}" + AND Seq_in_index = 2 + """, + as_dict=True, + ) + if not clustered_index: + return index + + def add_index(self, doctype: str, fields: list, index_name: str | None = None): + """Creates an index with given fields if not already created. + Index name will be `fieldname1_fieldname2_index`""" + index_name = index_name or self.get_index_name(fields) + table_name = get_table_name(doctype) + if not self.has_index(table_name, index_name): + self.commit() + self.sql( + """ALTER TABLE `{}` + ADD INDEX `{}`({})""".format(table_name, index_name, ", ".join(fields)) + ) + + def add_unique(self, doctype, fields, constraint_name=None): + if isinstance(fields, str): + fields = [fields] + if not constraint_name: + constraint_name = "unique_" + "_".join(fields) + + if not self.sql( + """select CONSTRAINT_NAME from information_schema.TABLE_CONSTRAINTS + where table_name=%s and constraint_type='UNIQUE' and CONSTRAINT_NAME=%s""", + ("tab" + doctype, constraint_name), + ): + self.commit() + self.sql( + """alter table `tab{}` + add unique `{}`({})""".format(doctype, constraint_name, ", ".join(fields)) + ) + + def updatedb(self, doctype, meta=None): + """ + Syncs a `DocType` to the table + * creates if required + * updates columns + * updates indices + """ + res = self.sql("select issingle from `tabDocType` where name=%s", (doctype,)) + if not res: + raise Exception(f"Wrong doctype {doctype} in updatedb") + + if not res[0][0]: + db_table = MariaDBTable(doctype, meta) + db_table.validate() + + db_table.sync() + self.commit() + + def get_database_list(self): + return self.sql("SHOW DATABASES", pluck=True) + + def get_tables(self, cached=True): + """Returns list of tables""" + to_query = not cached + + if cached: + tables = xhiveframework.cache.get_value("db_tables") + to_query = not tables + + if to_query: + information_schema = xhiveframework.qb.Schema("information_schema") + + tables = ( + xhiveframework.qb.from_(information_schema.tables) + .select(information_schema.tables.table_name) + .where(information_schema.tables.table_schema != "information_schema") + .run(pluck=True) + ) + xhiveframework.cache.set_value("db_tables", tables) + + return tables + + def get_row_size(self, doctype: str) -> int: + """Get estimated max row size of any table in bytes.""" + + # Query reused from this answer: https://dba.stackexchange.com/a/313889/274503 + # Modification: get values for particular table instead of full summary. + # Reference: https://mariadb.com/kb/en/data-type-storage-requirements/ + + est_row_size = xhiveframework.db.sql( + """ + SELECT SUM(col_sizes.col_size) AS EST_MAX_ROW_SIZE + FROM ( + SELECT + cols.COLUMN_NAME, + CASE cols.DATA_TYPE + WHEN 'tinyint' THEN 1 + WHEN 'smallint' THEN 2 + WHEN 'mediumint' THEN 3 + WHEN 'int' THEN 4 + WHEN 'bigint' THEN 8 + WHEN 'float' THEN IF(cols.NUMERIC_PRECISION > 24, 8, 4) + WHEN 'double' THEN 8 + WHEN 'decimal' THEN ((cols.NUMERIC_PRECISION - cols.NUMERIC_SCALE) DIV 9)*4 + (cols.NUMERIC_SCALE DIV 9)*4 + CEIL(MOD(cols.NUMERIC_PRECISION - cols.NUMERIC_SCALE,9)/2) + CEIL(MOD(cols.NUMERIC_SCALE,9)/2) + WHEN 'bit' THEN (cols.NUMERIC_PRECISION + 7) DIV 8 + WHEN 'year' THEN 1 + WHEN 'date' THEN 3 + WHEN 'time' THEN 3 + CEIL(cols.DATETIME_PRECISION /2) + WHEN 'datetime' THEN 5 + CEIL(cols.DATETIME_PRECISION /2) + WHEN 'timestamp' THEN 4 + CEIL(cols.DATETIME_PRECISION /2) + WHEN 'char' THEN cols.CHARACTER_OCTET_LENGTH + WHEN 'binary' THEN cols.CHARACTER_OCTET_LENGTH + WHEN 'varchar' THEN IF(cols.CHARACTER_OCTET_LENGTH > 255, 2, 1) + cols.CHARACTER_OCTET_LENGTH + WHEN 'varbinary' THEN IF(cols.CHARACTER_OCTET_LENGTH > 255, 2, 1) + cols.CHARACTER_OCTET_LENGTH + WHEN 'tinyblob' THEN 9 + WHEN 'tinytext' THEN 9 + WHEN 'blob' THEN 10 + WHEN 'text' THEN 10 + WHEN 'mediumblob' THEN 11 + WHEN 'mediumtext' THEN 11 + WHEN 'longblob' THEN 12 + WHEN 'longtext' THEN 12 + WHEN 'enum' THEN 2 + WHEN 'set' THEN 8 + ELSE 0 + END AS col_size + FROM INFORMATION_SCHEMA.COLUMNS cols + WHERE cols.TABLE_NAME = %s + ) AS col_sizes;""", + (get_table_name(doctype),), + ) + + if est_row_size: + return int(est_row_size[0][0]) + + @contextmanager + def unbuffered_cursor(self): + from pymysql.cursors import SSCursor + + try: + original_cursor = self._cursor + new_cursor = self._cursor = self._conn.cursor(SSCursor) + yield + finally: + self._cursor = original_cursor + new_cursor.close() diff --git a/xhiveframework/database/mariadb/framework_mariadb.sql b/xhiveframework/database/mariadb/framework_mariadb.sql new file mode 100644 index 0000000..91ef696 --- /dev/null +++ b/xhiveframework/database/mariadb/framework_mariadb.sql @@ -0,0 +1,338 @@ +-- Core Elements to install WNFramework +-- To be called from install.py + + +-- +-- Table structure for table `tabDocField` +-- + +DROP TABLE IF EXISTS `tabDocField`; +CREATE TABLE `tabDocField` ( + `name` varchar(255) NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `fieldname` varchar(255) DEFAULT NULL, + `label` varchar(255) DEFAULT NULL, + `oldfieldname` varchar(255) DEFAULT NULL, + `fieldtype` varchar(255) DEFAULT NULL, + `oldfieldtype` varchar(255) DEFAULT NULL, + `options` text, + `search_index` int(1) NOT NULL DEFAULT 0, + `show_dashboard` int(1) NOT NULL DEFAULT 0, + `hidden` int(1) NOT NULL DEFAULT 0, + `set_only_once` int(1) NOT NULL DEFAULT 0, + `allow_in_quick_entry` int(1) NOT NULL DEFAULT 0, + `print_hide` int(1) NOT NULL DEFAULT 0, + `report_hide` int(1) NOT NULL DEFAULT 0, + `reqd` int(1) NOT NULL DEFAULT 0, + `bold` int(1) NOT NULL DEFAULT 0, + `in_global_search` int(1) NOT NULL DEFAULT 0, + `collapsible` int(1) NOT NULL DEFAULT 0, + `unique` int(1) NOT NULL DEFAULT 0, + `no_copy` int(1) NOT NULL DEFAULT 0, + `allow_on_submit` int(1) NOT NULL DEFAULT 0, + `show_preview_popup` int(1) NOT NULL DEFAULT 0, + `trigger` varchar(255) DEFAULT NULL, + `collapsible_depends_on` text, + `mandatory_depends_on` text, + `read_only_depends_on` text, + `depends_on` text, + `permlevel` int(11) NOT NULL DEFAULT 0, + `ignore_user_permissions` int(1) NOT NULL DEFAULT 0, + `width` varchar(255) DEFAULT NULL, + `print_width` varchar(255) DEFAULT NULL, + `columns` int(11) NOT NULL DEFAULT 0, + `default` text, + `description` text, + `in_list_view` int(1) NOT NULL DEFAULT 0, + `fetch_if_empty` int(1) NOT NULL DEFAULT 0, + `in_filter` int(1) NOT NULL DEFAULT 0, + `remember_last_selected_value` int(1) NOT NULL DEFAULT 0, + `ignore_xss_filter` int(1) NOT NULL DEFAULT 0, + `print_hide_if_no_value` int(1) NOT NULL DEFAULT 0, + `allow_bulk_edit` int(1) NOT NULL DEFAULT 0, + `in_standard_filter` int(1) NOT NULL DEFAULT 0, + `in_preview` int(1) NOT NULL DEFAULT 0, + `read_only` int(1) NOT NULL DEFAULT 0, + `precision` varchar(255) DEFAULT NULL, + `max_height` varchar(10) DEFAULT NULL, + `length` int(11) NOT NULL DEFAULT 0, + `translatable` int(1) NOT NULL DEFAULT 0, + `hide_border` int(1) NOT NULL DEFAULT 0, + `hide_days` int(1) NOT NULL DEFAULT 0, + `hide_seconds` int(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`name`), + KEY `parent` (`parent`), + KEY `label` (`label`), + KEY `fieldtype` (`fieldtype`), + KEY `fieldname` (`fieldname`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +-- +-- Table structure for table `tabDocPerm` +-- + +DROP TABLE IF EXISTS `tabDocPerm`; +CREATE TABLE `tabDocPerm` ( + `name` varchar(255) NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `permlevel` int(11) DEFAULT '0', + `role` varchar(255) DEFAULT NULL, + `match` varchar(255) DEFAULT NULL, + `read` int(1) NOT NULL DEFAULT 1, + `write` int(1) NOT NULL DEFAULT 1, + `create` int(1) NOT NULL DEFAULT 1, + `submit` int(1) NOT NULL DEFAULT 0, + `cancel` int(1) NOT NULL DEFAULT 0, + `delete` int(1) NOT NULL DEFAULT 1, + `amend` int(1) NOT NULL DEFAULT 0, + `report` int(1) NOT NULL DEFAULT 1, + `export` int(1) NOT NULL DEFAULT 1, + `import` int(1) NOT NULL DEFAULT 0, + `share` int(1) NOT NULL DEFAULT 1, + `print` int(1) NOT NULL DEFAULT 1, + `email` int(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`name`), + KEY `parent` (`parent`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `tabDocType Action` +-- + +DROP TABLE IF EXISTS `tabDocType Action`; +CREATE TABLE `tabDocType Action` ( + `name` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `owner` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `parentfield` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `parenttype` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `label` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `group` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `action_type` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `action` text COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`name`), + KEY `parent` (`parent`), + KEY `modified` (`modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; + +-- +-- Table structure for table `tabDocType Link` +-- + +DROP TABLE IF EXISTS `tabDocType Link`; +CREATE TABLE `tabDocType Link` ( + `name` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `owner` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `parentfield` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `parenttype` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `group` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `link_doctype` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `link_fieldname` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`name`), + KEY `parent` (`parent`), + KEY `modified` (`modified`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; + +-- +-- Table structure for table `tabDocType` +-- + +DROP TABLE IF EXISTS `tabDocType`; +CREATE TABLE `tabDocType` ( + `name` varchar(255) NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `idx` int(8) NOT NULL DEFAULT 0, + `search_fields` varchar(255) DEFAULT NULL, + `issingle` int(1) NOT NULL DEFAULT 0, + `is_virtual` int(1) NOT NULL DEFAULT 0, + `is_tree` int(1) NOT NULL DEFAULT 0, + `istable` int(1) NOT NULL DEFAULT 0, + `editable_grid` int(1) NOT NULL DEFAULT 1, + `track_changes` int(1) NOT NULL DEFAULT 0, + `module` varchar(255) DEFAULT NULL, + `restrict_to_domain` varchar(255) DEFAULT NULL, + `app` varchar(255) DEFAULT NULL, + `autoname` varchar(255) DEFAULT NULL, + `naming_rule` varchar(40) DEFAULT NULL, + `title_field` varchar(255) DEFAULT NULL, + `image_field` varchar(255) DEFAULT NULL, + `timeline_field` varchar(255) DEFAULT NULL, + `sort_field` varchar(255) DEFAULT NULL, + `sort_order` varchar(255) DEFAULT NULL, + `description` text, + `colour` varchar(255) DEFAULT NULL, + `read_only` int(1) NOT NULL DEFAULT 0, + `in_create` int(1) NOT NULL DEFAULT 0, + `menu_index` int(11) DEFAULT NULL, + `parent_node` varchar(255) DEFAULT NULL, + `smallicon` varchar(255) DEFAULT NULL, + `allow_copy` int(1) NOT NULL DEFAULT 0, + `allow_rename` int(1) NOT NULL DEFAULT 0, + `allow_import` int(1) NOT NULL DEFAULT 0, + `hide_toolbar` int(1) NOT NULL DEFAULT 0, + `track_seen` int(1) NOT NULL DEFAULT 0, + `max_attachments` int(11) NOT NULL DEFAULT 0, + `print_outline` varchar(255) DEFAULT NULL, + `document_type` varchar(255) DEFAULT NULL, + `icon` varchar(255) DEFAULT NULL, + `color` varchar(255) DEFAULT NULL, + `tag_fields` varchar(255) DEFAULT NULL, + `subject` varchar(255) DEFAULT NULL, + `_last_update` varchar(32) DEFAULT NULL, + `engine` varchar(20) DEFAULT 'InnoDB', + `default_print_format` varchar(255) DEFAULT NULL, + `is_submittable` int(1) NOT NULL DEFAULT 0, + `show_name_in_global_search` int(1) NOT NULL DEFAULT 0, + `_user_tags` varchar(255) DEFAULT NULL, + `custom` int(1) NOT NULL DEFAULT 0, + `beta` int(1) NOT NULL DEFAULT 0, + `has_web_view` int(1) NOT NULL DEFAULT 0, + `allow_guest_to_view` int(1) NOT NULL DEFAULT 0, + `route` varchar(255) DEFAULT NULL, + `is_published_field` varchar(255) DEFAULT NULL, + `website_search_field` varchar(255) DEFAULT NULL, + `email_append_to` int(1) NOT NULL DEFAULT 0, + `subject_field` varchar(255) DEFAULT NULL, + `sender_field` varchar(255) DEFAULT NULL, + `show_title_field_in_link` int(1) NOT NULL DEFAULT 0, + `migration_hash` varchar(255) DEFAULT NULL, + `translated_doctype` int(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`name`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `tabSeries` +-- + +DROP TABLE IF EXISTS `tabSeries`; +CREATE TABLE `tabSeries` ( + `name` varchar(100), + `current` int(10) NOT NULL DEFAULT 0, + PRIMARY KEY(`name`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +-- +-- Table structure for table `tabSessions` +-- + +DROP TABLE IF EXISTS `tabSessions`; +CREATE TABLE `tabSessions` ( + `user` varchar(255) DEFAULT NULL, + `sid` varchar(255) DEFAULT NULL, + `sessiondata` longtext, + `ipaddress` varchar(16) DEFAULT NULL, + `lastupdate` datetime(6) DEFAULT NULL, + `status` varchar(20) DEFAULT NULL, + KEY `sid` (`sid`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + +-- +-- Table structure for table `tabSingles` +-- + +DROP TABLE IF EXISTS `tabSingles`; +CREATE TABLE `tabSingles` ( + `doctype` varchar(255) DEFAULT NULL, + `field` varchar(255) DEFAULT NULL, + `value` longtext, + KEY `singles_doctype_field_index` (`doctype`, `field`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `__Auth` +-- + +DROP TABLE IF EXISTS `__Auth`; +CREATE TABLE `__Auth` ( + `doctype` VARCHAR(140) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `fieldname` VARCHAR(140) NOT NULL, + `password` TEXT NOT NULL, + `encrypted` INT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`doctype`, `name`, `fieldname`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `tabFile` +-- + +DROP TABLE IF EXISTS `tabFile`; +CREATE TABLE `tabFile` ( + `name` varchar(255) NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `file_name` varchar(255) DEFAULT NULL, + `file_url` varchar(255) DEFAULT NULL, + `module` varchar(255) DEFAULT NULL, + `attached_to_name` varchar(255) DEFAULT NULL, + `file_size` int(11) NOT NULL DEFAULT 0, + `attached_to_doctype` varchar(255) DEFAULT NULL, + PRIMARY KEY (`name`), + KEY `parent` (`parent`), + KEY `attached_to_name` (`attached_to_name`), + KEY `attached_to_doctype` (`attached_to_doctype`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Table structure for table `tabDefaultValue` +-- + +DROP TABLE IF EXISTS `tabDefaultValue`; +CREATE TABLE `tabDefaultValue` ( + `name` varchar(255) NOT NULL, + `creation` datetime(6) DEFAULT NULL, + `modified` datetime(6) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, + `docstatus` int(1) NOT NULL DEFAULT 0, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, + `idx` int(8) NOT NULL DEFAULT 0, + `defvalue` text, + `defkey` varchar(255) DEFAULT NULL, + PRIMARY KEY (`name`), + KEY `parent` (`parent`), + KEY `defaultvalue_parent_defkey_index` (`parent`,`defkey`) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/xhiveframework/database/mariadb/schema.py b/xhiveframework/database/mariadb/schema.py new file mode 100644 index 0000000..c9af2bc --- /dev/null +++ b/xhiveframework/database/mariadb/schema.py @@ -0,0 +1,119 @@ +from pymysql.constants.ER import DUP_ENTRY + +import xhiveframework +from xhiveframework import _ +from xhiveframework.database.schema import DBTable + + +class MariaDBTable(DBTable): + def create(self): + additional_definitions = [] + engine = self.meta.get("engine") or "InnoDB" + varchar_len = xhiveframework.db.VARCHAR_LEN + name_column = f"name varchar({varchar_len}) primary key" + + # columns + column_defs = self.get_column_definitions() + if column_defs: + additional_definitions += column_defs + + # index + index_defs = self.get_index_definitions() + if index_defs: + additional_definitions += index_defs + + # child table columns + if self.meta.get("istable") or 0: + additional_definitions += [ + f"parent varchar({varchar_len})", + f"parentfield varchar({varchar_len})", + f"parenttype varchar({varchar_len})", + "index parent(parent)", + ] + else: + # parent types + additional_definitions.append("index modified(modified)") + + # creating sequence(s) + if not self.meta.issingle and self.meta.autoname == "autoincrement": + xhiveframework.db.create_sequence(self.doctype, check_not_exists=True) + + # NOTE: not used nextval func as default as the ability to restore + # database with sequences has bugs in mariadb and gives a scary error. + # issue link: https://jira.mariadb.org/browse/MDEV-20070 + name_column = "name bigint primary key" + + additional_definitions = ",\n".join(additional_definitions) + + # create table + query = f"""create table `{self.table_name}` ( + {name_column}, + creation datetime(6), + modified datetime(6), + modified_by varchar({varchar_len}), + owner varchar({varchar_len}), + docstatus int(1) not null default '0', + idx int(8) not null default '0', + {additional_definitions}) + ENGINE={engine} + ROW_FORMAT=DYNAMIC + CHARACTER SET=utf8mb4 + COLLATE=utf8mb4_unicode_ci""" + + xhiveframework.db.sql_ddl(query) + + def alter(self): + for col in self.columns.values(): + col.build_for_alter_table(self.current_columns.get(col.fieldname.lower())) + + add_column_query = [f"ADD COLUMN `{col.fieldname}` {col.get_definition()}" for col in self.add_column] + columns_to_modify = set(self.change_type + self.set_default) + modify_column_query = [ + f"MODIFY `{col.fieldname}` {col.get_definition(for_modification=True)}" + for col in columns_to_modify + ] + modify_column_query.extend( + [f"ADD UNIQUE INDEX IF NOT EXISTS {col.fieldname} (`{col.fieldname}`)" for col in self.add_unique] + ) + add_index_query = [ + f"ADD INDEX `{col.fieldname}_index`(`{col.fieldname}`)" + for col in self.add_index + if not xhiveframework.db.get_column_index(self.table_name, col.fieldname, unique=False) + ] + drop_index_query = [] + + for col in {*self.drop_index, *self.drop_unique}: + if col.fieldname == "name": + continue + + current_column = self.current_columns.get(col.fieldname.lower()) + unique_constraint_changed = current_column.unique != col.unique + if unique_constraint_changed and not col.unique: + if unique_index := xhiveframework.db.get_column_index(self.table_name, col.fieldname, unique=True): + drop_index_query.append(f"DROP INDEX `{unique_index.Key_name}`") + + index_constraint_changed = current_column.index != col.set_index + if index_constraint_changed and not col.set_index: + if index_record := xhiveframework.db.get_column_index(self.table_name, col.fieldname, unique=False): + drop_index_query.append(f"DROP INDEX `{index_record.Key_name}`") + + try: + for query_parts in [add_column_query, modify_column_query, add_index_query, drop_index_query]: + if query_parts: + query_body = ", ".join(query_parts) + query = f"ALTER TABLE `{self.table_name}` {query_body}" + xhiveframework.db.sql_ddl(query) + + except Exception as e: + if query := locals().get("query"): # this weirdness is to avoid potentially unbounded vars + print(f"Failed to alter schema using query: {query}") + + if e.args[0] == DUP_ENTRY: + fieldname = str(e).split("'")[-2] + xhiveframework.throw( + _( + "{0} field cannot be set as unique in {1}, as there are non-unique existing values" + ).format(fieldname, self.table_name) + ) + + raise diff --git a/xhiveframework/database/mariadb/setup_db.py b/xhiveframework/database/mariadb/setup_db.py new file mode 100644 index 0000000..0d0a6b0 --- /dev/null +++ b/xhiveframework/database/mariadb/setup_db.py @@ -0,0 +1,173 @@ +import os + +import click + +import xhiveframework +from xhiveframework.database.db_manager import DbManager + +REQUIRED_MARIADB_CONFIG = { + "character_set_server": "utf8mb4", + "collation_server": "utf8mb4_unicode_ci", +} + + +def get_mariadb_variables(): + return xhiveframework._dict(xhiveframework.db.sql("show variables")) + + +def get_mariadb_version(version_string: str = ""): + # MariaDB classifies their versions as Major (1st and 2nd number), and Minor (3rd number) + # Example: Version 10.3.13 is Major Version = 10.3, Minor Version = 13 + version_string = version_string or get_mariadb_variables().get("version") + version = version_string.split("-", 1)[0] + return version.rsplit(".", 1) + + +def setup_database(force, source_sql, verbose, no_mariadb_socket=False): + xhiveframework.local.session = xhiveframework._dict({"user": "Administrator"}) + + db_name = xhiveframework.local.conf.db_name + root_conn = get_root_connection(xhiveframework.flags.root_login, xhiveframework.flags.root_password) + dbman = DbManager(root_conn) + dbman_kwargs = {} + if no_mariadb_socket: + dbman_kwargs["host"] = "%" + + if force or (db_name not in dbman.get_database_list()): + dbman.delete_user(db_name, **dbman_kwargs) + dbman.drop_database(db_name) + else: + raise Exception(f"Database {db_name} already exists") + + dbman.create_user(db_name, xhiveframework.conf.db_password, **dbman_kwargs) + if verbose: + print("Created user %s" % db_name) + + dbman.create_database(db_name) + if verbose: + print("Created database %s" % db_name) + + dbman.grant_all_privileges(db_name, db_name, **dbman_kwargs) + dbman.flush_privileges() + if verbose: + print(f"Granted privileges to user {db_name} and database {db_name}") + + # close root connection + root_conn.close() + + bootstrap_database(db_name, verbose, source_sql) + + +def drop_user_and_database(db_name, root_login, root_password): + xhiveframework.local.db = get_root_connection(root_login, root_password) + dbman = DbManager(xhiveframework.local.db) + dbman.drop_database(db_name) + dbman.delete_user(db_name, host="%") + dbman.delete_user(db_name) + + +def bootstrap_database(db_name, verbose, source_sql=None): + import sys + + xhiveframework.connect(db_name=db_name) + if not check_database_settings(): + print("Database settings do not match expected values; stopping database setup.") + sys.exit(1) + + import_db_from_sql(source_sql, verbose) + + xhiveframework.connect(db_name=db_name) + if "tabDefaultValue" not in xhiveframework.db.get_tables(cached=False): + from click import secho + + secho( + "Table 'tabDefaultValue' missing in the restored site. " + "Database not installed correctly, this can due to lack of " + "permission, or that the database name exists. Check your mysql" + " root password, validity of the backup file or use --force to" + " reinstall", + fg="red", + ) + sys.exit(1) + + +def import_db_from_sql(source_sql=None, verbose=False): + if verbose: + print("Starting database import...") + db_name = xhiveframework.conf.db_name + if not source_sql: + source_sql = os.path.join(os.path.dirname(__file__), "framework_mariadb.sql") + DbManager(xhiveframework.local.db).restore_database( + verbose, db_name, source_sql, db_name, xhiveframework.conf.db_password + ) + if verbose: + print("Imported from database %s" % source_sql) + + +def check_database_settings(): + check_compatible_versions() + + # Check each expected value vs. actuals: + mariadb_variables = get_mariadb_variables() + result = True + for key, expected_value in REQUIRED_MARIADB_CONFIG.items(): + if mariadb_variables.get(key) != expected_value: + print(f"For key {key}. Expected value {expected_value}, found value {mariadb_variables.get(key)}") + result = False + + if not result: + print( + ( + "{sep2}Creation of your site - {site} failed because MariaDB is not properly {sep}" + "configured.{sep2}" + "Please verify the above settings in MariaDB's my.cnf. Restart MariaDB.{sep}" + "And then run `bench new-site {site}` again.{sep2}" + ).format(site=xhiveframework.local.site, sep2="\n\n", sep="\n") + ) + + return result + + +def check_compatible_versions(): + try: + version = get_mariadb_version() + version_tuple = tuple(int(v) for v in version[0].split(".")) + + if version_tuple < (10, 6): + click.secho( + f"Warning: MariaDB version {version} is less than 10.6 which is not supported by XhiveFramework", + fg="yellow", + ) + elif version_tuple >= (10, 9): + click.secho( + f"Warning: MariaDB version {version} is more than 10.8 which is not yet tested with Xhive Framework.", + fg="yellow", + ) + except Exception: + click.secho( + "MariaDB version compatibility checks failed, make sure you're running a supported version.", + fg="yellow", + ) + + +def get_root_connection(root_login, root_password): + import getpass + + if not xhiveframework.local.flags.root_connection: + if not root_login: + root_login = "root" + + if not root_password: + root_password = xhiveframework.conf.get("root_password") or None + + if not root_password: + root_password = getpass.getpass("MySQL root password: ") + + xhiveframework.local.flags.root_connection = xhiveframework.database.get_db( + host=xhiveframework.conf.db_host, + port=xhiveframework.conf.db_port, + user=root_login, + password=root_password, + ) + + return xhiveframework.local.flags.root_connection diff --git a/xhiveframework/database/operator_map.py b/xhiveframework/database/operator_map.py new file mode 100644 index 0000000..3eb2994 --- /dev/null +++ b/xhiveframework/database/operator_map.py @@ -0,0 +1,138 @@ +# Copyright (c) 2023, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import operator +from collections.abc import Callable + +import xhiveframework +from xhiveframework.database.utils import NestedSetHierarchy +from xhiveframework.model.db_query import get_timespan_date_range +from xhiveframework.query_builder import Field + + +def like(key: Field, value: str) -> xhiveframework.qb: + """Wrapper method for `LIKE` + + Args: + key (str): field + value (str): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `LIKE` + """ + return key.like(value) + + +def func_in(key: Field, value: list | tuple) -> xhiveframework.qb: + """Wrapper method for `IN` + + Args: + key (str): field + value (Union[int, str]): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `IN` + """ + if isinstance(value, str): + value = value.split(",") + return key.isin(value) + + +def not_like(key: Field, value: str) -> xhiveframework.qb: + """Wrapper method for `NOT LIKE` + + Args: + key (str): field + value (str): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `NOT LIKE` + """ + return key.not_like(value) + + +def func_not_in(key: Field, value: list | tuple | str): + """Wrapper method for `NOT IN` + + Args: + key (str): field + value (Union[int, str]): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `NOT IN` + """ + if isinstance(value, str): + value = value.split(",") + return key.notin(value) + + +def func_regex(key: Field, value: str) -> xhiveframework.qb: + """Wrapper method for `REGEX` + + Args: + key (str): field + value (str): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `REGEX` + """ + return key.regex(value) + + +def func_between(key: Field, value: list | tuple) -> xhiveframework.qb: + """Wrapper method for `BETWEEN` + + Args: + key (str): field + value (Union[int, str]): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `BETWEEN` + """ + return key[slice(*value)] + + +def func_is(key, value): + "Wrapper for IS" + return key.isnotnull() if value.lower() == "set" else key.isnull() + + +def func_timespan(key: Field, value: str) -> xhiveframework.qb: + """Wrapper method for `TIMESPAN` + + Args: + key (str): field + value (str): criterion + + Returns: + xhiveframework.qb: `xhiveframework.qb object with `TIMESPAN` + """ + + return func_between(key, get_timespan_date_range(value)) + + +# default operators +OPERATOR_MAP: dict[str, Callable] = { + "+": operator.add, + "=": operator.eq, + "-": operator.sub, + "!=": operator.ne, + "<": operator.lt, + ">": operator.gt, + "<=": operator.le, + "=<": operator.le, + ">=": operator.ge, + "=>": operator.ge, + "/": operator.truediv, + "*": operator.mul, + "in": func_in, + "not in": func_not_in, + "like": like, + "not like": not_like, + "regex": func_regex, + "between": func_between, + "is": func_is, + "timespan": func_timespan, + "nested_set": NestedSetHierarchy, + # TODO: Add support for custom operators (WIP) - via filters_config hooks +} diff --git a/xhiveframework/database/postgres/__init__.py b/xhiveframework/database/postgres/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/database/postgres/database.py b/xhiveframework/database/postgres/database.py new file mode 100644 index 0000000..0bb9c67 --- /dev/null +++ b/xhiveframework/database/postgres/database.py @@ -0,0 +1,471 @@ +import re + +import psycopg2 +import psycopg2.extensions +from psycopg2.errorcodes import ( + CLASS_INTEGRITY_CONSTRAINT_VIOLATION, + DEADLOCK_DETECTED, + DUPLICATE_COLUMN, + INSUFFICIENT_PRIVILEGE, + STRING_DATA_RIGHT_TRUNCATION, + UNDEFINED_COLUMN, + UNDEFINED_TABLE, + UNIQUE_VIOLATION, +) +from psycopg2.errors import ( + LockNotAvailable, + ReadOnlySqlTransaction, + SequenceGeneratorLimitExceeded, + SyntaxError, +) +from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ + +import xhiveframework +from xhiveframework.database.database import Database +from xhiveframework.database.postgres.schema import PostgresTable +from xhiveframework.database.utils import EmptyQueryValues, LazyDecode +from xhiveframework.utils import cstr, get_table_name + +# cast decimals as floats +DEC2FLOAT = psycopg2.extensions.new_type( + psycopg2.extensions.DECIMAL.values, + "DEC2FLOAT", + lambda value, curs: float(value) if value is not None else None, +) + +psycopg2.extensions.register_type(DEC2FLOAT) + +LOCATE_SUB_PATTERN = re.compile(r"locate\(([^,]+),([^)]+)(\)?)\)", flags=re.IGNORECASE) +LOCATE_QUERY_PATTERN = re.compile(r"locate\(", flags=re.IGNORECASE) +PG_TRANSFORM_PATTERN = re.compile(r"([=><]+)\s*([+-]?\d+)(\.0)?(?![a-zA-Z\.\d])") +FROM_TAB_PATTERN = re.compile(r"from tab([\w-]*)", flags=re.IGNORECASE) + + +class PostgresExceptionUtil: + ProgrammingError = psycopg2.ProgrammingError + TableMissingError = psycopg2.ProgrammingError + OperationalError = psycopg2.OperationalError + InternalError = psycopg2.InternalError + SQLError = psycopg2.ProgrammingError + DataError = psycopg2.DataError + InterfaceError = psycopg2.InterfaceError + SequenceGeneratorLimitExceeded = SequenceGeneratorLimitExceeded + + @staticmethod + def is_deadlocked(e): + return getattr(e, "pgcode", None) == DEADLOCK_DETECTED + + @staticmethod + def is_timedout(e): + # http://initd.org/psycopg/docs/extensions.html?highlight=datatype#psycopg2.extensions.QueryCanceledError + return isinstance(e, (psycopg2.extensions.QueryCanceledError | LockNotAvailable)) + + @staticmethod + def is_read_only_mode_error(e) -> bool: + return isinstance(e, ReadOnlySqlTransaction) + + @staticmethod + def is_syntax_error(e): + return isinstance(e, SyntaxError) + + @staticmethod + def is_table_missing(e): + return getattr(e, "pgcode", None) == UNDEFINED_TABLE + + @staticmethod + def is_missing_table(e): + return PostgresDatabase.is_table_missing(e) + + @staticmethod + def is_missing_column(e): + return getattr(e, "pgcode", None) == UNDEFINED_COLUMN + + @staticmethod + def is_access_denied(e): + return getattr(e, "pgcode", None) == INSUFFICIENT_PRIVILEGE + + @staticmethod + def cant_drop_field_or_key(e): + return getattr(e, "pgcode", None) == CLASS_INTEGRITY_CONSTRAINT_VIOLATION + + @staticmethod + def is_duplicate_entry(e): + return getattr(e, "pgcode", None) == UNIQUE_VIOLATION + + @staticmethod + def is_primary_key_violation(e): + return getattr(e, "pgcode", None) == UNIQUE_VIOLATION and "_pkey" in cstr(e.args[0]) + + @staticmethod + def is_unique_key_violation(e): + return getattr(e, "pgcode", None) == UNIQUE_VIOLATION and "_key" in cstr(e.args[0]) + + @staticmethod + def is_duplicate_fieldname(e): + return getattr(e, "pgcode", None) == DUPLICATE_COLUMN + + @staticmethod + def is_statement_timeout(e): + return PostgresDatabase.is_timedout(e) or isinstance(e, xhiveframework.QueryTimeoutError) + + @staticmethod + def is_data_too_long(e): + return getattr(e, "pgcode", None) == STRING_DATA_RIGHT_TRUNCATION + + @staticmethod + def is_db_table_size_limit(e) -> bool: + return False + + +class PostgresDatabase(PostgresExceptionUtil, Database): + REGEX_CHARACTER = "~" + default_port = "5432" + + def setup_type_map(self): + self.db_type = "postgres" + self.type_map = { + "Currency": ("decimal", "21,9"), + "Int": ("bigint", None), + "Long Int": ("bigint", None), + "Float": ("decimal", "21,9"), + "Percent": ("decimal", "21,9"), + "Check": ("smallint", None), + "Small Text": ("text", ""), + "Long Text": ("text", ""), + "Code": ("text", ""), + "Text Editor": ("text", ""), + "Markdown Editor": ("text", ""), + "HTML Editor": ("text", ""), + "Date": ("date", ""), + "Datetime": ("timestamp", None), + "Time": ("time", "6"), + "Text": ("text", ""), + "Data": ("varchar", self.VARCHAR_LEN), + "Link": ("varchar", self.VARCHAR_LEN), + "Dynamic Link": ("varchar", self.VARCHAR_LEN), + "Password": ("text", ""), + "Select": ("varchar", self.VARCHAR_LEN), + "Rating": ("decimal", "3,2"), + "Read Only": ("varchar", self.VARCHAR_LEN), + "Attach": ("text", ""), + "Attach Image": ("text", ""), + "Signature": ("text", ""), + "Color": ("varchar", self.VARCHAR_LEN), + "Barcode": ("text", ""), + "Geolocation": ("text", ""), + "Duration": ("decimal", "21,9"), + "Icon": ("varchar", self.VARCHAR_LEN), + "Phone": ("varchar", self.VARCHAR_LEN), + "Autocomplete": ("varchar", self.VARCHAR_LEN), + "JSON": ("json", ""), + } + + @property + def last_query(self): + return LazyDecode(self._cursor.query) + + def get_connection(self): + conn = psycopg2.connect( + "host='{}' dbname='{}' user='{}' password='{}' port={}".format( + self.host, self.user, self.user, self.password, self.port + ) + ) + conn.set_isolation_level(ISOLATION_LEVEL_REPEATABLE_READ) + + return conn + + def set_execution_timeout(self, seconds: int): + # Postgres expects milliseconds as input + self.sql("set local statement_timeout = %s", int(seconds) * 1000) + + def escape(self, s, percent=True): + """Escape quotes and percent in given string.""" + if isinstance(s, bytes): + s = s.decode("utf-8") + + # MariaDB's driver treats None as an empty string + # So Postgres should do the same + + if s is None: + s = "" + + if percent: + s = s.replace("%", "%%") + + s = s.encode("utf-8") + + return str(psycopg2.extensions.QuotedString(s)) + + def get_database_size(self): + """'Returns database size in MB""" + db_size = self.sql( + "SELECT (pg_database_size(%s) / 1024 / 1024) as database_size", self.db_name, as_dict=True + ) + return db_size[0].get("database_size") + + # pylint: disable=W0221 + def sql(self, query, values=EmptyQueryValues, *args, **kwargs): + return super().sql(modify_query(query), modify_values(values), *args, **kwargs) + + def lazy_mogrify(self, *args, **kwargs) -> str: + return self.last_query + + def get_tables(self, cached=True): + return [ + d[0] + for d in self.sql( + """select table_name + from information_schema.tables + where table_catalog='{}' + and table_type = 'BASE TABLE' + and table_schema='{}'""".format(xhiveframework.conf.db_name, xhiveframework.conf.get("db_schema", "public")) + ) + ] + + def format_date(self, date): + if not date: + return "0001-01-01" + + if not isinstance(date, str): + date = date.strftime("%Y-%m-%d") + + return date + + # column type + @staticmethod + def is_type_number(code): + return code == psycopg2.NUMBER + + @staticmethod + def is_type_datetime(code): + return code == psycopg2.DATETIME + + def rename_table(self, old_name: str, new_name: str) -> list | tuple: + old_name = get_table_name(old_name) + new_name = get_table_name(new_name) + return self.sql(f"ALTER TABLE `{old_name}` RENAME TO `{new_name}`") + + def describe(self, doctype: str) -> list | tuple: + table_name = get_table_name(doctype) + return self.sql( + f"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = '{table_name}'" + ) + + def change_column_type( + self, doctype: str, column: str, type: str, nullable: bool = False, use_cast: bool = False + ) -> list | tuple: + table_name = get_table_name(doctype) + null_constraint = "SET NOT NULL" if not nullable else "DROP NOT NULL" + using_cast = f'using "{column}"::{type}' if use_cast else "" + + # postgres allows ddl in transactions but since we've currently made + # things same as mariadb (raising exception on ddl commands if the transaction has any writes), + # hence using sql_ddl here for committing and then moving forward. + return self.sql_ddl( + f"""ALTER TABLE "{table_name}" + ALTER COLUMN "{column}" TYPE {type} {using_cast}, + ALTER COLUMN "{column}" {null_constraint}""" + ) + + def rename_column(self, doctype: str, old_column_name: str, new_column_name: str): + table_name = get_table_name(doctype) + xhiveframework.db.sql_ddl( + f"ALTER TABLE `{table_name}` RENAME COLUMN `{old_column_name}` TO `{new_column_name}`" + ) + + def create_auth_table(self): + self.sql_ddl( + """create table if not exists "__Auth" ( + "doctype" VARCHAR(140) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "fieldname" VARCHAR(140) NOT NULL, + "password" TEXT NOT NULL, + "encrypted" INT NOT NULL DEFAULT 0, + PRIMARY KEY ("doctype", "name", "fieldname") + )""" + ) + + def create_global_search_table(self): + if "__global_search" not in self.get_tables(): + self.sql( + f"""create table "__global_search"( + doctype varchar(100), + name varchar({self.VARCHAR_LEN}), + title varchar({self.VARCHAR_LEN}), + content text, + route varchar({self.VARCHAR_LEN}), + published int not null default 0, + unique (doctype, name))""" + ) + + def create_user_settings_table(self): + self.sql_ddl( + """create table if not exists "__UserSettings" ( + "user" VARCHAR(180) NOT NULL, + "doctype" VARCHAR(180) NOT NULL, + "data" TEXT, + UNIQUE ("user", "doctype") + )""" + ) + + def updatedb(self, doctype, meta=None): + """ + Syncs a `DocType` to the table + * creates if required + * updates columns + * updates indices + """ + res = self.sql(f"select issingle from `tabDocType` where name='{doctype}'") + if not res: + raise Exception(f"Wrong doctype {doctype} in updatedb") + + if not res[0][0]: + db_table = PostgresTable(doctype, meta) + db_table.validate() + + db_table.sync() + self.begin() + + @staticmethod + def get_on_duplicate_update(key="name"): + if isinstance(key, list): + key = '", "'.join(key) + return f'ON CONFLICT ("{key}") DO UPDATE SET ' + + def check_implicit_commit(self, query): + pass # postgres can run DDL in transactions without implicit commits + + def has_index(self, table_name, index_name): + return self.sql( + f"""SELECT 1 FROM pg_indexes WHERE tablename='{table_name}' + and indexname='{index_name}' limit 1""" + ) + + def add_index(self, doctype: str, fields: list, index_name: str | None = None): + """Creates an index with given fields if not already created. + Index name will be `fieldname1_fieldname2_index`""" + table_name = get_table_name(doctype) + index_name = index_name or self.get_index_name(fields) + fields_str = '", "'.join(re.sub(r"\(.*\)", "", field) for field in fields) + + self.sql_ddl(f'CREATE INDEX IF NOT EXISTS "{index_name}" ON `{table_name}` ("{fields_str}")') + + def add_unique(self, doctype, fields, constraint_name=None): + if isinstance(fields, str): + fields = [fields] + if not constraint_name: + constraint_name = "unique_" + "_".join(fields) + + if not self.sql( + """ + SELECT CONSTRAINT_NAME + FROM information_schema.TABLE_CONSTRAINTS + WHERE table_name=%s + AND constraint_type='UNIQUE' + AND CONSTRAINT_NAME=%s""", + ("tab" + doctype, constraint_name), + ): + self.commit() + self.sql( + """ALTER TABLE `tab{}` + ADD CONSTRAINT {} UNIQUE ({})""".format(doctype, constraint_name, ", ".join(fields)) + ) + + def get_table_columns_description(self, table_name): + """Returns list of column and its description""" + # pylint: disable=W1401 + return self.sql( + f""" + SELECT a.column_name AS name, + CASE LOWER(a.data_type) + WHEN 'character varying' THEN CONCAT('varchar(', a.character_maximum_length ,')') + WHEN 'timestamp without time zone' THEN 'timestamp' + ELSE a.data_type + END AS type, + BOOL_OR(b.index) AS index, + SPLIT_PART(COALESCE(a.column_default, NULL), '::', 1) AS default, + BOOL_OR(b.unique) AS unique + FROM information_schema.columns a + LEFT JOIN + (SELECT indexdef, tablename, + indexdef LIKE '%UNIQUE INDEX%' AS unique, + indexdef NOT LIKE '%UNIQUE INDEX%' AS index + FROM pg_indexes + WHERE tablename='{table_name}') b + ON SUBSTRING(b.indexdef, '(.*)') LIKE CONCAT('%', a.column_name, '%') + WHERE a.table_name = '{table_name}' + GROUP BY a.column_name, a.data_type, a.column_default, a.character_maximum_length; + """, + as_dict=1, + ) + + def get_column_type(self, doctype, column): + """Returns column type from database.""" + information_schema = xhiveframework.qb.Schema("information_schema") + table = get_table_name(doctype) + + return ( + xhiveframework.qb.from_(information_schema.columns) + .select(information_schema.columns.data_type) + .where( + (information_schema.columns.table_name == table) + & (information_schema.columns.column_name == column) + ) + .run(pluck=True)[0] + ) + + def get_database_list(self): + return self.sql("SELECT datname FROM pg_database", pluck=True) + + +def modify_query(query): + """ "Modifies query according to the requirements of postgres""" + # replace ` with " for definitions + query = str(query).replace("`", '"') + query = replace_locate_with_strpos(query) + # select from requires "" + query = FROM_TAB_PATTERN.sub(r'from "tab\1"', query) + + # only find int (with/without signs), ignore decimals (with/without signs), ignore hashes (which start with numbers), + # drop .0 from decimals and add quotes around them + # + # >>> query = "c='abcd' , a >= 45, b = -45.0, c = 40, d=4500.0, e=3500.53, f=40psdfsd, g=9092094312, h=12.00023" + # >>> re.sub(r"([=><]+)\s*([+-]?\d+)(\.0)?(?![a-zA-Z\.\d])", r"\1 '\2'", query) + # "c='abcd' , a >= '45', b = '-45', c = '40', d= '4500', e=3500.53, f=40psdfsd, g= '9092094312', h=12.00023 + + return PG_TRANSFORM_PATTERN.sub(r"\1 '\2'", query) + + +def modify_values(values): + def modify_value(value): + if isinstance(value, list | tuple): + value = tuple(modify_values(value)) + + elif isinstance(value, int): + value = str(value) + + return value + + if not values or values == EmptyQueryValues: + return values + + if isinstance(values, dict): + for k, v in values.items(): + values[k] = modify_value(v) + elif isinstance(values, tuple | list): + new_values = [] + for val in values: + new_values.append(modify_value(val)) + + values = new_values + else: + values = modify_value(values) + + return values + + +def replace_locate_with_strpos(query): + # strpos is the locate equivalent in postgres + if LOCATE_QUERY_PATTERN.search(query): + query = LOCATE_SUB_PATTERN.sub(r"strpos(\2\3, \1)", query) + return query diff --git a/xhiveframework/database/postgres/framework_postgres.sql b/xhiveframework/database/postgres/framework_postgres.sql new file mode 100644 index 0000000..e27cbff --- /dev/null +++ b/xhiveframework/database/postgres/framework_postgres.sql @@ -0,0 +1,345 @@ +-- Core Elements to install WNFramework +-- To be called from install.py + + +-- +-- Table structure for table "tabDocField" +-- + +DROP TABLE IF EXISTS "tabDocField"; +CREATE TABLE "tabDocField" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "fieldname" varchar(255) DEFAULT NULL, + "label" varchar(255) DEFAULT NULL, + "oldfieldname" varchar(255) DEFAULT NULL, + "fieldtype" varchar(255) DEFAULT NULL, + "oldfieldtype" varchar(255) DEFAULT NULL, + "options" text, + "search_index" smallint NOT NULL DEFAULT 0, + "hidden" smallint NOT NULL DEFAULT 0, + "set_only_once" smallint NOT NULL DEFAULT 0, + "show_dashboard" smallint NOT NULL DEFAULT 0, + "allow_in_quick_entry" smallint NOT NULL DEFAULT 0, + "print_hide" smallint NOT NULL DEFAULT 0, + "report_hide" smallint NOT NULL DEFAULT 0, + "reqd" smallint NOT NULL DEFAULT 0, + "bold" smallint NOT NULL DEFAULT 0, + "in_global_search" smallint NOT NULL DEFAULT 0, + "collapsible" smallint NOT NULL DEFAULT 0, + "unique" smallint NOT NULL DEFAULT 0, + "no_copy" smallint NOT NULL DEFAULT 0, + "allow_on_submit" smallint NOT NULL DEFAULT 0, + "show_preview_popup" smallint NOT NULL DEFAULT 0, + "trigger" varchar(255) DEFAULT NULL, + "collapsible_depends_on" text, + "mandatory_depends_on" text, + "read_only_depends_on" text, + "depends_on" text, + "permlevel" bigint NOT NULL DEFAULT 0, + "ignore_user_permissions" smallint NOT NULL DEFAULT 0, + "width" varchar(255) DEFAULT NULL, + "print_width" varchar(255) DEFAULT NULL, + "columns" bigint NOT NULL DEFAULT 0, + "default" text, + "description" text, + "in_list_view" smallint NOT NULL DEFAULT 0, + "fetch_if_empty" smallint NOT NULL DEFAULT 0, + "in_filter" smallint NOT NULL DEFAULT 0, + "remember_last_selected_value" smallint NOT NULL DEFAULT 0, + "ignore_xss_filter" smallint NOT NULL DEFAULT 0, + "print_hide_if_no_value" smallint NOT NULL DEFAULT 0, + "allow_bulk_edit" smallint NOT NULL DEFAULT 0, + "in_standard_filter" smallint NOT NULL DEFAULT 0, + "in_preview" smallint NOT NULL DEFAULT 0, + "read_only" smallint NOT NULL DEFAULT 0, + "precision" varchar(255) DEFAULT NULL, + "max_height" varchar(10) DEFAULT NULL, + "length" bigint NOT NULL DEFAULT 0, + "translatable" smallint NOT NULL DEFAULT 0, + "hide_border" smallint NOT NULL DEFAULT 0, + "hide_days" smallint NOT NULL DEFAULT 0, + "hide_seconds" smallint NOT NULL DEFAULT 0, + PRIMARY KEY ("name") +) ; + +create index on "tabDocField" ("parent"); +create index on "tabDocField" ("label"); +create index on "tabDocField" ("fieldtype"); +create index on "tabDocField" ("fieldname"); + +-- +-- Table structure for table "tabDocPerm" +-- + +DROP TABLE IF EXISTS "tabDocPerm"; +CREATE TABLE "tabDocPerm" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "permlevel" bigint DEFAULT '0', + "role" varchar(255) DEFAULT NULL, + "match" varchar(255) DEFAULT NULL, + "read" smallint NOT NULL DEFAULT 1, + "write" smallint NOT NULL DEFAULT 1, + "create" smallint NOT NULL DEFAULT 1, + "submit" smallint NOT NULL DEFAULT 0, + "cancel" smallint NOT NULL DEFAULT 0, + "delete" smallint NOT NULL DEFAULT 1, + "amend" smallint NOT NULL DEFAULT 0, + "report" smallint NOT NULL DEFAULT 1, + "export" smallint NOT NULL DEFAULT 1, + "import" smallint NOT NULL DEFAULT 0, + "share" smallint NOT NULL DEFAULT 1, + "print" smallint NOT NULL DEFAULT 1, + "email" smallint NOT NULL DEFAULT 1, + PRIMARY KEY ("name") +) ; + +create index on "tabDocPerm" ("parent"); + +-- +-- Table structure for table "tabDocType Action" +-- + +DROP TABLE IF EXISTS "tabDocType Action"; +CREATE TABLE "tabDocType Action" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "label" varchar(140) NOT NULL, + "group" text DEFAULT NULL, + "action_type" varchar(140) NOT NULL, + "action" varchar(140) NOT NULL, + PRIMARY KEY ("name") +) ; + +create index on "tabDocType Action" ("parent"); + +-- +-- Table structure for table "tabDocType Link" +-- + +DROP TABLE IF EXISTS "tabDocType Link"; +CREATE TABLE "tabDocType Link" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "label" varchar(140) DEFAULT NULL, + "group" varchar(140) DEFAULT NULL, + "link_doctype" varchar(140) NOT NULL, + "link_fieldname" varchar(140) NOT NULL, + PRIMARY KEY ("name") +) ; + +create index on "tabDocType Link" ("parent"); + + +-- +-- Table structure for table "tabDocType" +-- + +DROP TABLE IF EXISTS "tabDocType"; +CREATE TABLE "tabDocType" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "idx" bigint NOT NULL DEFAULT 0, + "search_fields" varchar(255) DEFAULT NULL, + "issingle" smallint NOT NULL DEFAULT 0, + "is_virtual" smallint NOT NULL DEFAULT 0, + "is_tree" smallint NOT NULL DEFAULT 0, + "istable" smallint NOT NULL DEFAULT 0, + "editable_grid" smallint NOT NULL DEFAULT 1, + "track_changes" smallint NOT NULL DEFAULT 0, + "module" varchar(255) DEFAULT NULL, + "restrict_to_domain" varchar(255) DEFAULT NULL, + "app" varchar(255) DEFAULT NULL, + "autoname" varchar(255) DEFAULT NULL, + "naming_rule" varchar(40) DEFAULT NULL, + "title_field" varchar(255) DEFAULT NULL, + "image_field" varchar(255) DEFAULT NULL, + "timeline_field" varchar(255) DEFAULT NULL, + "sort_field" varchar(255) DEFAULT NULL, + "sort_order" varchar(255) DEFAULT NULL, + "description" text, + "colour" varchar(255) DEFAULT NULL, + "read_only" smallint NOT NULL DEFAULT 0, + "in_create" smallint NOT NULL DEFAULT 0, + "menu_index" bigint DEFAULT NULL, + "parent_node" varchar(255) DEFAULT NULL, + "smallicon" varchar(255) DEFAULT NULL, + "allow_copy" smallint NOT NULL DEFAULT 0, + "allow_rename" smallint NOT NULL DEFAULT 0, + "allow_import" smallint NOT NULL DEFAULT 0, + "hide_toolbar" smallint NOT NULL DEFAULT 0, + "track_seen" smallint NOT NULL DEFAULT 0, + "max_attachments" bigint NOT NULL DEFAULT 0, + "print_outline" varchar(255) DEFAULT NULL, + "document_type" varchar(255) DEFAULT NULL, + "icon" varchar(255) DEFAULT NULL, + "color" varchar(255) DEFAULT NULL, + "tag_fields" varchar(255) DEFAULT NULL, + "subject" varchar(255) DEFAULT NULL, + "_last_update" varchar(32) DEFAULT NULL, + "engine" varchar(20) DEFAULT 'InnoDB', + "default_print_format" varchar(255) DEFAULT NULL, + "is_submittable" smallint NOT NULL DEFAULT 0, + "show_name_in_global_search" smallint NOT NULL DEFAULT 0, + "_user_tags" varchar(255) DEFAULT NULL, + "custom" smallint NOT NULL DEFAULT 0, + "beta" smallint NOT NULL DEFAULT 0, + "has_web_view" smallint NOT NULL DEFAULT 0, + "allow_guest_to_view" smallint NOT NULL DEFAULT 0, + "route" varchar(255) DEFAULT NULL, + "is_published_field" varchar(255) DEFAULT NULL, + "website_search_field" varchar(255) DEFAULT NULL, + "email_append_to" smallint NOT NULL DEFAULT 0, + "subject_field" varchar(255) DEFAULT NULL, + "sender_field" varchar(255) DEFAULT NULL, + "show_title_field_in_link" smallint NOT NULL DEFAULT 0, + "migration_hash" varchar(255) DEFAULT NULL, + "translated_doctype" smallint NOT NULL DEFAULT 0, + PRIMARY KEY ("name") +) ; + +-- +-- Table structure for table "tabSeries" +-- + +DROP TABLE IF EXISTS "tabSeries"; +CREATE TABLE "tabSeries" ( + "name" varchar(100), + "current" bigint NOT NULL DEFAULT 0, + PRIMARY KEY ("name") +) ; + +-- +-- Table structure for table "tabSessions" +-- + +DROP TABLE IF EXISTS "tabSessions"; +CREATE TABLE "tabSessions" ( + "user" varchar(255) DEFAULT NULL, + "sid" varchar(255) DEFAULT NULL, + "sessiondata" text, + "ipaddress" varchar(16) DEFAULT NULL, + "lastupdate" timestamp(6) DEFAULT NULL, + "status" varchar(20) DEFAULT NULL +); + +create index on "tabSessions" ("sid"); + +-- +-- Table structure for table "tabSingles" +-- + +DROP TABLE IF EXISTS "tabSingles"; +CREATE TABLE "tabSingles" ( + "doctype" varchar(255) DEFAULT NULL, + "field" varchar(255) DEFAULT NULL, + "value" text +); + +create index on "tabSingles" ("doctype", "field"); + +-- +-- Table structure for table "__Auth" +-- + +DROP TABLE IF EXISTS "__Auth"; +CREATE TABLE "__Auth" ( + "doctype" VARCHAR(140) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "fieldname" VARCHAR(140) NOT NULL, + "password" TEXT NOT NULL, + "encrypted" int NOT NULL DEFAULT 0, + PRIMARY KEY ("doctype", "name", "fieldname") +); + +create index on "__Auth" ("doctype", "name", "fieldname"); + +-- +-- Table structure for table "tabFile" +-- + +DROP TABLE IF EXISTS "tabFile"; +CREATE TABLE "tabFile" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "file_name" varchar(255) DEFAULT NULL, + "file_url" varchar(255) DEFAULT NULL, + "module" varchar(255) DEFAULT NULL, + "attached_to_name" varchar(255) DEFAULT NULL, + "file_size" bigint NOT NULL DEFAULT 0, + "attached_to_doctype" varchar(255) DEFAULT NULL, + PRIMARY KEY ("name") +); + +create index on "tabFile" ("parent"); +create index on "tabFile" ("attached_to_name"); +create index on "tabFile" ("attached_to_doctype"); + +-- +-- Table structure for table "tabDefaultValue" +-- + +DROP TABLE IF EXISTS "tabDefaultValue"; +CREATE TABLE "tabDefaultValue" ( + "name" varchar(255) NOT NULL, + "creation" timestamp(6) DEFAULT NULL, + "modified" timestamp(6) DEFAULT NULL, + "modified_by" varchar(255) DEFAULT NULL, + "owner" varchar(255) DEFAULT NULL, + "docstatus" smallint NOT NULL DEFAULT 0, + "parent" varchar(255) DEFAULT NULL, + "parentfield" varchar(255) DEFAULT NULL, + "parenttype" varchar(255) DEFAULT NULL, + "idx" bigint NOT NULL DEFAULT 0, + "defvalue" text, + "defkey" varchar(255) DEFAULT NULL, + PRIMARY KEY ("name") +); + +create index on "tabDefaultValue" ("parent"); +create index on "tabDefaultValue" ("parent", "defkey"); diff --git a/xhiveframework/database/postgres/schema.py b/xhiveframework/database/postgres/schema.py new file mode 100644 index 0000000..cf49ddf --- /dev/null +++ b/xhiveframework/database/postgres/schema.py @@ -0,0 +1,159 @@ +import xhiveframework +from xhiveframework import _ +from xhiveframework.database.schema import DBTable, get_definition +from xhiveframework.utils import cint, flt + + +class PostgresTable(DBTable): + def create(self): + varchar_len = xhiveframework.db.VARCHAR_LEN + name_column = f"name varchar({varchar_len}) primary key" + + additional_definitions = "" + # columns + column_defs = self.get_column_definitions() + if column_defs: + additional_definitions += ",\n".join(column_defs) + + # child table columns + if self.meta.get("istable") or 0: + if column_defs: + additional_definitions += ",\n" + + additional_definitions += ",\n".join( + ( + f"parent varchar({varchar_len})", + f"parentfield varchar({varchar_len})", + f"parenttype varchar({varchar_len})", + ) + ) + + # creating sequence(s) + if not self.meta.issingle and self.meta.autoname == "autoincrement": + xhiveframework.db.create_sequence(self.doctype, check_not_exists=True) + name_column = "name bigint primary key" + + # TODO: set docstatus length + # create table + xhiveframework.db.sql( + f"""create table `{self.table_name}` ( + {name_column}, + creation timestamp(6), + modified timestamp(6), + modified_by varchar({varchar_len}), + owner varchar({varchar_len}), + docstatus smallint not null default '0', + idx bigint not null default '0', + {additional_definitions} + )""" + ) + + self.create_indexes() + xhiveframework.db.commit() + + def create_indexes(self): + create_index_query = "" + for col in self.columns.values(): + if ( + col.set_index + and col.fieldtype in xhiveframework.db.type_map + and xhiveframework.db.type_map.get(col.fieldtype)[0] not in ("text", "longtext") + ): + create_index_query += ( + f'CREATE INDEX IF NOT EXISTS "{col.fieldname}" ON `{self.table_name}`(`{col.fieldname}`);' + ) + if create_index_query: + # nosemgrep + xhiveframework.db.sql(create_index_query) + + def alter(self): + for col in self.columns.values(): + col.build_for_alter_table(self.current_columns.get(col.fieldname.lower())) + + query = [f"ADD COLUMN `{col.fieldname}` {col.get_definition()}" for col in self.add_column] + + for col in self.change_type: + using_clause = "" + if col.fieldtype in ("Datetime"): + # The USING option of SET DATA TYPE can actually specify any expression + # involving the old values of the row + # read more https://www.postgresql.org/docs/9.1/sql-altertable.html + using_clause = f"USING {col.fieldname}::timestamp without time zone" + elif col.fieldtype == "Check": + using_clause = f"USING {col.fieldname}::smallint" + + query.append( + "ALTER COLUMN `{}` TYPE {} {}".format( + col.fieldname, + get_definition(col.fieldtype, precision=col.precision, length=col.length), + using_clause, + ) + ) + + for col in self.set_default: + if col.fieldname == "name": + continue + + if col.fieldtype in ("Check", "Int"): + col_default = cint(col.default) + + elif col.fieldtype in ("Currency", "Float", "Percent"): + col_default = flt(col.default) + + elif not col.default: + col_default = "NULL" + + else: + col_default = f"{xhiveframework.db.escape(col.default)}" + + query.append(f"ALTER COLUMN `{col.fieldname}` SET DEFAULT {col_default}") + + create_contraint_query = "" + for col in self.add_index: + # if index key not exists + create_contraint_query += ( + f'CREATE INDEX IF NOT EXISTS "{col.fieldname}" ON `{self.table_name}`(`{col.fieldname}`);' + ) + + for col in self.add_unique: + # if index key not exists + create_contraint_query += 'CREATE UNIQUE INDEX IF NOT EXISTS "unique_{index_name}" ON `{table_name}`(`{field}`);'.format( + index_name=col.fieldname, table_name=self.table_name, field=col.fieldname + ) + + drop_contraint_query = "" + for col in self.drop_index: + # primary key + if col.fieldname != "name": + # if index key exists + drop_contraint_query += f'DROP INDEX IF EXISTS "{col.fieldname}" ;' + + for col in self.drop_unique: + # primary key + if col.fieldname != "name": + # if index key exists + drop_contraint_query += f'DROP INDEX IF EXISTS "unique_{col.fieldname}" ;' + try: + if query: + final_alter_query = "ALTER TABLE `{}` {}".format(self.table_name, ", ".join(query)) + # nosemgrep + xhiveframework.db.sql(final_alter_query) + if create_contraint_query: + # nosemgrep + xhiveframework.db.sql(create_contraint_query) + if drop_contraint_query: + # nosemgrep + xhiveframework.db.sql(drop_contraint_query) + except Exception as e: + # sanitize + if xhiveframework.db.is_duplicate_fieldname(e): + xhiveframework.throw(str(e)) + elif xhiveframework.db.is_duplicate_entry(e): + fieldname = str(e).split("'")[-2] + xhiveframework.throw( + _( + "{0} field cannot be set as unique in {1}, as there are non-unique existing values" + ).format(fieldname, self.table_name) + ) + else: + raise e diff --git a/xhiveframework/database/postgres/setup_db.py b/xhiveframework/database/postgres/setup_db.py new file mode 100644 index 0000000..6a72af1 --- /dev/null +++ b/xhiveframework/database/postgres/setup_db.py @@ -0,0 +1,91 @@ +import os + +import xhiveframework +from xhiveframework.database.db_manager import DbManager + + +def setup_database(force, source_sql=None, verbose=False): + root_conn = get_root_connection(xhiveframework.flags.root_login, xhiveframework.flags.root_password) + root_conn.commit() + root_conn.sql("end") + root_conn.sql(f"DROP DATABASE IF EXISTS `{xhiveframework.conf.db_name}`") + root_conn.sql(f"DROP USER IF EXISTS {xhiveframework.conf.db_name}") + root_conn.sql(f"CREATE DATABASE `{xhiveframework.conf.db_name}`") + root_conn.sql(f"CREATE user {xhiveframework.conf.db_name} password '{xhiveframework.conf.db_password}'") + root_conn.sql(f"GRANT ALL PRIVILEGES ON DATABASE `{xhiveframework.conf.db_name}` TO {xhiveframework.conf.db_name}") + root_conn.close() + + bootstrap_database(xhiveframework.conf.db_name, verbose, source_sql=source_sql) + xhiveframework.connect() + + +def bootstrap_database(db_name, verbose, source_sql=None): + xhiveframework.connect(db_name=db_name) + import_db_from_sql(source_sql, verbose) + xhiveframework.connect(db_name=db_name) + + if "tabDefaultValue" not in xhiveframework.db.get_tables(): + import sys + + from click import secho + + secho( + "Table 'tabDefaultValue' missing in the restored site. " + "This may be due to incorrect permissions or the result of a restore from a bad backup file. " + "Database not installed correctly.", + fg="red", + ) + sys.exit(1) + + +def import_db_from_sql(source_sql=None, verbose=False): + if verbose: + print("Starting database import...") + db_name = xhiveframework.conf.db_name + if not source_sql: + source_sql = os.path.join(os.path.dirname(__file__), "framework_postgres.sql") + DbManager(xhiveframework.local.db).restore_database( + verbose, db_name, source_sql, db_name, xhiveframework.conf.db_password + ) + if verbose: + print("Imported from database %s" % source_sql) + + +def get_root_connection(root_login=None, root_password=None): + if not xhiveframework.local.flags.root_connection: + if not root_login: + root_login = xhiveframework.conf.get("root_login") or None + + if not root_login: + root_login = input("Enter postgres super user: ") + + if not root_password: + root_password = xhiveframework.conf.get("root_password") or None + + if not root_password: + from getpass import getpass + + root_password = getpass("Postgres super user password: ") + + xhiveframework.local.flags.root_connection = xhiveframework.database.get_db( + host=xhiveframework.conf.db_host, + port=xhiveframework.conf.db_port, + user=root_login, + password=root_password, + ) + + return xhiveframework.local.flags.root_connection + + +def drop_user_and_database(db_name, root_login, root_password): + root_conn = get_root_connection( + xhiveframework.flags.root_login or root_login, xhiveframework.flags.root_password or root_password + ) + root_conn.commit() + root_conn.sql( + "SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = %s", + (db_name,), + ) + root_conn.sql("end") + root_conn.sql(f"DROP DATABASE IF EXISTS {db_name}") + root_conn.sql(f"DROP USER IF EXISTS {db_name}") diff --git a/xhiveframework/database/query.py b/xhiveframework/database/query.py new file mode 100644 index 0000000..5859fed --- /dev/null +++ b/xhiveframework/database/query.py @@ -0,0 +1,557 @@ +import re +from ast import literal_eval +from types import BuiltinFunctionType +from typing import TYPE_CHECKING + +import sqlparse +from pypika.queries import QueryBuilder, Table + +import xhiveframework +from xhiveframework import _ +from xhiveframework.database.operator_map import OPERATOR_MAP +from xhiveframework.database.schema import SPECIAL_CHAR_PATTERN +from xhiveframework.database.utils import DefaultOrderBy, get_doctype_name +from xhiveframework.query_builder import Criterion, Field, Order, functions +from xhiveframework.query_builder.functions import Function, SqlFunctions +from xhiveframework.query_builder.utils import PseudoColumnMapper +from xhiveframework.utils.data import MARIADB_SPECIFIC_COMMENT + +if TYPE_CHECKING: + from xhiveframework.query_builder import DocType + +TAB_PATTERN = re.compile("^tab") +WORDS_PATTERN = re.compile(r"\w+") +BRACKETS_PATTERN = re.compile(r"\(.*?\)|$") +SQL_FUNCTIONS = [sql_function.value for sql_function in SqlFunctions] +COMMA_PATTERN = re.compile(r",\s*(?![^()]*\))") + +# less restrictive version of xhiveframework.core.doctype.doctype.doctype.START_WITH_LETTERS_PATTERN +# to allow table names like __Auth +TABLE_NAME_PATTERN = re.compile(r"^[\w -]*$", flags=re.ASCII) + + +class Engine: + def get_query( + self, + table: str | Table, + fields: list | tuple | None = None, + filters: dict[str, str | int] | str | int | list[list | str | int] | None = None, + order_by: str | None = None, + group_by: str | None = None, + limit: int | None = None, + offset: int | None = None, + distinct: bool = False, + for_update: bool = False, + update: bool = False, + into: bool = False, + delete: bool = False, + *, + validate_filters: bool = False, + skip_locked: bool = False, + wait: bool = True, + ) -> QueryBuilder: + self.is_mariadb = xhiveframework.db.db_type == "mariadb" + self.is_postgres = xhiveframework.db.db_type == "postgres" + self.validate_filters = validate_filters + + if isinstance(table, Table): + self.table = table + self.doctype = get_doctype_name(table.get_sql()) + else: + self.doctype = table + self.validate_doctype() + self.table = xhiveframework.qb.DocType(table) + + if update: + self.query = xhiveframework.qb.update(self.table) + elif into: + self.query = xhiveframework.qb.into(self.table) + elif delete: + self.query = xhiveframework.qb.from_(self.table).delete() + else: + self.query = xhiveframework.qb.from_(self.table) + self.apply_fields(fields) + + self.apply_filters(filters) + self.apply_order_by(order_by) + + if limit: + self.query = self.query.limit(limit) + + if offset: + self.query = self.query.offset(offset) + + if distinct: + self.query = self.query.distinct() + + if for_update: + self.query = self.query.for_update(skip_locked=skip_locked, nowait=not wait) + + if group_by: + self.query = self.query.groupby(group_by) + + return self.query + + def validate_doctype(self): + if not TABLE_NAME_PATTERN.match(self.doctype): + xhiveframework.throw(_("Invalid DocType: {0}").format(self.doctype)) + + def apply_fields(self, fields): + # add fields + self.fields = self.parse_fields(fields) + if not self.fields: + self.fields = [self.table.name] + + self.query._child_queries = [] + for field in self.fields: + if isinstance(field, DynamicTableField): + self.query = field.apply_select(self.query) + elif isinstance(field, ChildQuery): + self.query._child_queries.append(field) + else: + self.query = self.query.select(field) + + def apply_filters( + self, + filters: dict[str, str | int] | str | int | list[list | str | int] | None = None, + ): + if filters is None: + return + + if isinstance(filters, str | int): + filters = {"name": str(filters)} + + if isinstance(filters, Criterion): + self.query = self.query.where(filters) + + elif isinstance(filters, dict): + self.apply_dict_filters(filters) + + elif isinstance(filters, list | tuple): + if all(isinstance(d, str | int) for d in filters) and len(filters) > 0: + self.apply_dict_filters({"name": ("in", filters)}) + else: + for filter in filters: + if isinstance(filter, str | int | Criterion | dict): + self.apply_filters(filter) + elif isinstance(filter, list | tuple): + self.apply_list_filters(filter) + + def apply_list_filters(self, filter: list): + if len(filter) == 2: + field, value = filter + self._apply_filter(field, value) + elif len(filter) == 3: + field, operator, value = filter + self._apply_filter(field, value, operator) + elif len(filter) == 4: + doctype, field, operator, value = filter + self._apply_filter(field, value, operator, doctype) + + def apply_dict_filters(self, filters: dict[str, str | int | list]): + for field, value in filters.items(): + operator = "=" + if isinstance(value, list | tuple): + operator, value = value + + self._apply_filter(field, value, operator) + + def _apply_filter( + self, field: str, value: str | int | list | None, operator: str = "=", doctype: str | None = None + ): + _field = field + _value = value + _operator = operator + + if not isinstance(_field, str): + pass + elif not self.validate_filters and (dynamic_field := DynamicTableField.parse(field, self.doctype)): + # apply implicit join if link field's field is referenced + self.query = dynamic_field.apply_join(self.query) + _field = dynamic_field.field + elif self.validate_filters and SPECIAL_CHAR_PATTERN.search(_field): + xhiveframework.throw(_("Invalid filter: {0}").format(_field)) + elif not doctype or doctype == self.doctype: + _field = self.table[field] + elif doctype: + _field = xhiveframework.qb.DocType(doctype)[field] + + # apply implicit join if child table is referenced + if doctype and doctype != self.doctype: + meta = xhiveframework.get_meta(doctype) + table = xhiveframework.qb.DocType(doctype) + if meta.istable and not self.query.is_joined(table): + self.query = self.query.left_join(table).on( + (table.parent == self.table.name) & (table.parenttype == self.doctype) + ) + + if isinstance(_value, bool): + _value = int(_value) + + elif not _value and isinstance(_value, list | tuple): + _value = ("",) + + # Nested set + if _operator in OPERATOR_MAP["nested_set"]: + hierarchy = _operator + docname = _value + + _df = xhiveframework.get_meta(self.doctype).get_field(field) + ref_doctype = _df.options if _df else self.doctype + + nodes = get_nested_set_hierarchy_result(ref_doctype, docname, hierarchy) + operator_fn = ( + OPERATOR_MAP["not in"] + if hierarchy in ("not ancestors of", "not descendants of") + else OPERATOR_MAP["in"] + ) + if nodes: + self.query = self.query.where(operator_fn(_field, nodes)) + else: + self.query = self.query.where(operator_fn(_field, ("",))) + return + + operator_fn = OPERATOR_MAP[_operator.casefold()] + if _value is None and isinstance(_field, Field): + self.query = self.query.where(_field.isnull()) + else: + self.query = self.query.where(operator_fn(_field, _value)) + + def get_function_object(self, field: str) -> "Function": + """Expects field to look like 'SUM(*)' or 'name' or something similar. Returns PyPika Function object""" + func = field.split("(", maxsplit=1)[0].capitalize() + args_start, args_end = len(func) + 1, field.index(")") + args = field[args_start:args_end].split(",") + + _, alias = field.split(" as ") if " as " in field else (None, None) + + to_cast = "*" not in args + _args = [] + + for arg in args: + initial_fields = literal_eval_(arg.strip()) + if to_cast: + has_primitive_operator = False + for _operator in OPERATOR_MAP.keys(): + if _operator in initial_fields: + operator_mapping = OPERATOR_MAP[_operator] + # Only perform this if operator is of primitive type. + if isinstance(operator_mapping, BuiltinFunctionType): + has_primitive_operator = True + field = operator_mapping( + *map( + lambda field: Field(field.strip()) + if "`" not in field + else PseudoColumnMapper(field.strip()), + arg.split(_operator), + ), + ) + + field = ( + ( + Field(initial_fields) + if "`" not in initial_fields + else PseudoColumnMapper(initial_fields) + ) + if not has_primitive_operator + else field + ) + else: + field = initial_fields + + _args.append(field) + + if alias and "`" in alias: + alias = alias.replace("`", "") + try: + if func.casefold() == "now": + return getattr(functions, func)() + return getattr(functions, func)(*_args, alias=alias or None) + except AttributeError: + # Fall back for functions not present in `SqlFunctions`` + return Function(func, *_args, alias=alias or None) + + def sanitize_fields(self, fields: str | list | tuple): + def _sanitize_field(field: str): + if not isinstance(field, str): + return field + stripped_field = sqlparse.format(field, strip_comments=True, keyword_case="lower") + if self.is_mariadb: + return MARIADB_SPECIFIC_COMMENT.sub("", stripped_field) + return stripped_field + + if isinstance(fields, list | tuple): + return [_sanitize_field(field) for field in fields] + elif isinstance(fields, str): + return _sanitize_field(fields) + + return fields + + def parse_string_field(self, field: str): + if field == "*": + return self.table.star + alias = None + if " as " in field: + field, alias = field.split(" as ") + if "`" in field: + if alias: + return PseudoColumnMapper(f"{field} {alias}") + return PseudoColumnMapper(field) + if alias: + return self.table[field].as_(alias) + return self.table[field] + + def parse_fields(self, fields: str | list | tuple | None) -> list: + if not fields: + return [] + fields = self.sanitize_fields(fields) + if isinstance(fields, list | tuple | set) and None in fields and Field not in fields: + return [] + + if not isinstance(fields, list | tuple): + fields = [fields] + + def parse_field(field: str): + if has_function(field): + return self.get_function_object(field) + elif parsed := DynamicTableField.parse(field, self.doctype): + return parsed + else: + return self.parse_string_field(field) + + _fields = [] + for field in fields: + if isinstance(field, Criterion): + _fields.append(field) + elif isinstance(field, dict): + for child_field, fields in field.items(): + _fields.append(ChildQuery(child_field, fields, self.doctype)) + elif isinstance(field, str): + if "," in field: + field = field.casefold() if "`" not in field else field + field_list = COMMA_PATTERN.split(field) + for field in field_list: + if _field := field.strip(): + _fields.append(parse_field(_field)) + else: + _fields.append(parse_field(field)) + + return _fields + + def apply_order_by(self, order_by: str | None): + if not order_by or order_by == DefaultOrderBy: + return + for declaration in order_by.split(","): + if _order_by := declaration.strip(): + parts = _order_by.split(" ") + order_field, order_direction = parts[0], parts[1] if len(parts) > 1 else "desc" + order_direction = Order.asc if order_direction.lower() == "asc" else Order.desc + self.query = self.query.orderby(order_field, order=order_direction) + + +class Permission: + @classmethod + def check_permissions(cls, query, **kwargs): + if not isinstance(query, str): + query = query.get_sql() + + doctype = cls.get_tables_from_query(query) + if isinstance(doctype, str): + doctype = [doctype] + + for dt in doctype: + dt = TAB_PATTERN.sub("", dt) + if not xhiveframework.has_permission( + dt, + "select", + user=kwargs.get("user"), + parent_doctype=kwargs.get("parent_doctype"), + ) and not xhiveframework.has_permission( + dt, + "read", + user=kwargs.get("user"), + parent_doctype=kwargs.get("parent_doctype"), + ): + xhiveframework.throw(_("Insufficient Permission for {0}").format(xhiveframework.bold(dt))) + + @staticmethod + def get_tables_from_query(query: str): + return [table for table in WORDS_PATTERN.findall(query) if table.startswith("tab")] + + +class DynamicTableField: + def __init__( + self, + doctype: str, + fieldname: str, + parent_doctype: str, + alias: str | None = None, + ) -> None: + self.doctype = doctype + self.fieldname = fieldname + self.alias = alias + self.parent_doctype = parent_doctype + + def __str__(self) -> str: + table_name = f"`tab{self.doctype}`" + fieldname = f"`{self.fieldname}`" + if xhiveframework.db.db_type == "postgres": + table_name = table_name.replace("`", '"') + fieldname = fieldname.replace("`", '"') + alias = f"AS {self.alias}" if self.alias else "" + return f"{table_name}.{fieldname} {alias}".strip() + + @staticmethod + def parse(field: str, doctype: str): + if "." in field: + alias = None + if " as " in field: + field, alias = field.split(" as ") + if field.startswith("`tab") or field.startswith('"tab'): + _, child_doctype, child_field = re.search(r'([`"])tab(.+?)\1.\1(.+)\1', field).groups() + if child_doctype == doctype: + return + return ChildTableField(child_doctype, child_field, doctype, alias=alias) + else: + linked_fieldname, fieldname = field.split(".") + linked_field = xhiveframework.get_meta(doctype).get_field(linked_fieldname) + linked_doctype = linked_field.options + if linked_field.fieldtype == "Link": + return LinkTableField(linked_doctype, fieldname, doctype, linked_fieldname, alias=alias) + elif linked_field.fieldtype in xhiveframework.model.table_fields: + return ChildTableField(linked_doctype, fieldname, doctype, alias=alias) + + def apply_select(self, query: QueryBuilder) -> QueryBuilder: + raise NotImplementedError + + +class ChildTableField(DynamicTableField): + def __init__( + self, + doctype: str, + fieldname: str, + parent_doctype: str, + alias: str | None = None, + ) -> None: + self.doctype = doctype + self.fieldname = fieldname + self.alias = alias + self.parent_doctype = parent_doctype + self.table = xhiveframework.qb.DocType(self.doctype) + self.field = self.table[self.fieldname] + + def apply_select(self, query: QueryBuilder) -> QueryBuilder: + table = xhiveframework.qb.DocType(self.doctype) + query = self.apply_join(query) + return query.select(getattr(table, self.fieldname).as_(self.alias or None)) + + def apply_join(self, query: QueryBuilder) -> QueryBuilder: + table = xhiveframework.qb.DocType(self.doctype) + main_table = xhiveframework.qb.DocType(self.parent_doctype) + if not query.is_joined(table): + query = query.left_join(table).on( + (table.parent == main_table.name) & (table.parenttype == self.parent_doctype) + ) + return query + + +class LinkTableField(DynamicTableField): + def __init__( + self, + doctype: str, + fieldname: str, + parent_doctype: str, + link_fieldname: str, + alias: str | None = None, + ) -> None: + super().__init__(doctype, fieldname, parent_doctype, alias=alias) + self.link_fieldname = link_fieldname + self.table = xhiveframework.qb.DocType(self.doctype) + self.field = self.table[self.fieldname] + + def apply_select(self, query: QueryBuilder) -> QueryBuilder: + table = xhiveframework.qb.DocType(self.doctype) + query = self.apply_join(query) + return query.select(getattr(table, self.fieldname).as_(self.alias or None)) + + def apply_join(self, query: QueryBuilder) -> QueryBuilder: + table = xhiveframework.qb.DocType(self.doctype) + main_table = xhiveframework.qb.DocType(self.parent_doctype) + if not query.is_joined(table): + query = query.left_join(table).on(table.name == getattr(main_table, self.link_fieldname)) + return query + + +class ChildQuery: + def __init__( + self, + fieldname: str, + fields: list, + parent_doctype: str, + ) -> None: + field = xhiveframework.get_meta(parent_doctype).get_field(fieldname) + if field.fieldtype not in xhiveframework.model.table_fields: + return + self.fieldname = fieldname + self.fields = fields + self.parent_doctype = parent_doctype + self.doctype = field.options + + def get_query(self, parent_names=None) -> QueryBuilder: + filters = { + "parenttype": self.parent_doctype, + "parentfield": self.fieldname, + "parent": ["in", parent_names], + } + return xhiveframework.qb.get_query( + self.doctype, + fields=[*self.fields, "parent", "parentfield"], + filters=filters, + order_by="idx asc", + ) + + +def literal_eval_(literal): + try: + return literal_eval(literal) + except (ValueError, SyntaxError): + return literal + + +def has_function(field): + _field = field.casefold() if (isinstance(field, str) and "`" not in field) else field + if not issubclass(type(_field), Criterion): + if any([f"{func}(" in _field for func in SQL_FUNCTIONS]): + return True + + +def get_nested_set_hierarchy_result(doctype: str, name: str, hierarchy: str) -> list[str]: + """Get matching nodes based on operator.""" + table = xhiveframework.qb.DocType(doctype) + try: + lft, rgt = xhiveframework.qb.from_(table).select("lft", "rgt").where(table.name == name).run()[0] + except IndexError: + lft, rgt = None, None + + if hierarchy in ("descendants of", "not descendants of", "descendants of (inclusive)"): + result = ( + xhiveframework.qb.from_(table) + .select(table.name) + .where(table.lft > lft) + .where(table.rgt < rgt) + .orderby(table.lft, order=Order.asc) + .run(pluck=True) + ) + if hierarchy == "descendants of (inclusive)": + result += [name] + else: + # Get ancestor elements of a DocType with a tree structure + result = ( + xhiveframework.qb.from_(table) + .select(table.name) + .where(table.lft < lft) + .where(table.rgt > rgt) + .orderby(table.lft, order=Order.desc) + .run(pluck=True) + ) + return result diff --git a/xhiveframework/database/schema.py b/xhiveframework/database/schema.py new file mode 100644 index 0000000..7c7a17b --- /dev/null +++ b/xhiveframework/database/schema.py @@ -0,0 +1,366 @@ +import re + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils import cint, cstr, flt + +SPECIAL_CHAR_PATTERN = re.compile(r"[\W]", flags=re.UNICODE) +VARCHAR_CAST_PATTERN = re.compile(r"varchar\(([\d]+)\)") + + +class InvalidColumnName(xhiveframework.ValidationError): + pass + + +class DBTable: + def __init__(self, doctype, meta=None): + self.doctype = doctype + self.table_name = f"tab{doctype}" + self.meta = meta or xhiveframework.get_meta(doctype, False) + self.columns: dict[str, DbColumn] = {} + self.current_columns = {} + + # lists for change + self.add_column: list[DbColumn] = [] + self.change_type: list[DbColumn] = [] + self.change_name: list[DbColumn] = [] + self.add_unique: list[DbColumn] = [] + self.add_index: list[DbColumn] = [] + self.drop_unique: list[DbColumn] = [] + self.drop_index: list[DbColumn] = [] + self.set_default: list[DbColumn] = [] + + # load + self.get_columns_from_docfields() + + def sync(self): + if self.meta.get("is_virtual"): + # no schema to sync for virtual doctypes + return + if self.is_new(): + self.create() + else: + xhiveframework.cache.hdel("table_columns", self.table_name) + self.alter() + + def create(self): + pass + + def get_column_definitions(self): + column_list = [*xhiveframework.db.DEFAULT_COLUMNS] + ret = [] + for k in list(self.columns): + if k not in column_list: + d = self.columns[k].get_definition() + if d: + ret.append("`" + k + "` " + d) + column_list.append(k) + return ret + + def get_index_definitions(self): + return [ + "index `" + key + "`(`" + key + "`)" + for key, col in self.columns.items() + if ( + col.set_index + and not col.unique + and col.fieldtype in xhiveframework.db.type_map + and xhiveframework.db.type_map.get(col.fieldtype)[0] not in ("text", "longtext") + ) + ] + + def get_columns_from_docfields(self): + """ + get columns from docfields and custom fields + """ + fields = self.meta.get_fieldnames_with_value(with_field_meta=True) + + # optional fields like _comments + if not self.meta.get("istable"): + for fieldname in xhiveframework.db.OPTIONAL_COLUMNS: + fields.append({"fieldname": fieldname, "fieldtype": "Text"}) + + # add _seen column if track_seen + if self.meta.get("track_seen"): + fields.append({"fieldname": "_seen", "fieldtype": "Text"}) + + for field in fields: + if field.get("is_virtual"): + continue + + self.columns[field.get("fieldname")] = DbColumn( + self, + field.get("fieldname"), + field.get("fieldtype"), + field.get("length"), + field.get("default"), + field.get("search_index"), + field.get("options"), + field.get("unique"), + field.get("precision"), + ) + + def validate(self): + """Check if change in varchar length isn't truncating the columns""" + if self.is_new(): + return + + self.setup_table_columns() + + columns = [ + xhiveframework._dict({"fieldname": f, "fieldtype": "Data"}) for f in xhiveframework.db.STANDARD_VARCHAR_COLUMNS + ] + if self.meta.get("istable"): + columns += [ + xhiveframework._dict({"fieldname": f, "fieldtype": "Data"}) for f in xhiveframework.db.CHILD_TABLE_COLUMNS + ] + columns += self.columns.values() + + for col in columns: + if len(col.fieldname) >= 64: + xhiveframework.throw( + _("Fieldname is limited to 64 characters ({0})").format(xhiveframework.bold(col.fieldname)) + ) + + if "varchar" in xhiveframework.db.type_map.get(col.fieldtype, ()): + # validate length range + new_length = cint(col.length) or cint(xhiveframework.db.VARCHAR_LEN) + if not (1 <= new_length <= 1000): + xhiveframework.throw(_("Length of {0} should be between 1 and 1000").format(col.fieldname)) + + current_col = self.current_columns.get(col.fieldname, {}) + if not current_col: + continue + current_type = self.current_columns[col.fieldname]["type"] + current_length = VARCHAR_CAST_PATTERN.findall(current_type) + if not current_length: + # case when the field is no longer a varchar + continue + current_length = current_length[0] + if cint(current_length) != cint(new_length): + try: + # check for truncation + max_length = xhiveframework.db.sql( + f"""SELECT MAX(CHAR_LENGTH(`{col.fieldname}`)) FROM `tab{self.doctype}`""" + ) + + except xhiveframework.db.InternalError as e: + if xhiveframework.db.is_missing_column(e): + # Unknown column 'column_name' in 'field list' + continue + raise + + if max_length and max_length[0][0] and max_length[0][0] > new_length: + if col.fieldname in self.columns: + self.columns[col.fieldname].length = current_length + info_message = _( + "Reverting length to {0} for '{1}' in '{2}'. Setting the length as {3} will cause truncation of data." + ).format(current_length, col.fieldname, self.doctype, new_length) + xhiveframework.msgprint(info_message) + + def is_new(self): + return self.table_name not in xhiveframework.db.get_tables() + + def setup_table_columns(self): + # TODO: figure out a way to get key data + for c in xhiveframework.db.get_table_columns_description(self.table_name): + self.current_columns[c.name.lower()] = c + + def alter(self): + pass + + +class DbColumn: + def __init__(self, table, fieldname, fieldtype, length, default, set_index, options, unique, precision): + self.table = table + self.fieldname = fieldname + self.fieldtype = fieldtype + self.length = length + self.set_index = set_index + self.default = default + self.options = options + self.unique = unique + self.precision = precision + + def get_definition(self, for_modification=False): + column_def = get_definition(self.fieldtype, precision=self.precision, length=self.length) + + if not column_def: + return column_def + + if self.fieldtype in ("Check", "Int"): + default_value = cint(self.default) or 0 + column_def += f" not null default {default_value}" + + elif self.fieldtype in ("Currency", "Float", "Percent"): + default_value = flt(self.default) or 0 + column_def += f" not null default {default_value}" + + elif ( + self.default + and (self.default not in xhiveframework.db.DEFAULT_SHORTCUTS) + and not cstr(self.default).startswith(":") + ): + column_def += f" default {xhiveframework.db.escape(self.default)}" + + if self.unique and not for_modification and (column_def not in ("text", "longtext")): + column_def += " unique" + + return column_def + + def build_for_alter_table(self, current_def): + column_type = get_definition(self.fieldtype, self.precision, self.length) + + # no columns + if not column_type: + return + + # to add? + if not current_def: + self.fieldname = validate_column_name(self.fieldname) + self.table.add_column.append(self) + + if column_type not in ("text", "longtext"): + if self.unique: + self.table.add_unique.append(self) + if self.set_index: + self.table.add_index.append(self) + return + + # type + if current_def["type"] != column_type: + self.table.change_type.append(self) + + # unique + if (self.unique and not current_def["unique"]) and column_type not in ("text", "longtext"): + self.table.add_unique.append(self) + elif (current_def["unique"] and not self.unique) and column_type not in ("text", "longtext"): + self.table.drop_unique.append(self) + + # default + if ( + self.default_changed(current_def) + and (self.default not in xhiveframework.db.DEFAULT_SHORTCUTS) + and not cstr(self.default).startswith(":") + ): + self.table.set_default.append(self) + + # index should be applied or dropped irrespective of type change + if (current_def["index"] and not self.set_index) and column_type not in ("text", "longtext"): + self.table.drop_index.append(self) + + elif (not current_def["index"] and self.set_index) and column_type not in ("text", "longtext"): + self.table.add_index.append(self) + + def default_changed(self, current_def): + if "decimal" in current_def["type"]: + return self.default_changed_for_decimal(current_def) + else: + cur_default = current_def["default"] + new_default = self.default + if cur_default == "NULL" or cur_default is None: + cur_default = None + else: + # Strip quotes from default value + # eg. database returns default value as "'System Manager'" + cur_default = cur_default.lstrip("'").rstrip("'") + + fieldtype = self.fieldtype + if fieldtype in ["Int", "Check"]: + cur_default = cint(cur_default) + new_default = cint(new_default) + elif fieldtype in ["Currency", "Float", "Percent"]: + cur_default = flt(cur_default) + new_default = flt(new_default) + return cur_default != new_default + + def default_changed_for_decimal(self, current_def): + try: + if current_def["default"] in ("", None) and self.default in ("", None): + # both none, empty + return False + + elif current_def["default"] in ("", None): + try: + # check if new default value is valid + float(self.default) + return True + except ValueError: + return False + + elif self.default in ("", None): + # new default value is empty + return True + + else: + # NOTE float() raise ValueError when "" or None is passed + return float(current_def["default"]) != float(self.default) + except TypeError: + return True + + +def validate_column_name(n): + if special_characters := SPECIAL_CHAR_PATTERN.findall(n): + special_characters = ", ".join(f'"{c}"' for c in special_characters) + xhiveframework.throw( + _("Fieldname {0} cannot have special characters like {1}").format( + xhiveframework.bold(cstr(n)), special_characters + ), + xhiveframework.db.InvalidColumnName, + ) + return n + + +def validate_column_length(fieldname): + if len(fieldname) > xhiveframework.db.MAX_COLUMN_LENGTH: + xhiveframework.throw(_("Fieldname is limited to 64 characters ({0})").format(fieldname)) + + +def get_definition(fieldtype, precision=None, length=None): + d = xhiveframework.db.type_map.get(fieldtype) + + if not d: + return + + if fieldtype == "Int" and length and length > 11: + # convert int to long int if the length of the int is greater than 11 + d = xhiveframework.db.type_map.get("Long Int") + + coltype = d[0] + size = d[1] if d[1] else None + + if size: + # This check needs to exist for backward compatibility. + # Till V13, default size used for float, currency and percent are (18, 6). + if fieldtype in ["Float", "Currency", "Percent"] and cint(precision) > 6: + size = "21,9" + + if length: + if coltype == "varchar": + size = length + elif coltype == "int" and length < 11: + # allow setting custom length for int if length provided is less than 11 + # NOTE: this will only be applicable for mariadb as xhiveframework implements int + # in postgres as bigint (as seen in type_map) + size = length + + if size is not None: + coltype = f"{coltype}({size})" + + return coltype + + +def add_column(doctype, column_name, fieldtype, precision=None, length=None, default=None, not_null=False): + xhiveframework.db.commit() + query = "alter table `tab{}` add column if not exists {} {}".format( + doctype, + column_name, + get_definition(fieldtype, precision, length), + ) + + if not_null: + query += " not null" + if default: + query += f" default '{default}'" + + xhiveframework.db.sql(query) diff --git a/xhiveframework/database/sequence.py b/xhiveframework/database/sequence.py new file mode 100644 index 0000000..3c83b6d --- /dev/null +++ b/xhiveframework/database/sequence.py @@ -0,0 +1,99 @@ +from xhiveframework import db, scrub + +# NOTE: +# FOR MARIADB - using no cache - as during backup, if the sequence was used in anyform, +# it drops the cache and uses the next non cached value in setval query and +# puts that in the backup file, which will start the counter +# from that value when inserting any new record in the doctype. +# By default the cache is 1000 which will mess up the sequence when +# using the system after a restore. +# +# Another case could be if the cached values expire then also there is a chance of +# the cache being skipped. +# +# FOR POSTGRES - The sequence cache for postgres is per connection. +# Since we're opening and closing connections for every request this results in skipping the cache +# to the next non-cached value hence not using cache in postgres. +# ref: https://stackoverflow.com/questions/21356375/postgres-9-0-4-sequence-skipping-numbers +SEQUENCE_CACHE = 0 + + +def create_sequence( + doctype_name: str, + *, + slug: str = "_id_seq", + temporary: bool = False, + check_not_exists: bool = False, + cycle: bool = False, + cache: int = SEQUENCE_CACHE, + start_value: int = 0, + increment_by: int = 0, + min_value: int = 0, + max_value: int = 0, +) -> str: + query = "create sequence" if not temporary else "create temporary sequence" + sequence_name = scrub(doctype_name + slug) + + if check_not_exists: + query += " if not exists" + + query += f" {sequence_name}" + + if increment_by: + # default is 1 + query += f" increment by {increment_by}" + + if min_value: + # default is 1 + query += f" minvalue {min_value}" + + if max_value: + query += f" maxvalue {max_value}" + + if start_value: + # default is 1 + query += f" start {start_value}" + + # in postgres, the default is cache 1 / no cache + if cache: + query += f" cache {cache}" + elif db.db_type == "mariadb": + query += " nocache" + + if not cycle: + # in postgres, default is no cycle + if db.db_type == "mariadb": + query += " nocycle" + else: + query += " cycle" + + db.sql_ddl(query) + + return sequence_name + + +def get_next_val(doctype_name: str, slug: str = "_id_seq") -> int: + sequence_name = scrub(f"{doctype_name}{slug}") + + if db.db_type == "postgres": + sequence_name = f"'\"{sequence_name}\"'" + elif db.db_type == "mariadb": + sequence_name = f"`{sequence_name}`" + + try: + return db.sql(f"SELECT nextval({sequence_name})")[0][0] + except IndexError: + raise db.SequenceGeneratorLimitExceeded + + +def set_next_val( + doctype_name: str, next_val: int, *, slug: str = "_id_seq", is_val_used: bool = False +) -> None: + is_val_used = "false" if not is_val_used else "true" + + db.multisql( + { + "postgres": f"SELECT SETVAL('\"{scrub(doctype_name + slug)}\"', {next_val}, {is_val_used})", + "mariadb": f"SELECT SETVAL(`{scrub(doctype_name + slug)}`, {next_val}, {is_val_used})", + } + ) diff --git a/xhiveframework/database/utils.py b/xhiveframework/database/utils.py new file mode 100644 index 0000000..6301e6d --- /dev/null +++ b/xhiveframework/database/utils.py @@ -0,0 +1,78 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import typing +from functools import cached_property +from types import NoneType + +import xhiveframework +from xhiveframework.query_builder.builder import MariaDB, Postgres +from xhiveframework.query_builder.functions import Function + +if typing.TYPE_CHECKING: + from xhiveframework.query_builder import DocType + +Query = str | MariaDB | Postgres +QueryValues = tuple | list | dict | NoneType + +EmptyQueryValues = object() +FallBackDateTimeStr = "0001-01-01 00:00:00.000000" +DefaultOrderBy = "KEEP_DEFAULT_ORDERING" +NestedSetHierarchy = ( + "ancestors of", + "descendants of", + "not ancestors of", + "not descendants of", + "descendants of (inclusive)", +) + + +def is_query_type(query: str, query_type: str | tuple[str]) -> bool: + return query.lstrip().split(maxsplit=1)[0].lower().startswith(query_type) + + +def is_pypika_function_object(field: str) -> bool: + return getattr(field, "__module__", None) == "pypika.functions" or isinstance(field, Function) + + +def get_doctype_name(table_name: str) -> str: + if table_name.startswith(("tab", "`tab", '"tab')): + table_name = table_name.replace("tab", "", 1) + table_name = table_name.replace("`", "") + return table_name.replace('"', "") + + +class LazyString: + def _setup(self) -> None: + raise NotImplementedError + + @cached_property + def value(self) -> str: + return self._setup() + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"'{self.value}'" + + +class LazyDecode(LazyString): + __slots__ = () + + def __init__(self, value: str) -> None: + self._value = value + + def _setup(self) -> None: + return self._value.decode() + + +class LazyMogrify(LazyString): + __slots__ = () + + def __init__(self, query, values) -> None: + self.query = query + self.values = values + + def _setup(self) -> str: + return xhiveframework.db.mogrify(self.query, self.values) diff --git a/xhiveframework/defaults.py b/xhiveframework/defaults.py new file mode 100644 index 0000000..cd03ee4 --- /dev/null +++ b/xhiveframework/defaults.py @@ -0,0 +1,264 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.cache_manager import clear_defaults_cache, common_default_keys +from xhiveframework.query_builder import DocType + +# Note: DefaultValue records are identified by parent (e.g. __default, __global) + + +def set_user_default(key, value, user=None, parenttype=None): + set_default(key, value, user or xhiveframework.session.user, parenttype) + + +def add_user_default(key, value, user=None, parenttype=None): + add_default(key, value, user or xhiveframework.session.user, parenttype) + + +def get_user_default(key, user=None): + user_defaults = get_defaults(user or xhiveframework.session.user) + d = user_defaults.get(key, None) + + if is_a_user_permission_key(key): + if d and isinstance(d, list | tuple) and len(d) == 1: + # Use User Permission value when only when it has a single value + d = d[0] + else: + d = user_defaults.get(xhiveframework.scrub(key), None) + user_permission_default = get_user_permission_default(key, user_defaults) + if not d: + # If no default value is found, use the User Permission value + d = user_permission_default + + value = isinstance(d, list | tuple) and d[0] or d + if not_in_user_permission(key, value, user): + return + + return value + + +def get_user_permission_default(key, defaults): + permissions = get_user_permissions() + user_default = "" + if permissions.get(key): + # global default in user permission + for item in permissions.get(key): + doc = item.get("doc") + if defaults.get(key) == doc: + user_default = doc + + for item in permissions.get(key): + if item.get("is_default"): + user_default = item.get("doc") + break + + return user_default + + +def get_user_default_as_list(key, user=None): + user_defaults = get_defaults(user or xhiveframework.session.user) + d = user_defaults.get(key, None) + + if is_a_user_permission_key(key): + if d and isinstance(d, list | tuple) and len(d) == 1: + # Use User Permission value when only when it has a single value + d = [d[0]] + + else: + d = user_defaults.get(xhiveframework.scrub(key), None) + + d = list(filter(None, (not isinstance(d, list | tuple)) and [d] or d)) + + # filter default values if not found in user permission + return [value for value in d if not not_in_user_permission(key, value)] + + +def is_a_user_permission_key(key): + return ":" not in key and key != xhiveframework.scrub(key) + + +def not_in_user_permission(key, value, user=None): + # returns true or false based on if value exist in user permission + user = user or xhiveframework.session.user + user_permission = get_user_permissions(user).get(xhiveframework.unscrub(key)) or [] + + for perm in user_permission: + # doc found in user permission + if perm.get("doc") == value: + return False + + # return true only if user_permission exists + return True if user_permission else False + + +def get_user_permissions(user=None): + from xhiveframework.core.doctype.user_permission.user_permission import ( + get_user_permissions as _get_user_permissions, + ) + + """Return xhiveframework.core.doctype.user_permissions.user_permissions._get_user_permissions (kept for backward compatibility)""" + return _get_user_permissions(user) + + +def get_defaults(user=None): + global_defaults = get_defaults_for() + + if not user: + user = xhiveframework.session.user if xhiveframework.session else "Guest" + + if not user: + return global_defaults + + defaults = global_defaults.copy() + defaults.update(get_defaults_for(user)) + defaults.update(user=user, owner=user) + + return defaults + + +def clear_user_default(key, user=None): + clear_default(key, parent=user or xhiveframework.session.user) + + +# Global + + +def set_global_default(key, value): + set_default(key, value, "__default") + + +def add_global_default(key, value): + add_default(key, value, "__default") + + +def get_global_default(key): + d = get_defaults().get(key, None) + + value = isinstance(d, list | tuple) and d[0] or d + if not_in_user_permission(key, value): + return + + return value + + +# Common + + +def set_default(key, value, parent, parenttype="__default"): + """Override or add a default value. + Adds default value in table `tabDefaultValue`. + + :param key: Default key. + :param value: Default value. + :param parent: Usually, **User** to whom the default belongs. + :param parenttype: [optional] default is `__default`.""" + table = DocType("DefaultValue") + key_exists = ( + xhiveframework.qb.from_(table) + .where((table.defkey == key) & (table.parent == parent)) + .select(table.defkey) + .for_update() + .run() + ) + if key_exists: + xhiveframework.db.delete("DefaultValue", {"defkey": key, "parent": parent}) + if value is not None: + add_default(key, value, parent) + else: + _clear_cache(parent) + + +def add_default(key, value, parent, parenttype=None): + d = xhiveframework.get_doc( + { + "doctype": "DefaultValue", + "parent": parent, + "parenttype": parenttype or "__default", + "parentfield": "system_defaults", + "defkey": key, + "defvalue": value, + } + ) + d.insert(ignore_permissions=True) + _clear_cache(parent) + + +def clear_default(key=None, value=None, parent=None, name=None, parenttype=None): + """Clear a default value by any of the given parameters and delete caches. + + :param key: Default key. + :param value: Default value. + :param parent: User name, or `__global`, `__default`. + :param name: Default ID. + :param parenttype: Clear defaults table for a particular type e.g. **User**. + """ + filters = {} + + if name: + filters.update({"name": name}) + + else: + if key: + filters.update({"defkey": key}) + + if value: + filters.update({"defvalue": value}) + + if parent: + filters.update({"parent": parent}) + + if parenttype: + filters.update({"parenttype": parenttype}) + + if parent: + clear_defaults_cache(parent) + else: + clear_defaults_cache("__default") + clear_defaults_cache("__global") + + if not filters: + raise Exception("[clear_default] No key specified.") + + xhiveframework.db.delete("DefaultValue", filters) + + _clear_cache(parent) + + +def get_defaults_for(parent="__default"): + """get all defaults""" + defaults = xhiveframework.cache.hget("defaults", parent) + + if defaults is None: + # sort descending because first default must get precedence + table = DocType("DefaultValue") + res = ( + xhiveframework.qb.from_(table) + .where(table.parent == parent) + .select(table.defkey, table.defvalue) + .orderby("creation") + .run(as_dict=True) + ) + + defaults = xhiveframework._dict() + for d in res: + if d.defkey in defaults: + # listify + if not isinstance(defaults[d.defkey], list) and defaults[d.defkey] != d.defvalue: + defaults[d.defkey] = [defaults[d.defkey]] + + if d.defvalue not in defaults[d.defkey]: + defaults[d.defkey].append(d.defvalue) + + elif d.defvalue is not None: + defaults[d.defkey] = d.defvalue + + xhiveframework.cache.hset("defaults", parent, defaults) + + return defaults + + +def _clear_cache(parent): + if xhiveframework.flags.in_install: + return + xhiveframework.clear_cache(user=parent if parent not in common_default_keys else None) diff --git a/xhiveframework/deferred_insert.py b/xhiveframework/deferred_insert.py new file mode 100644 index 0000000..73eac4d --- /dev/null +++ b/xhiveframework/deferred_insert.py @@ -0,0 +1,59 @@ +import json +from typing import TYPE_CHECKING, Union + +import redis + +import xhiveframework +from xhiveframework.utils import cstr + +if TYPE_CHECKING: + from xhiveframework.model.document import Document + +queue_prefix = "insert_queue_for_" + + +def deferred_insert(doctype: str, records: list[Union[dict, "Document"]] | str): + if isinstance(records, dict | list): + _records = json.dumps(records) + else: + _records = records + + try: + xhiveframework.cache.rpush(f"{queue_prefix}{doctype}", _records) + except redis.exceptions.ConnectionError: + for record in records: + insert_record(record, doctype) + + +def save_to_db(): + queue_keys = xhiveframework.cache.get_keys(queue_prefix) + for key in queue_keys: + record_count = 0 + queue_key = get_key_name(key) + doctype = get_doctype_name(key) + while xhiveframework.cache.llen(queue_key) > 0 and record_count <= 500: + records = xhiveframework.cache.lpop(queue_key) + records = json.loads(records.decode("utf-8")) + if isinstance(records, dict): + record_count += 1 + insert_record(records, doctype) + continue + for record in records: + record_count += 1 + insert_record(record, doctype) + + +def insert_record(record: Union[dict, "Document"], doctype: str): + try: + record.update({"doctype": doctype}) + xhiveframework.get_doc(record).insert() + except Exception as e: + xhiveframework.logger().error(f"Error while inserting deferred {doctype} record: {e}") + + +def get_key_name(key: str) -> str: + return cstr(key).split("|")[1] + + +def get_doctype_name(key: str) -> str: + return cstr(key).split(queue_prefix)[1] diff --git a/xhiveframework/desk/__init__.py b/xhiveframework/desk/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/desk/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/desk/calendar.py b/xhiveframework/desk/calendar.py new file mode 100644 index 0000000..994a10f --- /dev/null +++ b/xhiveframework/desk/calendar.py @@ -0,0 +1,57 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ + + +@xhiveframework.whitelist() +def update_event(args, field_map): + """Updates Event (called via calendar) based on passed `field_map`""" + args = xhiveframework._dict(json.loads(args)) + field_map = xhiveframework._dict(json.loads(field_map)) + w = xhiveframework.get_doc(args.doctype, args.name) + w.set(field_map.start, args[field_map.start]) + w.set(field_map.end, args.get(field_map.end)) + w.save() + + +def get_event_conditions(doctype, filters=None): + """Returns SQL conditions with user permissions and filters for event queries""" + from xhiveframework.desk.reportview import get_filters_cond + + if not xhiveframework.has_permission(doctype): + xhiveframework.throw(_("Not Permitted"), xhiveframework.PermissionError) + + return get_filters_cond(doctype, filters, [], with_match_conditions=True) + + +@xhiveframework.whitelist() +def get_events(doctype, start, end, field_map, filters=None, fields=None): + field_map = xhiveframework._dict(json.loads(field_map)) + fields = xhiveframework.parse_json(fields) + + doc_meta = xhiveframework.get_meta(doctype) + for d in doc_meta.fields: + if d.fieldtype == "Color": + field_map.update({"color": d.fieldname}) + + filters = json.loads(filters) if filters else [] + + if not fields: + fields = [field_map.start, field_map.end, field_map.title, "name"] + + if field_map.color: + fields.append(field_map.color) + + start_date = "ifnull(%s, '0001-01-01 00:00:00')" % field_map.start + end_date = "ifnull(%s, '2199-12-31 00:00:00')" % field_map.end + + filters += [ + [doctype, start_date, "<=", end], + [doctype, end_date, ">=", start], + ] + fields = list({field for field in fields if field}) + return xhiveframework.get_list(doctype, fields=fields, filters=filters) diff --git a/xhiveframework/desk/desk_page.py b/xhiveframework/desk/desk_page.py new file mode 100644 index 0000000..0de2ad2 --- /dev/null +++ b/xhiveframework/desk/desk_page.py @@ -0,0 +1,33 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +@xhiveframework.whitelist() +def get(name): + """ + Return the :term:`doclist` of the `Page` specified by `name` + """ + page = xhiveframework.get_doc("Page", name) + if page.is_permitted(): + page.load_assets() + docs = xhiveframework._dict(page.as_dict()) + if getattr(page, "_dynamic_page", None): + docs["_dynamic_page"] = 1 + + return docs + else: + xhiveframework.response["403"] = 1 + raise xhiveframework.PermissionError("No read permission for Page %s" % (page.title or name)) + + +@xhiveframework.whitelist(allow_guest=True) +def getpage(): + """ + Load the page from `xhiveframework.form` and send it via `xhiveframework.response` + """ + page = xhiveframework.form_dict.get("name") + doc = get(page) + + xhiveframework.response.docs.append(doc) diff --git a/xhiveframework/desk/desktop.py b/xhiveframework/desk/desktop.py new file mode 100644 index 0000000..7e2c8cd --- /dev/null +++ b/xhiveframework/desk/desktop.py @@ -0,0 +1,668 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +# Author - Shivam Mishra + +from functools import wraps +from json import dumps, loads + +import xhiveframework +from xhiveframework import DoesNotExistError, ValidationError, _, _dict +from xhiveframework.boot import get_allowed_pages, get_allowed_reports +from xhiveframework.cache_manager import ( + build_domain_restriced_doctype_cache, + build_domain_restriced_page_cache, + build_table_count_cache, +) +from xhiveframework.core.doctype.custom_role.custom_role import get_custom_allowed_roles + + +def handle_not_exist(fn): + @wraps(fn) + def wrapper(*args, **kwargs): + try: + return fn(*args, **kwargs) + except DoesNotExistError: + xhiveframework.clear_last_message() + return [] + + return wrapper + + +class Workspace: + def __init__(self, page, minimal=False): + self.page_name = page.get("name") + self.page_title = page.get("title") + self.public_page = page.get("public") + self.workspace_manager = "Workspace Manager" in xhiveframework.get_roles() + + self.user = xhiveframework.get_user() + self.allowed_modules = self.get_cached("user_allowed_modules", self.get_allowed_modules) + + self.doc = xhiveframework.get_cached_doc("Workspace", self.page_name) + if ( + self.doc + and self.doc.module + and self.doc.module not in self.allowed_modules + and not self.workspace_manager + ): + raise xhiveframework.PermissionError + + self.can_read = self.get_cached("user_perm_can_read", self.get_can_read_items) + + self.allowed_pages = get_allowed_pages(cache=True) + self.allowed_reports = get_allowed_reports(cache=True) + + if not minimal: + if self.doc.content: + self.onboarding_list = [ + x["data"]["onboarding_name"] for x in loads(self.doc.content) if x["type"] == "onboarding" + ] + self.onboardings = [] + + self.table_counts = get_table_with_counts() + self.restricted_doctypes = ( + xhiveframework.cache.get_value("domain_restricted_doctypes") or build_domain_restriced_doctype_cache() + ) + self.restricted_pages = ( + xhiveframework.cache.get_value("domain_restricted_pages") or build_domain_restriced_page_cache() + ) + + def is_permitted(self): + """Returns true if Has Role is not set or the user is allowed.""" + from xhiveframework.utils import has_common + + allowed = [d.role for d in self.doc.roles] + + custom_roles = get_custom_allowed_roles("page", self.doc.name) + allowed.extend(custom_roles) + + if not allowed: + return True + + roles = xhiveframework.get_roles() + + if has_common(roles, allowed): + return True + + def get_cached(self, cache_key, fallback_fn): + value = xhiveframework.cache.get_value(cache_key, user=xhiveframework.session.user) + if value: + return value + + value = fallback_fn() + + # Expire every six hour + xhiveframework.cache.set_value(cache_key, value, xhiveframework.session.user, 21600) + return value + + def get_can_read_items(self): + if not self.user.can_read: + self.user.build_permissions() + + return self.user.can_read + + def get_allowed_modules(self): + if not self.user.allow_modules: + self.user.build_permissions() + + return self.user.allow_modules + + def get_onboarding_doc(self, onboarding): + # Check if onboarding is enabled + if not xhiveframework.get_system_settings("enable_onboarding"): + return None + + if not self.onboarding_list: + return None + + if xhiveframework.db.get_value("Module Onboarding", onboarding, "is_complete"): + return None + + doc = xhiveframework.get_doc("Module Onboarding", onboarding) + + # Check if user is allowed + allowed_roles = set(doc.get_allowed_roles()) + user_roles = set(xhiveframework.get_roles()) + if not allowed_roles & user_roles: + return None + + # Check if already complete + if doc.check_completion(): + return None + + return doc + + def is_item_allowed(self, name, item_type): + if xhiveframework.session.user == "Administrator": + return True + + item_type = item_type.lower() + + if item_type == "doctype": + return name in self.can_read or [] and name in self.restricted_doctypes or [] + if item_type == "page": + return name in self.allowed_pages and name in self.restricted_pages + if item_type == "report": + return name in self.allowed_reports + if item_type == "help": + return True + if item_type == "dashboard": + return True + if item_type == "url": + return True + + return False + + def build_workspace(self): + self.cards = {"items": self.get_links()} + self.charts = {"items": self.get_charts()} + self.shortcuts = {"items": self.get_shortcuts()} + self.onboardings = {"items": self.get_onboardings()} + self.quick_lists = {"items": self.get_quick_lists()} + self.number_cards = {"items": self.get_number_cards()} + self.custom_blocks = {"items": self.get_custom_blocks()} + + def _doctype_contains_a_record(self, name): + exists = self.table_counts.get(name, False) + + if not exists and xhiveframework.db.exists(name): + if not xhiveframework.db.get_value("DocType", name, "issingle"): + exists = bool(xhiveframework.get_all(name, limit=1)) + else: + exists = True + self.table_counts[name] = exists + + return exists + + def _prepare_item(self, item): + if item.dependencies: + dependencies = [dep.strip() for dep in item.dependencies.split(",")] + + incomplete_dependencies = [d for d in dependencies if not self._doctype_contains_a_record(d)] + + if len(incomplete_dependencies): + item.incomplete_dependencies = incomplete_dependencies + else: + item.incomplete_dependencies = "" + + if item.onboard: + # Mark Spotlights for initial + if item.get("type") == "doctype": + name = item.get("name") + count = self._doctype_contains_a_record(name) + + item["count"] = count + + if item.get("link_type") == "DocType": + item["description"] = xhiveframework.get_meta(item.link_to).description + + # Translate label + item["label"] = _(item.label) if item.label else _(item.name) + + return item + + def is_custom_block_permitted(self, custom_block_name): + from xhiveframework.utils import has_common + + allowed = [ + d.role for d in xhiveframework.get_all("Has Role", fields=["role"], filters={"parent": custom_block_name}) + ] + + if not allowed: + return True + + roles = xhiveframework.get_roles() + + if has_common(roles, allowed): + return True + + return False + + @handle_not_exist + def get_links(self): + cards = self.doc.get_link_groups() + + if not self.doc.hide_custom: + cards = cards + get_custom_reports_and_doctypes(self.doc.module) + + default_country = xhiveframework.db.get_default("country") + + new_data = [] + for card in cards: + new_items = [] + card = _dict(card) + + links = card.get("links", []) + + for item in links: + item = _dict(item) + + # Condition: based on country + if item.country and item.country != default_country: + continue + + # Check if user is allowed to view + if self.is_item_allowed(item.link_to, item.link_type): + prepared_item = self._prepare_item(item) + new_items.append(prepared_item) + + if new_items: + if isinstance(card, _dict): + new_card = card.copy() + else: + new_card = card.as_dict().copy() + new_card["links"] = new_items + new_card["label"] = _(new_card["label"]) + new_data.append(new_card) + + return new_data + + @handle_not_exist + def get_charts(self): + all_charts = [] + if xhiveframework.has_permission("Dashboard Chart", throw=False): + charts = self.doc.charts + + for chart in charts: + if xhiveframework.has_permission("Dashboard Chart", doc=chart.chart_name): + # Translate label + chart.label = _(chart.label) if chart.label else _(chart.chart_name) + all_charts.append(chart) + + return all_charts + + @handle_not_exist + def get_shortcuts(self): + def _in_active_domains(item): + if not item.restrict_to_domain: + return True + else: + return item.restrict_to_domain in xhiveframework.get_active_domains() + + items = [] + shortcuts = self.doc.shortcuts + + for item in shortcuts: + new_item = item.as_dict().copy() + if self.is_item_allowed(item.link_to, item.type) and _in_active_domains(item): + if item.type == "Report": + report = self.allowed_reports.get(item.link_to, {}) + if report.get("report_type") in ["Query Report", "Script Report", "Custom Report"]: + new_item["is_query_report"] = 1 + else: + new_item["ref_doctype"] = report.get("ref_doctype") + + # Translate label + new_item["label"] = _(item.label) if item.label else _(item.link_to) + + items.append(new_item) + + return items + + @handle_not_exist + def get_quick_lists(self): + items = [] + quick_lists = self.doc.quick_lists + + for item in quick_lists: + if self.is_item_allowed(item.document_type, "doctype"): + new_item = item.as_dict().copy() + + # Translate label + new_item["label"] = _(item.label) if item.label else _(item.document_type) + + items.append(new_item) + + return items + + @handle_not_exist + def get_onboardings(self): + if self.onboarding_list: + for onboarding in self.onboarding_list: + onboarding_doc = self.get_onboarding_doc(onboarding) + if onboarding_doc: + item = { + "label": _(onboarding), + "title": _(onboarding_doc.title), + "subtitle": _(onboarding_doc.subtitle), + "success": _(onboarding_doc.success_message), + "docs_url": onboarding_doc.documentation_url, + "items": self.get_onboarding_steps(onboarding_doc), + } + self.onboardings.append(item) + return self.onboardings + + @handle_not_exist + def get_onboarding_steps(self, onboarding_doc): + steps = [] + for doc in onboarding_doc.get_steps(): + step = doc.as_dict().copy() + step.label = _(doc.title) + if step.action == "Create Entry": + step.is_submittable = xhiveframework.db.get_value( + "DocType", step.reference_document, "is_submittable", cache=True + ) + steps.append(step) + + return steps + + @handle_not_exist + def get_number_cards(self): + all_number_cards = [] + if xhiveframework.has_permission("Number Card", throw=False): + number_cards = self.doc.number_cards + for number_card in number_cards: + if xhiveframework.has_permission("Number Card", doc=number_card.number_card_name): + # Translate label + number_card.label = ( + _(number_card.label) if number_card.label else _(number_card.number_card_name) + ) + all_number_cards.append(number_card) + + return all_number_cards + + @handle_not_exist + def get_custom_blocks(self): + all_custom_blocks = [] + if xhiveframework.has_permission("Custom HTML Block", throw=False): + custom_blocks = self.doc.custom_blocks + + for custom_block in custom_blocks: + if xhiveframework.has_permission("Custom HTML Block", doc=custom_block.custom_block_name): + if not self.is_custom_block_permitted(custom_block.custom_block_name): + continue + + # Translate label + custom_block.label = ( + _(custom_block.label) if custom_block.label else _(custom_block.custom_block_name) + ) + all_custom_blocks.append(custom_block) + + return all_custom_blocks + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_desktop_page(page): + """Applies permissions, customizations and returns the configruration for a page + on desk. + + Args: + page (json): page data + + Returns: + dict: dictionary of cards, charts and shortcuts to be displayed on website + """ + try: + workspace = Workspace(loads(page)) + workspace.build_workspace() + return { + "charts": workspace.charts, + "shortcuts": workspace.shortcuts, + "cards": workspace.cards, + "onboardings": workspace.onboardings, + "quick_lists": workspace.quick_lists, + "number_cards": workspace.number_cards, + "custom_blocks": workspace.custom_blocks, + } + except DoesNotExistError: + xhiveframework.log_error("Workspace Missing") + return {} + + +@xhiveframework.whitelist() +def get_workspace_sidebar_items(): + """Get list of sidebar items for desk""" + has_access = "Workspace Manager" in xhiveframework.get_roles() + + # don't get domain restricted pages + blocked_modules = xhiveframework.get_doc("User", xhiveframework.session.user).get_blocked_modules() + blocked_modules.append("Dummy Module") + + # adding None to allowed_domains to include pages without domain restriction + allowed_domains = [None, *xhiveframework.get_active_domains()] + + filters = { + "restrict_to_domain": ["in", allowed_domains], + "module": ["not in", blocked_modules], + } + + if has_access: + filters = [] + + # pages sorted based on sequence id + order_by = "sequence_id asc" + fields = [ + "name", + "title", + "for_user", + "parent_page", + "content", + "public", + "module", + "icon", + "indicator_color", + "is_hidden", + ] + all_pages = xhiveframework.get_all( + "Workspace", fields=fields, filters=filters, order_by=order_by, ignore_permissions=True + ) + pages = [] + private_pages = [] + + # Filter Page based on Permission + for page in all_pages: + try: + workspace = Workspace(page, True) + if has_access or workspace.is_permitted(): + if page.public and (has_access or not page.is_hidden) and page.title != "Welcome Workspace": + pages.append(page) + elif page.for_user == xhiveframework.session.user: + private_pages.append(page) + page["label"] = _(page.get("name")) + except xhiveframework.PermissionError: + pass + if private_pages: + pages.extend(private_pages) + + if len(pages) == 0: + pages = [xhiveframework.get_doc("Workspace", "Welcome Workspace").as_dict()] + pages[0]["label"] = _("Welcome Workspace") + + return {"pages": pages, "has_access": has_access} + + +def get_table_with_counts(): + counts = xhiveframework.cache.get_value("information_schema:counts") + if not counts: + counts = build_table_count_cache() + + return counts + + +def get_custom_reports_and_doctypes(module): + return [ + _dict({"label": _("Custom Documents"), "links": get_custom_doctype_list(module)}), + _dict({"label": _("Custom Reports"), "links": get_custom_report_list(module)}), + ] + + +def get_custom_doctype_list(module): + doctypes = xhiveframework.get_all( + "DocType", + fields=["name"], + filters={"custom": 1, "istable": 0, "module": module}, + order_by="name", + ) + + return [ + { + "type": "Link", + "link_type": "doctype", + "link_to": d.name, + "label": _(d.name), + } + for d in doctypes + ] + + +def get_custom_report_list(module): + """Returns list on new style reports for modules.""" + reports = xhiveframework.get_all( + "Report", + fields=["name", "ref_doctype", "report_type"], + filters={"is_standard": "No", "disabled": 0, "module": module}, + order_by="name", + ) + + return [ + { + "type": "Link", + "link_type": "report", + "doctype": r.ref_doctype, + "dependencies": r.ref_doctype, + "is_query_report": 1 + if r.report_type in ("Query Report", "Script Report", "Custom Report") + else 0, + "label": _(r.name), + "link_to": r.name, + } + for r in reports + ] + + +def save_new_widget(doc, page, blocks, new_widgets): + if loads(new_widgets): + widgets = _dict(loads(new_widgets)) + + if widgets.chart: + doc.charts.extend(new_widget(widgets.chart, "Workspace Chart", "charts")) + if widgets.shortcut: + doc.shortcuts.extend(new_widget(widgets.shortcut, "Workspace Shortcut", "shortcuts")) + if widgets.quick_list: + doc.quick_lists.extend(new_widget(widgets.quick_list, "Workspace Quick List", "quick_lists")) + if widgets.custom_block: + doc.custom_blocks.extend( + new_widget(widgets.custom_block, "Workspace Custom Block", "custom_blocks") + ) + if widgets.number_card: + doc.number_cards.extend(new_widget(widgets.number_card, "Workspace Number Card", "number_cards")) + if widgets.card: + doc.build_links_table_from_card(widgets.card) + + # remove duplicate and unwanted widgets + clean_up(doc, blocks) + + try: + doc.save(ignore_permissions=True) + except (ValidationError, TypeError) as e: + # Create a json string to log + json_config = widgets and dumps(widgets, sort_keys=True, indent=4) + + # Error log body + log = f""" + page: {page} + config: {json_config} + exception: {e} + """ + doc.log_error("Could not save customization", log) + return False + + return True + + +def clean_up(original_page, blocks): + page_widgets = {} + + for wid in ["shortcut", "card", "chart", "quick_list", "number_card", "custom_block"]: + # get list of widget's name from blocks + page_widgets[wid] = [x["data"][wid + "_name"] for x in loads(blocks) if x["type"] == wid] + + # shortcut, chart, quick_list, number_card & custom_block cleanup + for wid in ["shortcut", "chart", "quick_list", "number_card", "custom_block"]: + updated_widgets = [] + original_page.get(wid + "s").reverse() + + for w in original_page.get(wid + "s"): + if w.label in page_widgets[wid] and w.label not in [x.label for x in updated_widgets]: + updated_widgets.append(w) + original_page.set(wid + "s", updated_widgets) + + # card cleanup + for i, v in enumerate(original_page.links): + if v.type == "Card Break" and v.label not in page_widgets["card"]: + del original_page.links[i : i + v.link_count + 1] + + +def new_widget(config, doctype, parentfield): + if not config: + return [] + prepare_widget_list = [] + for idx, widget in enumerate(config): + # Some cleanup + widget.pop("name", None) + + # New Doc + doc = xhiveframework.new_doc(doctype) + doc.update(widget) + + # Manually Set IDX + doc.idx = idx + 1 + + # Set Parent Field + doc.parentfield = parentfield + + prepare_widget_list.append(doc) + return prepare_widget_list + + +def prepare_widget(config, doctype, parentfield): + """Create widget child table entries with parent details + + Args: + config (dict): Dictionary containing widget config + doctype (string): Doctype name of the child table + parentfield (string): Parent field for the child table + + Returns: + TYPE: List of Document objects + """ + if not config: + return [] + order = config.get("order") + widgets = config.get("widgets") + prepare_widget_list = [] + for idx, name in enumerate(order): + wid_config = widgets[name].copy() + # Some cleanup + wid_config.pop("name", None) + + # New Doc + doc = xhiveframework.new_doc(doctype) + doc.update(wid_config) + + # Manually Set IDX + doc.idx = idx + 1 + + # Set Parent Field + doc.parentfield = parentfield + + prepare_widget_list.append(doc) + return prepare_widget_list + + +@xhiveframework.whitelist() +def update_onboarding_step(name, field, value): + """Update status of onboaridng step + + Args: + name (string): Name of the doc + field (string): field to be updated + value: Value to be updated + + """ + from xhiveframework.utils.telemetry import capture + + xhiveframework.db.set_value("Onboarding Step", name, field, value) + + capture(xhiveframework.scrub(name), app="xhiveframework_onboarding", properties={field: value}) diff --git a/xhiveframework/desk/doctype/__init__.py b/xhiveframework/desk/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/bulk_update/__init__.py b/xhiveframework/desk/doctype/bulk_update/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/bulk_update/bulk_update.js b/xhiveframework/desk/doctype/bulk_update/bulk_update.js new file mode 100644 index 0000000..eab9201 --- /dev/null +++ b/xhiveframework/desk/doctype/bulk_update/bulk_update.js @@ -0,0 +1,58 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Bulk Update", { + refresh: function (frm) { + frm.set_query("document_type", function () { + return { + filters: [ + ["DocType", "issingle", "=", 0], + ["DocType", "name", "not in", xhiveframework.model.core_doctypes_list], + ], + }; + }); + + frm.page.set_primary_action(__("Update"), function () { + if (!frm.doc.update_value) { + xhiveframework.throw(__('Field "value" is mandatory. Please specify value to be updated')); + } else { + frm.call("bulk_update").then((r) => { + let failed = r.message; + if (!failed) failed = []; + + if (failed.length && !r._server_messages) { + xhiveframework.throw( + __("Cannot update {0}", [ + failed.map((f) => (f.bold ? f.bold() : f)).join(", "), + ]) + ); + } else { + xhiveframework.msgprint({ + title: __("Success"), + message: __("Updated Successfully"), + indicator: "green", + }); + } + + xhiveframework.hide_progress(); + frm.save(); + }); + } + }); + }, + + document_type: function (frm) { + // set field options + if (!frm.doc.document_type) return; + + xhiveframework.model.with_doctype(frm.doc.document_type, function () { + var options = $.map(xhiveframework.get_meta(frm.doc.document_type).fields, function (d) { + if (d.fieldname && xhiveframework.model.no_value_type.indexOf(d.fieldtype) === -1) { + return d.fieldname; + } + return null; + }); + frm.set_df_property("field", "options", options); + }); + }, +}); diff --git a/xhiveframework/desk/doctype/bulk_update/bulk_update.json b/xhiveframework/desk/doctype/bulk_update/bulk_update.json new file mode 100644 index 0000000..9345851 --- /dev/null +++ b/xhiveframework/desk/doctype/bulk_update/bulk_update.json @@ -0,0 +1,77 @@ +{ + "actions": [], + "creation": "2016-07-15 05:51:29.224123", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "field", + "update_value", + "condition", + "limit" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Field", + "reqd": 1 + }, + { + "fieldname": "update_value", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Update Value", + "reqd": 1 + }, + { + "bold": 1, + "description": "SQL Conditions. Example: status=\"Open\"", + "fieldname": "condition", + "fieldtype": "Small Text", + "label": "Condition" + }, + { + "bold": 1, + "default": "500", + "description": "Max 500 records at a time", + "fieldname": "limit", + "fieldtype": "Int", + "label": "Limit" + } + ], + "issingle": 1, + "links": [], + "modified": "2022-08-03 12:20:50.742376", + "modified_by": "Administrator", + "module": "Desk", + "name": "Bulk Update", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/bulk_update/bulk_update.py b/xhiveframework/desk/doctype/bulk_update/bulk_update.py new file mode 100644 index 0000000..1d6fc8d --- /dev/null +++ b/xhiveframework/desk/doctype/bulk_update/bulk_update.py @@ -0,0 +1,118 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.doctype.submission_queue.submission_queue import queue_submission +from xhiveframework.model.document import Document +from xhiveframework.utils import cint +from xhiveframework.utils.deprecations import deprecated +from xhiveframework.utils.scheduler import is_scheduler_inactive + + +class BulkUpdate(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + condition: DF.SmallText | None + document_type: DF.Link + field: DF.Literal[None] + limit: DF.Int + update_value: DF.SmallText + + # end: auto-generated types + + @xhiveframework.whitelist() + def bulk_update(self): + self.check_permission("write") + limit = self.limit if self.limit and cint(self.limit) < 500 else 500 + + condition = "" + if self.condition: + if ";" in self.condition: + xhiveframework.throw(_("; not allowed in condition")) + + condition = f" where {self.condition}" + + docnames = xhiveframework.db.sql_list( + f"""select name from `tab{self.document_type}`{condition} limit {limit} offset 0""" + ) + return submit_cancel_or_update_docs( + self.document_type, docnames, "update", {self.field: self.update_value} + ) + + +@xhiveframework.whitelist() +def submit_cancel_or_update_docs(doctype, docnames, action="submit", data=None, task_id=None): + if isinstance(docnames, str): + docnames = xhiveframework.parse_json(docnames) + + if len(docnames) < 20: + return _bulk_action(doctype, docnames, action, data, task_id) + elif len(docnames) <= 500: + xhiveframework.msgprint(_("Bulk operation is enqueued in background."), alert=True) + xhiveframework.enqueue( + _bulk_action, + doctype=doctype, + docnames=docnames, + action=action, + data=data, + task_id=task_id, + queue="short", + timeout=1000, + ) + else: + xhiveframework.throw(_("Bulk operations only support up to 500 documents."), title=_("Too Many Documents")) + + +def _bulk_action(doctype, docnames, action, data, task_id=None): + if data: + data = xhiveframework.parse_json(data) + + failed = [] + num_documents = len(docnames) + + for idx, docname in enumerate(docnames, 1): + doc = xhiveframework.get_doc(doctype, docname) + try: + message = "" + if action == "submit" and doc.docstatus.is_draft(): + if doc.meta.queue_in_background and not is_scheduler_inactive(): + queue_submission(doc, action) + message = _("Queuing {0} for Submission").format(doctype) + else: + doc.submit() + message = _("Submitting {0}").format(doctype) + elif action == "cancel" and doc.docstatus.is_submitted(): + doc.cancel() + message = _("Cancelling {0}").format(doctype) + elif action == "update" and not doc.docstatus.is_cancelled(): + doc.update(data) + doc.save() + message = _("Updating {0}").format(doctype) + else: + failed.append(docname) + xhiveframework.db.commit() + xhiveframework.publish_progress( + percent=idx / num_documents * 100, + title=message, + description=docname, + task_id=task_id, + ) + + except Exception: + failed.append(docname) + xhiveframework.db.rollback() + + return failed + + +@deprecated +def show_progress(docnames, message, i, description): + n = len(docnames) + xhiveframework.publish_progress(float(i) * 100 / n, title=message, description=description) diff --git a/xhiveframework/desk/doctype/bulk_update/test_bulk_update.py b/xhiveframework/desk/doctype/bulk_update/test_bulk_update.py new file mode 100644 index 0000000..5ef08c2 --- /dev/null +++ b/xhiveframework/desk/doctype/bulk_update/test_bulk_update.py @@ -0,0 +1,48 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See LICENSE + +import time + +import xhiveframework +from xhiveframework.core.doctype.doctype.test_doctype import new_doctype +from xhiveframework.desk.doctype.bulk_update.bulk_update import submit_cancel_or_update_docs +from xhiveframework.tests.utils import XhiveFrameworkTestCase, timeout + + +class TestBulkUpdate(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + cls.doctype = new_doctype(is_submittable=1, custom=1).insert().name + xhiveframework.db.commit() + for _ in range(50): + xhiveframework.new_doc(cls.doctype, some_fieldname=xhiveframework.mock("name")).insert() + + @timeout() + def wait_for_assertion(self, assertion): + """Wait till an assertion becomes True""" + while True: + if assertion(): + break + time.sleep(0.2) + + def test_bulk_submit_in_background(self): + unsubmitted = xhiveframework.get_all(self.doctype, {"docstatus": 0}, limit=5, pluck="name") + failed = submit_cancel_or_update_docs(self.doctype, unsubmitted, action="submit") + self.assertEqual(failed, []) + + def check_docstatus(docs, status): + xhiveframework.db.rollback() + matching_docs = xhiveframework.get_all( + self.doctype, {"docstatus": status, "name": ("in", docs)}, pluck="name" + ) + return set(matching_docs) == set(docs) + + unsubmitted = xhiveframework.get_all(self.doctype, {"docstatus": 0}, limit=20, pluck="name") + submit_cancel_or_update_docs(self.doctype, unsubmitted, action="submit") + + self.wait_for_assertion(lambda: check_docstatus(unsubmitted, 1)) + + submitted = xhiveframework.get_all(self.doctype, {"docstatus": 1}, limit=20, pluck="name") + submit_cancel_or_update_docs(self.doctype, submitted, action="cancel") + self.wait_for_assertion(lambda: check_docstatus(submitted, 2)) diff --git a/xhiveframework/desk/doctype/calendar_view/__init__.py b/xhiveframework/desk/doctype/calendar_view/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/calendar_view/calendar_view.js b/xhiveframework/desk/doctype/calendar_view/calendar_view.js new file mode 100644 index 0000000..f3b7c22 --- /dev/null +++ b/xhiveframework/desk/doctype/calendar_view/calendar_view.js @@ -0,0 +1,36 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Calendar View", { + onload: function (frm) { + frm.trigger("reference_doctype"); + }, + refresh: function (frm) { + if (!frm.is_new()) { + frm.add_custom_button(__("Show Calendar"), () => + xhiveframework.set_route("List", frm.doc.reference_doctype, "Calendar", frm.doc.name) + ); + } + }, + reference_doctype: function (frm) { + const { reference_doctype } = frm.doc; + if (!reference_doctype) return; + + xhiveframework.model.with_doctype(reference_doctype, () => { + const meta = xhiveframework.get_meta(reference_doctype); + + const subject_options = meta.fields + .filter((df) => !xhiveframework.model.no_value_type.includes(df.fieldtype)) + .map((df) => df.fieldname); + + const date_options = meta.fields + .filter((df) => ["Date", "Datetime"].includes(df.fieldtype)) + .map((df) => df.fieldname); + + frm.set_df_property("subject_field", "options", subject_options); + frm.set_df_property("start_date_field", "options", date_options); + frm.set_df_property("end_date_field", "options", date_options); + frm.refresh(); + }); + }, +}); diff --git a/xhiveframework/desk/doctype/calendar_view/calendar_view.json b/xhiveframework/desk/doctype/calendar_view/calendar_view.json new file mode 100644 index 0000000..8149446 --- /dev/null +++ b/xhiveframework/desk/doctype/calendar_view/calendar_view.json @@ -0,0 +1,83 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2017-10-23 13:02:10.295824", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "subject_field", + "start_date_field", + "end_date_field", + "column_break_5", + "all_day" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "subject_field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Subject Field", + "reqd": 1 + }, + { + "fieldname": "start_date_field", + "fieldtype": "Select", + "label": "Start Date Field", + "reqd": 1 + }, + { + "fieldname": "end_date_field", + "fieldtype": "Select", + "label": "End Date Field", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "all_day", + "fieldtype": "Check", + "label": "All Day" + } + ], + "links": [], + "modified": "2023-08-28 22:29:39.662726", + "modified_by": "Administrator", + "module": "Desk", + "name": "Calendar View", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Desk User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/calendar_view/calendar_view.py b/xhiveframework/desk/doctype/calendar_view/calendar_view.py new file mode 100644 index 0000000..c61236b --- /dev/null +++ b/xhiveframework/desk/doctype/calendar_view/calendar_view.py @@ -0,0 +1,22 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class CalendarView(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + all_day: DF.Check + end_date_field: DF.Literal[None] + reference_doctype: DF.Link + start_date_field: DF.Literal[None] + subject_field: DF.Literal[None] + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/calendar_view/calendar_view_list.js b/xhiveframework/desk/doctype/calendar_view/calendar_view_list.js new file mode 100644 index 0000000..b5b54d5 --- /dev/null +++ b/xhiveframework/desk/doctype/calendar_view/calendar_view_list.js @@ -0,0 +1,16 @@ +xhiveframework.listview_settings["Calendar View"] = { + button: { + show(doc) { + return doc.name; + }, + get_label() { + return xhiveframework.utils.icon("calendar", "sm"); + }, + get_description(doc) { + return __("View {0}", [`${doc.name}`]); + }, + action(doc) { + xhiveframework.set_route("List", doc.reference_doctype, "Calendar", doc.name); + }, + }, +}; diff --git a/xhiveframework/desk/doctype/console_log/__init__.py b/xhiveframework/desk/doctype/console_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/console_log/console_log.js b/xhiveframework/desk/doctype/console_log/console_log.js new file mode 100644 index 0000000..cd09d81 --- /dev/null +++ b/xhiveframework/desk/doctype/console_log/console_log.js @@ -0,0 +1,12 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Console Log", { + refresh: function (frm) { + frm.add_custom_button(__("Re-Run in Console"), () => { + window.localStorage.setItem("system_console_code", frm.doc.script); + window.localStorage.setItem("system_console_type", frm.doc.type); + xhiveframework.set_route("Form", "System Console"); + }); + }, +}); diff --git a/xhiveframework/desk/doctype/console_log/console_log.json b/xhiveframework/desk/doctype/console_log/console_log.json new file mode 100644 index 0000000..a2955bf --- /dev/null +++ b/xhiveframework/desk/doctype/console_log/console_log.json @@ -0,0 +1,62 @@ +{ + "actions": [], + "autoname": "format:Log on {timestamp}", + "creation": "2020-08-18 19:56:12.336427", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "script", + "type", + "committed" + ], + "fields": [ + { + "fieldname": "script", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Script", + "read_only": 1 + }, + { + "fieldname": "type", + "fieldtype": "Data", + "hidden": 1, + "label": "Type", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "committed", + "fieldtype": "Check", + "label": "Committed", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-03-12 20:35:43.921009", + "modified_by": "Administrator", + "module": "Desk", + "name": "Console Log", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/console_log/console_log.py b/xhiveframework/desk/doctype/console_log/console_log.py new file mode 100644 index 0000000..b715941 --- /dev/null +++ b/xhiveframework/desk/doctype/console_log/console_log.py @@ -0,0 +1,24 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class ConsoleLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + committed: DF.Check + script: DF.Code | None + type: DF.Data | None + # end: auto-generated types + + def after_delete(self): + # because on_trash can be bypassed + xhiveframework.throw(xhiveframework._("Console Logs can not be deleted")) diff --git a/xhiveframework/desk/doctype/console_log/test_console_log.py b/xhiveframework/desk/doctype/console_log/test_console_log.py new file mode 100644 index 0000000..90d1542 --- /dev/null +++ b/xhiveframework/desk/doctype/console_log/test_console_log.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestConsoleLog(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/custom_html_block/__init__.py b/xhiveframework/desk/doctype/custom_html_block/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/custom_html_block/custom_html_block.js b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.js new file mode 100644 index 0000000..3c15eed --- /dev/null +++ b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.js @@ -0,0 +1,23 @@ +// Copyright (c) 2023, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Custom HTML Block", { + refresh(frm) { + if ( + !has_common(xhiveframework.user_roles, [ + "Administrator", + "System Manager", + "Workspace Manager", + ]) + ) { + frm.set_value("private", true); + } else { + frm.set_df_property("private", "read_only", false); + } + + let wrapper = frm.fields_dict["preview"].wrapper; + wrapper.classList.add("mb-3"); + + xhiveframework.create_shadow_element(wrapper, frm.doc.html, frm.doc.style, frm.doc.script); + }, +}); diff --git a/xhiveframework/desk/doctype/custom_html_block/custom_html_block.json b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.json new file mode 100644 index 0000000..f65ba51 --- /dev/null +++ b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "prompt", + "creation": "2023-05-17 13:58:37.311045", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "private", + "preview_section", + "preview", + "html_section", + "html", + "javascript_section", + "js_message", + "script", + "css_section", + "style", + "roles_section", + "roles" + ], + "fields": [ + { + "collapsible": 1, + "collapsible_depends_on": "eval:true;", + "fieldname": "html_section", + "fieldtype": "Section Break", + "label": "HTML" + }, + { + "fieldname": "html", + "fieldtype": "Code", + "options": "HTML" + }, + { + "fieldname": "preview_section", + "fieldtype": "Section Break", + "label": "Preview" + }, + { + "fieldname": "preview", + "fieldtype": "HTML" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:true;", + "fieldname": "javascript_section", + "fieldtype": "Section Break", + "label": "Javascript" + }, + { + "fieldname": "script", + "fieldtype": "Code", + "options": "JS" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:true;", + "fieldname": "css_section", + "fieldtype": "Section Break", + "label": "CSS" + }, + { + "fieldname": "style", + "fieldtype": "Code", + "options": "CSS" + }, + { + "fieldname": "js_message", + "fieldtype": "HTML", + "label": "JS Message", + "options": "

    To interact with above HTML you will have to use `root_element` as a parent selector.

    For example:

    // here root_element is provided by default\nlet some_class_element = root_element.querySelector('.some-class');\nsome_class_element.textContent = \"New content\";\n
    " + }, + { + "fieldname": "roles_section", + "fieldtype": "Section Break", + "label": "Roles" + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "label": "Roles", + "options": "Has Role" + }, + { + "default": "0", + "depends_on": "eval: doc.private || doc.__unsaved", + "fieldname": "private", + "fieldtype": "Check", + "label": "Private", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 20:25:00.740795", + "modified_by": "Administrator", + "module": "Desk", + "name": "Custom HTML Block", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Workspace Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/custom_html_block/custom_html_block.py b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.py new file mode 100644 index 0000000..ce12ac9 --- /dev/null +++ b/xhiveframework/desk/doctype/custom_html_block/custom_html_block.py @@ -0,0 +1,40 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.query_builder.utils import DocType + + +class CustomHTMLBlock(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.types import DF + + html: DF.Code | None + private: DF.Check + roles: DF.Table[HasRole] + script: DF.Code | None + style: DF.Code | None + # end: auto-generated types + pass + + +@xhiveframework.whitelist() +def get_custom_blocks_for_user(doctype, txt, searchfield, start, page_len, filters): + # return logged in users private blocks and all public blocks + customHTMLBlock = DocType("Custom HTML Block") + + condition_query = xhiveframework.qb.from_(customHTMLBlock) + + return ( + condition_query.select(customHTMLBlock.name).where( + (customHTMLBlock.private == 0) + | ((customHTMLBlock.owner == xhiveframework.session.user) & (customHTMLBlock.private == 1)) + ) + ).run() diff --git a/xhiveframework/desk/doctype/custom_html_block/test_custom_html_block.py b/xhiveframework/desk/doctype/custom_html_block/test_custom_html_block.py new file mode 100644 index 0000000..599c064 --- /dev/null +++ b/xhiveframework/desk/doctype/custom_html_block/test_custom_html_block.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestCustomHTMLBlock(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/dashboard/__init__.py b/xhiveframework/desk/doctype/dashboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard/dashboard.js b/xhiveframework/desk/doctype/dashboard/dashboard.js new file mode 100644 index 0000000..69dace4 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard/dashboard.js @@ -0,0 +1,30 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Dashboard", { + refresh: function (frm) { + frm.add_custom_button(__("Show Dashboard"), () => + xhiveframework.set_route("dashboard-view", frm.doc.name) + ); + + if (!xhiveframework.boot.developer_mode && frm.doc.is_standard) { + frm.disable_form(); + } + + frm.set_query("chart", "charts", function () { + return { + filters: { + is_public: 1, + }, + }; + }); + + frm.set_query("card", "cards", function () { + return { + filters: { + is_public: 1, + }, + }; + }); + }, +}); diff --git a/xhiveframework/desk/doctype/dashboard/dashboard.json b/xhiveframework/desk/doctype/dashboard/dashboard.json new file mode 100644 index 0000000..342f2a4 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard/dashboard.json @@ -0,0 +1,116 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:dashboard_name", + "creation": "2019-01-10 12:54:40.938705", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "dashboard_name", + "is_default", + "is_standard", + "module", + "charts", + "chart_options", + "cards" + ], + "fields": [ + { + "fieldname": "dashboard_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Dashboard Name", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "label": "Is Default" + }, + { + "fieldname": "charts", + "fieldtype": "Table", + "label": "Charts", + "options": "Dashboard Chart Link", + "reqd": 1 + }, + { + "description": "Set Default Options for all charts on this Dashboard (Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"])", + "fieldname": "chart_options", + "fieldtype": "Code", + "label": "Chart Options", + "options": "JSON" + }, + { + "fieldname": "cards", + "fieldtype": "Table", + "label": "Cards", + "options": "Number Card Link" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only_depends_on": "eval: !xhiveframework.boot.developer_mode" + }, + { + "depends_on": "eval: doc.is_standard", + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "mandatory_depends_on": "eval: doc.is_standard", + "options": "Module Def" + } + ], + "links": [], + "modified": "2023-08-28 22:35:02.993039", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Dashboard Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "dashboard_name", + "track_changes": 1 +} diff --git a/xhiveframework/desk/doctype/dashboard/dashboard.py b/xhiveframework/desk/doctype/dashboard/dashboard.py new file mode 100644 index 0000000..da19411 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard/dashboard.py @@ -0,0 +1,137 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.config import get_modules_from_all_apps_for_user +from xhiveframework.model.document import Document +from xhiveframework.modules.export_file import export_to_files +from xhiveframework.query_builder import DocType + + +class Dashboard(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.dashboard_chart_link.dashboard_chart_link import DashboardChartLink + from xhiveframework.desk.doctype.number_card_link.number_card_link import NumberCardLink + from xhiveframework.types import DF + + cards: DF.Table[NumberCardLink] + chart_options: DF.Code | None + charts: DF.Table[DashboardChartLink] + dashboard_name: DF.Data + is_default: DF.Check + is_standard: DF.Check + module: DF.Link | None + + # end: auto-generated types + def on_update(self): + if self.is_default: + # make all other dashboards non-default + DashBoard = DocType("Dashboard") + + xhiveframework.qb.update(DashBoard).set(DashBoard.is_default, 0).where(DashBoard.name != self.name).run() + + if xhiveframework.conf.developer_mode and self.is_standard: + export_to_files( + record_list=[["Dashboard", self.name, f"{self.module} Dashboard"]], record_module=self.module + ) + + def validate(self): + if not xhiveframework.conf.developer_mode and self.is_standard: + xhiveframework.throw(_("Cannot edit Standard Dashboards")) + + if self.is_standard: + non_standard_docs_map = { + "Dashboard Chart": get_non_standard_charts_in_dashboard(self), + "Number Card": get_non_standard_cards_in_dashboard(self), + } + + if non_standard_docs_map["Dashboard Chart"] or non_standard_docs_map["Number Card"]: + message = get_non_standard_warning_message(non_standard_docs_map) + xhiveframework.throw(message, title=_("Standard Not Set"), is_minimizable=True) + + self.validate_custom_options() + + def validate_custom_options(self): + if self.chart_options: + try: + json.loads(self.chart_options) + except ValueError as error: + xhiveframework.throw(_("Invalid json added in the custom options: {0}").format(error)) + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + return + + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return None + + allowed_modules = [ + xhiveframework.db.escape(module.get("module_name")) for module in get_modules_from_all_apps_for_user() + ] + return "`tabDashboard`.`module` in ({allowed_modules}) or `tabDashboard`.`module` is NULL".format( + allowed_modules=",".join(allowed_modules) + ) + + +@xhiveframework.whitelist() +def get_permitted_charts(dashboard_name): + permitted_charts = [] + dashboard = xhiveframework.get_doc("Dashboard", dashboard_name) + for chart in dashboard.charts: + if xhiveframework.has_permission("Dashboard Chart", doc=chart.chart): + chart_dict = xhiveframework._dict() + chart_dict.update(chart.as_dict()) + + if dashboard.get("chart_options"): + chart_dict.custom_options = dashboard.get("chart_options") + permitted_charts.append(chart_dict) + + return permitted_charts + + +@xhiveframework.whitelist() +def get_permitted_cards(dashboard_name): + dashboard = xhiveframework.get_doc("Dashboard", dashboard_name) + return [card for card in dashboard.cards if xhiveframework.has_permission("Number Card", doc=card.card)] + + +def get_non_standard_charts_in_dashboard(dashboard): + non_standard_charts = [doc.name for doc in xhiveframework.get_list("Dashboard Chart", {"is_standard": 0})] + return [chart_link.chart for chart_link in dashboard.charts if chart_link.chart in non_standard_charts] + + +def get_non_standard_cards_in_dashboard(dashboard): + non_standard_cards = [doc.name for doc in xhiveframework.get_list("Number Card", {"is_standard": 0})] + return [card_link.card for card_link in dashboard.cards if card_link.card in non_standard_cards] + + +def get_non_standard_warning_message(non_standard_docs_map): + message = _("""Please set the following documents in this Dashboard as standard first.""") + + def get_html(docs, doctype): + html = f"

    {xhiveframework.bold(doctype)}

    " + for doc in docs: + html += f'' + html += "
    " + return html + + html = message + "
    " + + for doctype in non_standard_docs_map: + if non_standard_docs_map[doctype]: + html += get_html(non_standard_docs_map[doctype], doctype) + + return html diff --git a/xhiveframework/desk/doctype/dashboard/dashboard_list.js b/xhiveframework/desk/doctype/dashboard/dashboard_list.js new file mode 100644 index 0000000..614414a --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard/dashboard_list.js @@ -0,0 +1,16 @@ +xhiveframework.listview_settings["Dashboard"] = { + button: { + show(doc) { + return doc.name; + }, + get_label() { + return xhiveframework.utils.icon("dashboard-list", "sm"); + }, + get_description(doc) { + return __("View {0}", [`${doc.name}`]); + }, + action(doc) { + xhiveframework.set_route("dashboard-view", doc.name); + }, + }, +}; diff --git a/xhiveframework/desk/doctype/dashboard/test_dashboard.py b/xhiveframework/desk/doctype/dashboard/test_dashboard.py new file mode 100644 index 0000000..85fb807 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard/test_dashboard.py @@ -0,0 +1,7 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDashboard(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/dashboard_chart/__init__.py b/xhiveframework/desk/doctype/dashboard_chart/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.js b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.js new file mode 100644 index 0000000..4b7056c --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.js @@ -0,0 +1,557 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.provide("xhiveframework.dashboards.chart_sources"); + +xhiveframework.ui.form.on("Dashboard Chart", { + setup: function (frm) { + // fetch timeseries from source + frm.add_fetch("source", "timeseries", "timeseries"); + }, + + before_save: function (frm) { + let dynamic_filters = JSON.parse(frm.doc.dynamic_filters_json || "null"); + let static_filters = JSON.parse(frm.doc.filters_json || "null"); + static_filters = xhiveframework.dashboard_utils.remove_common_static_filter_values( + static_filters, + dynamic_filters + ); + + frm.set_value("filters_json", JSON.stringify(static_filters)); + frm.trigger("show_filters"); + }, + + refresh: function (frm) { + frm.chart_filters = null; + frm.is_disabled = !xhiveframework.boot.developer_mode && frm.doc.is_standard; + + if (frm.is_disabled) { + !frm.doc.custom_options && frm.set_df_property("chart_options_section", "hidden", 1); + frm.disable_form(); + } + + if (!frm.is_new()) { + frm.add_custom_button("Add Chart to Dashboard", () => { + const dialog = xhiveframework.dashboard_utils.get_add_to_dashboard_dialog( + frm.doc.name, + "Dashboard Chart", + "xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.add_chart_to_dashboard" + ); + + if (!frm.doc.chart_name) { + xhiveframework.msgprint(__("Please create chart first")); + } else { + dialog.show(); + } + }); + } + + frm.set_df_property("filters_section", "hidden", 1); + frm.set_df_property("dynamic_filters_section", "hidden", 1); + + frm.trigger("set_parent_document_type"); + frm.trigger("set_time_series"); + frm.set_query("document_type", function () { + return { + filters: { + issingle: false, + }, + }; + }); + frm.trigger("update_options"); + frm.trigger("set_heatmap_year_options"); + if (frm.doc.report_name) { + frm.trigger("set_chart_report_filters"); + } + }, + + is_standard: function (frm) { + if (xhiveframework.boot.developer_mode && frm.doc.is_standard) { + frm.trigger("render_dynamic_filters_table"); + } else { + frm.set_df_property("dynamic_filters_section", "hidden", 1); + } + }, + + source: function (frm) { + frm.trigger("show_filters"); + }, + + set_heatmap_year_options: function (frm) { + if (frm.doc.type == "Heatmap") { + xhiveframework.db.get_doc("System Settings").then((doc) => { + const creation_date = doc.creation; + frm.set_df_property( + "heatmap_year", + "options", + xhiveframework.dashboard_utils.get_years_since_creation(creation_date) + ); + }); + } + }, + + chart_type: function (frm) { + frm.trigger("set_time_series"); + if (frm.doc.chart_type == "Report") { + frm.set_query("report_name", () => { + return { + filters: { + report_type: ["!=", "Report Builder"], + }, + }; + }); + } else { + frm.set_value("document_type", ""); + } + }, + + set_time_series: function (frm) { + // set timeseries based on chart type + if (["Count", "Average", "Sum"].includes(frm.doc.chart_type)) { + frm.set_value("timeseries", 1); + } else if (frm.doc.chart_type == "Custom") { + return; + } else { + frm.set_value("timeseries", 0); + } + }, + + document_type: function (frm) { + // update `based_on` options based on date / datetime fields + frm.set_value("source", ""); + frm.set_value("based_on", ""); + frm.set_value("value_based_on", ""); + frm.set_value("parent_document_type", ""); + frm.set_value("filters_json", "[]"); + frm.set_value("dynamic_filters_json", "[]"); + frm.trigger("update_options"); + frm.trigger("set_parent_document_type"); + }, + + report_name: function (frm) { + frm.set_value("x_field", ""); + frm.set_value("y_axis", []); + frm.set_df_property("x_field", "options", []); + frm.set_value("filters_json", "{}"); + frm.set_value("dynamic_filters_json", "{}"); + frm.set_value("use_report_chart", 0); + frm.trigger("set_chart_report_filters"); + }, + + set_chart_report_filters: function (frm) { + let report_name = frm.doc.report_name; + + if (report_name) { + if (frm.doc.filters_json.length > 2) { + frm.trigger("show_filters"); + frm.trigger("set_chart_field_options"); + } else { + xhiveframework.report_utils.get_report_filters(report_name).then((filters) => { + if (filters) { + frm.chart_filters = filters; + let filter_values = xhiveframework.report_utils.get_filter_values(filters); + frm.set_value("filters_json", JSON.stringify(filter_values)); + } + frm.trigger("show_filters"); + frm.trigger("set_chart_field_options"); + }); + } + } + }, + + use_report_chart: function (frm) { + !frm.doc.use_report_chart && frm.trigger("set_chart_field_options"); + }, + + set_chart_field_options: function (frm) { + let filters = frm.doc.filters_json.length > 2 ? JSON.parse(frm.doc.filters_json) : null; + if (frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2) { + filters = xhiveframework.dashboard_utils.get_all_filters(frm.doc); + } + xhiveframework + .xcall("xhiveframework.desk.query_report.run", { + report_name: frm.doc.report_name, + filters: filters, + ignore_prepared_report: 1, + }) + .then((data) => { + frm.report_data = data; + let report_has_chart = Boolean(data.chart); + + frm.set_df_property("use_report_chart", "hidden", !report_has_chart); + + if (!frm.doc.use_report_chart) { + if (data.result.length) { + frm.field_options = xhiveframework.report_utils.get_field_options_from_report( + data.columns, + data + ); + frm.set_df_property( + "x_field", + "options", + frm.field_options.non_numeric_fields + ); + if (!frm.field_options.numeric_fields.length) { + xhiveframework.msgprint( + __("Report has no numeric fields, please change the Report Name") + ); + } else { + let y_field_df = xhiveframework.meta.get_docfield( + "Dashboard Chart Field", + "y_field", + frm.doc.name + ); + y_field_df.options = frm.field_options.numeric_fields; + } + } else { + xhiveframework.msgprint( + __( + "Report has no data, please modify the filters or change the Report Name" + ) + ); + } + } else { + frm.set_value("use_report_chart", 1); + frm.set_df_property("use_report_chart", "hidden", false); + } + }); + }, + + timespan: function (frm) { + const time_interval_options = { + "Select Date Range": ["Quarterly", "Monthly", "Weekly", "Daily"], + "All Time": ["Yearly", "Monthly"], + "Last Year": ["Quarterly", "Monthly", "Weekly", "Daily"], + "Last Quarter": ["Monthly", "Weekly", "Daily"], + "Last Month": ["Weekly", "Daily"], + "Last Week": ["Daily"], + }; + if (frm.doc.timespan) { + frm.set_df_property( + "time_interval", + "options", + time_interval_options[frm.doc.timespan] + ); + } + }, + + update_options: function (frm) { + let doctype = frm.doc.document_type; + let date_fields = [ + { label: __("Created On"), value: "creation" }, + { label: __("Last Modified On"), value: "modified" }, + ]; + let value_fields = []; + let group_by_fields = [{ label: "Created By", value: "owner" }]; + let aggregate_function_fields = []; + let update_form = function () { + // update select options + frm.set_df_property("based_on", "options", date_fields); + frm.set_df_property("value_based_on", "options", value_fields); + frm.set_df_property("group_by_based_on", "options", group_by_fields); + frm.set_df_property( + "aggregate_function_based_on", + "options", + aggregate_function_fields + ); + frm.trigger("show_filters"); + }; + + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => { + // get all date and datetime fields + xhiveframework.get_meta(doctype).fields.map((df) => { + if (["Date", "Datetime"].includes(df.fieldtype)) { + date_fields.push({ label: df.label, value: df.fieldname }); + } + if ( + ["Int", "Float", "Currency", "Percent", "Duration"].includes(df.fieldtype) + ) { + value_fields.push({ label: df.label, value: df.fieldname }); + aggregate_function_fields.push({ label: df.label, value: df.fieldname }); + } + if (["Link", "Select"].includes(df.fieldtype)) { + group_by_fields.push({ label: df.label, value: df.fieldname }); + } + }); + update_form(); + }); + } else { + // update select options + update_form(); + } + }, + + show_filters: function (frm) { + frm.chart_filters = []; + xhiveframework.dashboard_utils.get_filters_for_chart_type(frm.doc).then((filters) => { + if (filters) { + frm.chart_filters = filters; + } + frm.trigger("render_filters_table"); + + if (xhiveframework.boot.developer_mode && frm.doc.is_standard) { + frm.trigger("render_dynamic_filters_table"); + } + }); + }, + + render_filters_table: function (frm) { + frm.set_df_property("filters_section", "hidden", 0); + let is_document_type = frm.doc.chart_type !== "Report" && frm.doc.chart_type !== "Custom"; + let is_dynamic_filter = (f) => ["Date", "DateRange"].includes(f.fieldtype) && f.default; + + let wrapper = $(frm.get_field("filters_json").wrapper).empty(); + let table = $(` + + + + + + + + +
    ${__("Filter")}${__("Condition")}${__("Value")}
    `).appendTo(wrapper); + $(`

    ${__("Click table to edit")}

    `).appendTo(wrapper); + + let filters = JSON.parse(frm.doc.filters_json || "[]"); + var filters_set = false; + + // Set dynamic filters for reports + if (frm.doc.chart_type == "Report") { + let set_filters = false; + frm.chart_filters.forEach((f) => { + if (is_dynamic_filter(f)) { + filters[f.fieldname] = f.default; + set_filters = true; + } + }); + set_filters && frm.set_value("filters_json", JSON.stringify(filters)); + } + + let fields = []; + if (is_document_type) { + fields = [ + { + fieldtype: "HTML", + fieldname: "filter_area", + }, + ]; + + if (filters.length > 0) { + filters.forEach((filter) => { + const filter_row = $(` + ${filter[1]} + ${filter[2] || ""} + ${filter[3]} + `); + + table.find("tbody").append(filter_row); + filters_set = true; + }); + } + } else if (frm.chart_filters.length) { + fields = frm.chart_filters.filter((f) => f.fieldname); + + fields.map((f) => { + if (filters[f.fieldname]) { + let condition = "="; + const filter_row = $(` + ${f.label} + ${condition} + ${filters[f.fieldname] || ""} + `); + + table.find("tbody").append(filter_row); + filters_set = true; + } + }); + } + + if (!filters_set) { + const filter_row = $(` + ${__("Click to Set Filters")}`); + table.find("tbody").append(filter_row); + } + + table.on("click", () => { + frm.is_disabled && xhiveframework.throw(__("Cannot edit filters for standard charts")); + + let dialog = new xhiveframework.ui.Dialog({ + title: __("Set Filters"), + fields: fields.filter((f) => !is_dynamic_filter(f)), + primary_action: function () { + let values = this.get_values(); + if (values) { + this.hide(); + if (is_document_type) { + let filters = frm.filter_group.get_filters(); + frm.set_value("filters_json", JSON.stringify(filters)); + } else { + frm.set_value("filters_json", JSON.stringify(values)); + } + + frm.trigger("show_filters"); + if (frm.doc.chart_type == "Report") { + frm.trigger("set_chart_report_filters"); + } + } + }, + primary_action_label: "Set", + }); + xhiveframework.dashboards.filters_dialog = dialog; + + if (is_document_type) { + frm.filter_group = new xhiveframework.ui.FilterGroup({ + parent: dialog.get_field("filter_area").$wrapper, + doctype: frm.doc.document_type, + parent_doctype: frm.doc.parent_document_type, + on_change: () => {}, + }); + + frm.filter_group.add_filters_to_filter_group(filters); + } + + dialog.show(); + + if (frm.doc.chart_type == "Report") { + //Set query report object so that it can be used while fetching filter values in the report + xhiveframework.query_report = new xhiveframework.views.QueryReport({ + filters: dialog.fields_list, + }); + xhiveframework.query_reports[frm.doc.report_name] && + xhiveframework.query_reports[frm.doc.report_name].onload && + xhiveframework.query_reports[frm.doc.report_name].onload(xhiveframework.query_report); + } + + dialog.set_values(filters); + }); + }, + + render_dynamic_filters_table(frm) { + frm.set_df_property("dynamic_filters_section", "hidden", 0); + + let is_document_type = frm.doc.chart_type !== "Report" && frm.doc.chart_type !== "Custom"; + + let wrapper = $(frm.get_field("dynamic_filters_json").wrapper).empty(); + + frm.dynamic_filter_table = + $(` + + + + + + + + +
    ${__("Filter")}${__("Condition")}${__("Value")}
    `).appendTo(wrapper); + + frm.dynamic_filters = + frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2 + ? JSON.parse(frm.doc.dynamic_filters_json) + : null; + + frm.trigger("set_dynamic_filters_in_table"); + + let filters = JSON.parse(frm.doc.filters_json || "[]"); + + let fields = xhiveframework.dashboard_utils.get_fields_for_dynamic_filter_dialog( + is_document_type, + filters, + frm.dynamic_filters + ); + + frm.dynamic_filter_table.on("click", () => { + let dialog = new xhiveframework.ui.Dialog({ + title: __("Set Dynamic Filters"), + fields: fields, + primary_action: () => { + let values = dialog.get_values(); + dialog.hide(); + let dynamic_filters = []; + for (let key of Object.keys(values)) { + if (is_document_type) { + let [doctype, fieldname] = key.split(":"); + dynamic_filters.push([doctype, fieldname, "=", values[key]]); + } + } + + if (is_document_type) { + frm.set_value("dynamic_filters_json", JSON.stringify(dynamic_filters)); + } else { + frm.set_value("dynamic_filters_json", JSON.stringify(values)); + } + frm.trigger("set_dynamic_filters_in_table"); + }, + primary_action_label: "Set", + }); + + dialog.show(); + dialog.set_values(frm.dynamic_filters); + }); + }, + + set_dynamic_filters_in_table: function (frm) { + frm.dynamic_filters = + frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2 + ? JSON.parse(frm.doc.dynamic_filters_json) + : null; + + if (!frm.dynamic_filters) { + const filter_row = $(` + ${__("Click to Set Dynamic Filters")}`); + frm.dynamic_filter_table.find("tbody").html(filter_row); + } else { + let filter_rows = ""; + if ($.isArray(frm.dynamic_filters)) { + frm.dynamic_filters.forEach((filter) => { + filter_rows += ` + ${filter[1]} + ${filter[2] || ""} + ${filter[3]} + `; + }); + } else { + let condition = "="; + for (let [key, val] of Object.entries(frm.dynamic_filters)) { + filter_rows += ` + ${key} + ${condition} + ${val || ""} + `; + } + } + + frm.dynamic_filter_table.find("tbody").html(filter_rows); + } + }, + + set_parent_document_type: async function (frm) { + let document_type = frm.doc.document_type; + let doc_is_table = + document_type && + (await xhiveframework.db.get_value("DocType", document_type, "istable")).message.istable; + + frm.set_df_property("parent_document_type", "hidden", !doc_is_table); + + if (document_type && doc_is_table) { + let parents = await xhiveframework.xcall( + "xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.get_parent_doctypes", + { child_type: document_type } + ); + + frm.set_query("parent_document_type", function () { + return { + filters: { + name: ["in", parents], + }, + }; + }); + + if (parents.length === 1) { + frm.set_value("parent_document_type", parents[0]); + } + } + }, +}); diff --git a/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.json b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.json new file mode 100644 index 0000000..fcbe0f4 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.json @@ -0,0 +1,336 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:chart_name", + "creation": "2019-01-10 12:28:06.282875", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_standard", + "module", + "chart_name", + "chart_type", + "report_name", + "use_report_chart", + "x_field", + "y_axis", + "source", + "document_type", + "parent_document_type", + "based_on", + "value_based_on", + "group_by_type", + "group_by_based_on", + "aggregate_function_based_on", + "number_of_groups", + "column_break_6", + "is_public", + "heatmap_year", + "timespan", + "from_date", + "to_date", + "time_interval", + "timeseries", + "type", + "filters_section", + "filters_json", + "dynamic_filters_section", + "dynamic_filters_json", + "chart_options_section", + "custom_options", + "column_break_2", + "color", + "section_break_10", + "last_synced_on", + "roles" + ], + "fields": [ + { + "fieldname": "chart_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Chart Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "chart_type", + "fieldtype": "Select", + "label": "Chart Type", + "options": "Count\nSum\nAverage\nGroup By\nCustom\nReport", + "set_only_once": 1 + }, + { + "depends_on": "eval:doc.chart_type === 'Custom'", + "fieldname": "source", + "fieldtype": "Link", + "label": "Chart Source", + "options": "Dashboard Chart Source" + }, + { + "depends_on": "eval: doc.chart_type !== 'Custom' && doc.chart_type !== 'Report'", + "fieldname": "document_type", + "fieldtype": "Link", + "label": "Document Type", + "options": "DocType", + "set_only_once": 1 + }, + { + "depends_on": "eval: doc.timeseries && ['Count', 'Sum', 'Average'].includes(doc.chart_type)", + "fieldname": "based_on", + "fieldtype": "Select", + "label": "Time Series Based On" + }, + { + "depends_on": "eval: ['Sum', 'Average'].includes(doc.chart_type)\n", + "fieldname": "value_based_on", + "fieldtype": "Select", + "label": "Value Based On" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.timeseries && doc.type !== 'Heatmap'", + "fieldname": "timespan", + "fieldtype": "Select", + "label": "Timespan", + "options": "Last Year\nLast Quarter\nLast Month\nLast Week\nSelect Date Range" + }, + { + "depends_on": "eval: doc.timeseries && doc.type !== 'Heatmap'", + "fieldname": "time_interval", + "fieldtype": "Select", + "label": "Time Interval", + "options": "Yearly\nQuarterly\nMonthly\nWeekly\nDaily" + }, + { + "default": "0", + "depends_on": "eval: !['Group By', 'Report'].includes(doc.chart_type)\n", + "fieldname": "timeseries", + "fieldtype": "Check", + "label": "Time Series" + }, + { + "fieldname": "filters_section", + "fieldtype": "Section Break", + "label": "Filters" + }, + { + "fieldname": "filters_json", + "fieldtype": "Code", + "label": "Filters JSON", + "options": "JSON", + "reqd": 1 + }, + { + "fieldname": "chart_options_section", + "fieldtype": "Section Break", + "label": "Chart Options" + }, + { + "default": "Line", + "fieldname": "type", + "fieldtype": "Select", + "label": "Type", + "options": "Line\nBar\nPercentage\nPie\nDonut\nHeatmap" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.chart_type !== 'Report' && doc.type !== 'Heatmap'", + "fieldname": "color", + "fieldtype": "Color", + "label": "Color" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "last_synced_on", + "fieldtype": "Datetime", + "label": "Last Synced On", + "read_only": 1 + }, + { + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "group_by_based_on", + "fieldtype": "Select", + "label": "Group By Based On" + }, + { + "default": "Count", + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "group_by_type", + "fieldtype": "Select", + "label": "Group By Type", + "options": "Count\nSum\nAverage" + }, + { + "depends_on": "eval: ['Sum', 'Average'].includes(doc.group_by_type)", + "fieldname": "aggregate_function_based_on", + "fieldtype": "Select", + "label": "Aggregate Function Based On" + }, + { + "depends_on": "eval:doc.chart_type === 'Group By'", + "fieldname": "number_of_groups", + "fieldtype": "Int", + "label": "Number of Groups" + }, + { + "depends_on": "eval:doc.timespan === 'Select Date Range'", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, + { + "depends_on": "eval:doc.timespan === 'Select Date Range'", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, + { + "depends_on": "eval:doc.chart_type == 'Report' && doc.report_name && !doc.use_report_chart", + "fieldname": "x_field", + "fieldtype": "Select", + "label": "X Field", + "mandatory_depends_on": "eval: doc.report_name && !doc.use_report_chart" + }, + { + "depends_on": "eval:doc.chart_type === 'Report'", + "fieldname": "report_name", + "fieldtype": "Link", + "label": "Report Name", + "mandatory_depends_on": "eval:doc.chart_type === 'Report'", + "options": "Report", + "set_only_once": 1 + }, + { + "depends_on": "eval:doc.chart_type == 'Report' && doc.report_name && !doc.use_report_chart", + "fieldname": "y_axis", + "fieldtype": "Table", + "label": "Y Axis", + "mandatory_depends_on": "eval:doc.report_name && !doc.use_report_chart", + "options": "Dashboard Chart Field" + }, + { + "description": "Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"]", + "fieldname": "custom_options", + "fieldtype": "Code", + "label": "Custom Options" + }, + { + "default": "0", + "description": "This chart will be available to all Users if this is set", + "fieldname": "is_public", + "fieldtype": "Check", + "label": "Is Public" + }, + { + "depends_on": "eval: doc.type == 'Heatmap'", + "fieldname": "heatmap_year", + "fieldtype": "Select", + "label": "Year" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only_depends_on": "eval: !xhiveframework.boot.developer_mode" + }, + { + "depends_on": "eval: doc.is_standard", + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "mandatory_depends_on": "eval: doc.is_standard", + "options": "Module Def" + }, + { + "fieldname": "dynamic_filters_json", + "fieldtype": "Code", + "label": "Dynamic Filters JSON", + "options": "JSON" + }, + { + "fieldname": "dynamic_filters_section", + "fieldtype": "Section Break", + "label": "Dynamic Filters" + }, + { + "default": "0", + "depends_on": "eval: doc.report_name", + "fieldname": "use_report_chart", + "fieldtype": "Check", + "label": "Use Report Chart" + }, + { + "depends_on": "eval: doc.chart_type !== 'Custom' && doc.chart_type !== 'Report'", + "description": "The document type selected is a child table, so the parent document type is required.", + "fieldname": "parent_document_type", + "fieldtype": "Link", + "label": "Parent Document Type", + "options": "DocType" + }, + { + "description": "If set, only user with these roles can access this chart. If not set, DocType or Report permissions will be used.", + "fieldname": "roles", + "fieldtype": "Table", + "label": "Roles", + "options": "Has Role" + } + ], + "links": [], + "modified": "2023-09-18 13:41:05.263676", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard Chart", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Dashboard Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.py b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.py new file mode 100644 index 0000000..f6451fc --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart/dashboard_chart.py @@ -0,0 +1,436 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import datetime +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.boot import get_allowed_report_names +from xhiveframework.config import get_modules_from_all_apps_for_user +from xhiveframework.model.document import Document +from xhiveframework.model.naming import append_number_if_name_exists +from xhiveframework.modules.export_file import export_to_files +from xhiveframework.utils import cint, get_datetime, getdate, has_common, now_datetime, nowdate +from xhiveframework.utils.dashboard import cache_source +from xhiveframework.utils.data import format_date +from xhiveframework.utils.dateutils import ( + get_dates_from_timegrain, + get_from_date_from_timespan, + get_period, + get_period_beginning, +) + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + return + + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return None + + doctype_condition = False + report_condition = False + module_condition = False + + allowed_doctypes = [xhiveframework.db.escape(doctype) for doctype in xhiveframework.permissions.get_doctypes_with_read()] + allowed_reports = [xhiveframework.db.escape(report) for report in get_allowed_report_names()] + allowed_modules = [ + xhiveframework.db.escape(module.get("module_name")) for module in get_modules_from_all_apps_for_user() + ] + + if allowed_doctypes: + doctype_condition = "`tabDashboard Chart`.`document_type` in ({allowed_doctypes})".format( + allowed_doctypes=",".join(allowed_doctypes) + ) + if allowed_reports: + report_condition = "`tabDashboard Chart`.`report_name` in ({allowed_reports})".format( + allowed_reports=",".join(allowed_reports) + ) + if allowed_modules: + module_condition = """`tabDashboard Chart`.`module` in ({allowed_modules}) + or `tabDashboard Chart`.`module` is NULL""".format(allowed_modules=",".join(allowed_modules)) + + return f""" + ((`tabDashboard Chart`.`chart_type` in ('Count', 'Sum', 'Average') + and {doctype_condition}) + or + (`tabDashboard Chart`.`chart_type` = 'Report' + and {report_condition})) + and + ({module_condition}) + """ + + +def has_permission(doc, ptype, user): + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return True + + if doc.roles: + allowed = [d.role for d in doc.roles] + if has_common(roles, allowed): + return True + elif doc.chart_type == "Report": + if doc.report_name in get_allowed_report_names(): + return True + else: + allowed_doctypes = xhiveframework.permissions.get_doctypes_with_read() + if doc.document_type in allowed_doctypes: + return True + + return False + + +@xhiveframework.whitelist() +@cache_source +def get( + chart_name=None, + chart=None, + no_cache=None, + filters=None, + from_date=None, + to_date=None, + timespan=None, + time_interval=None, + heatmap_year=None, + refresh=None, +): + if chart_name: + chart: DashboardChart = xhiveframework.get_doc("Dashboard Chart", chart_name) + else: + chart = xhiveframework._dict(xhiveframework.parse_json(chart)) + + heatmap_year = heatmap_year or chart.heatmap_year + timespan = timespan or chart.timespan + + if timespan == "Select Date Range": + if from_date and len(from_date): + from_date = get_datetime(from_date) + else: + from_date = chart.from_date + + if to_date and len(to_date): + to_date = get_datetime(to_date) + else: + to_date = get_datetime(chart.to_date) + + timegrain = time_interval or chart.time_interval + filters = xhiveframework.parse_json(filters) or xhiveframework.parse_json(chart.filters_json) + if not filters: + filters = [] + + # don't include cancelled documents + filters.append([chart.document_type, "docstatus", "<", 2, False]) + + if chart.chart_type == "Group By": + chart_config = get_group_by_chart_config(chart, filters) + else: + if chart.type == "Heatmap": + chart_config = get_heatmap_chart_config(chart, filters, heatmap_year) + else: + chart_config = get_chart_config(chart, filters, timespan, timegrain, from_date, to_date) + + return chart_config + + +@xhiveframework.whitelist() +def create_dashboard_chart(args): + args = xhiveframework.parse_json(args) + doc = xhiveframework.new_doc("Dashboard Chart") + + doc.update(args) + + if args.get("custom_options"): + doc.custom_options = json.dumps(args.get("custom_options")) + + if xhiveframework.db.exists("Dashboard Chart", args.chart_name): + args.chart_name = append_number_if_name_exists("Dashboard Chart", args.chart_name) + doc.chart_name = args.chart_name + doc.insert(ignore_permissions=True) + return doc + + +@xhiveframework.whitelist() +def create_report_chart(args): + doc = create_dashboard_chart(args) + args = xhiveframework.parse_json(args) + args.chart_name = doc.chart_name + if args.dashboard: + add_chart_to_dashboard(json.dumps(args)) + + +@xhiveframework.whitelist() +def add_chart_to_dashboard(args): + args = xhiveframework.parse_json(args) + + dashboard = xhiveframework.get_doc("Dashboard", args.dashboard) + dashboard_link = xhiveframework.new_doc("Dashboard Chart Link") + dashboard_link.chart = args.chart_name or args.name + + if args.set_standard and dashboard.is_standard: + chart = xhiveframework.get_doc("Dashboard Chart", dashboard_link.chart) + chart.is_standard = 1 + chart.module = dashboard.module + chart.save() + + dashboard.append("charts", dashboard_link) + dashboard.save() + xhiveframework.db.commit() + + +def get_chart_config(chart, filters, timespan, timegrain, from_date, to_date): + if not from_date: + from_date = get_from_date_from_timespan(to_date, timespan) + from_date = get_period_beginning(from_date, timegrain) + if not to_date: + to_date = now_datetime() + + doctype = chart.document_type + datefield = chart.based_on + value_field = chart.value_based_on or "1" + from_date = from_date.strftime("%Y-%m-%d") + to_date = to_date + + filters.append([doctype, datefield, ">=", from_date, False]) + filters.append([doctype, datefield, "<=", to_date, False]) + + data = xhiveframework.get_list( + doctype, + fields=[datefield, f"SUM({value_field})", "COUNT(*)"], + filters=filters, + group_by=datefield, + order_by=datefield, + as_list=True, + parent_doctype=chart.parent_document_type, + ) + + result = get_result(data, timegrain, from_date, to_date, chart.chart_type) + + return { + "labels": [ + format_date(get_period(r[0], timegrain), parse_day_first=True) + if timegrain in ("Daily", "Weekly") + else get_period(r[0], timegrain) + for r in result + ], + "datasets": [{"name": chart.name, "values": [r[1] for r in result]}], + } + + +def get_heatmap_chart_config(chart, filters, heatmap_year): + aggregate_function = get_aggregate_function(chart.chart_type) + value_field = chart.value_based_on or "1" + doctype = chart.document_type + datefield = chart.based_on + year = cint(heatmap_year) if heatmap_year else getdate(nowdate()).year + year_start_date = datetime.date(year, 1, 1).strftime("%Y-%m-%d") + next_year_start_date = datetime.date(year + 1, 1, 1).strftime("%Y-%m-%d") + + filters.append([doctype, datefield, ">", f"{year_start_date}", False]) + filters.append([doctype, datefield, "<", f"{next_year_start_date}", False]) + + if xhiveframework.db.db_type == "mariadb": + timestamp_field = f"unix_timestamp({datefield})" + else: + timestamp_field = f"extract(epoch from timestamp {datefield})" + + data = dict( + xhiveframework.get_all( + doctype, + fields=[ + timestamp_field, + f"{aggregate_function}({value_field})", + ], + filters=filters, + group_by=f"date({datefield})", + as_list=1, + order_by=f"{datefield} asc", + ignore_ifnull=True, + ) + ) + + return { + "labels": [], + "dataPoints": data, + } + + +def get_group_by_chart_config(chart, filters): + aggregate_function = get_aggregate_function(chart.group_by_type) + value_field = chart.aggregate_function_based_on or "1" + group_by_field = chart.group_by_based_on + doctype = chart.document_type + + data = xhiveframework.get_list( + doctype, + fields=[ + f"{group_by_field} as name", + f"{aggregate_function}({value_field}) as count", + ], + filters=filters, + parent_doctype=chart.parent_document_type, + group_by=group_by_field, + order_by="count desc", + ignore_ifnull=True, + ) + + if data: + return { + "labels": [item["name"] if item["name"] else "Not Specified" for item in data], + "datasets": [{"name": chart.name, "values": [item["count"] for item in data]}], + } + else: + return None + + +def get_aggregate_function(chart_type): + return { + "Sum": "SUM", + "Count": "COUNT", + "Average": "AVG", + }[chart_type] + + +def get_result(data, timegrain, from_date, to_date, chart_type): + dates = get_dates_from_timegrain(from_date, to_date, timegrain) + result = [[date, 0] for date in dates] + data_index = 0 + if data: + for d in result: + count = 0 + while data_index < len(data) and getdate(data[data_index][0]) <= d[0]: + d[1] += data[data_index][1] + count += data[data_index][2] + data_index += 1 + if chart_type == "Average" and not count == 0: + d[1] = d[1] / count + if chart_type == "Count": + d[1] = count + + return result + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_charts_for_user(doctype, txt, searchfield, start, page_len, filters): + or_filters = {"owner": xhiveframework.session.user, "is_public": 1} + return xhiveframework.db.get_list( + "Dashboard Chart", fields=["name"], filters=filters, or_filters=or_filters, as_list=1 + ) + + +class DashboardChart(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.desk.doctype.dashboard_chart_field.dashboard_chart_field import DashboardChartField + from xhiveframework.types import DF + + aggregate_function_based_on: DF.Literal[None] + based_on: DF.Literal[None] + chart_name: DF.Data + chart_type: DF.Literal["Count", "Sum", "Average", "Group By", "Custom", "Report"] + color: DF.Color | None + custom_options: DF.Code | None + document_type: DF.Link | None + dynamic_filters_json: DF.Code | None + filters_json: DF.Code + from_date: DF.Date | None + group_by_based_on: DF.Literal[None] + group_by_type: DF.Literal["Count", "Sum", "Average"] + heatmap_year: DF.Literal[None] + is_public: DF.Check + is_standard: DF.Check + last_synced_on: DF.Datetime | None + module: DF.Link | None + number_of_groups: DF.Int + parent_document_type: DF.Link | None + report_name: DF.Link | None + roles: DF.Table[HasRole] + source: DF.Link | None + time_interval: DF.Literal["Yearly", "Quarterly", "Monthly", "Weekly", "Daily"] + timeseries: DF.Check + timespan: DF.Literal["Last Year", "Last Quarter", "Last Month", "Last Week", "Select Date Range"] + to_date: DF.Date | None + type: DF.Literal["Line", "Bar", "Percentage", "Pie", "Donut", "Heatmap"] + use_report_chart: DF.Check + value_based_on: DF.Literal[None] + x_field: DF.Literal[None] + y_axis: DF.Table[DashboardChartField] + + # end: auto-generated types + def on_update(self): + xhiveframework.cache.delete_key(f"chart-data:{self.name}") + if xhiveframework.conf.developer_mode and self.is_standard: + export_to_files(record_list=[["Dashboard Chart", self.name]], record_module=self.module) + + def validate(self): + if not xhiveframework.conf.developer_mode and self.is_standard: + xhiveframework.throw(_("Cannot edit Standard charts")) + if self.chart_type != "Custom" and self.chart_type != "Report": + self.check_required_field() + self.check_document_type() + + self.validate_custom_options() + + def check_required_field(self): + if not self.document_type: + xhiveframework.throw(_("Document type is required to create a dashboard chart")) + + if ( + self.document_type + and xhiveframework.get_meta(self.document_type).istable + and not self.parent_document_type + ): + xhiveframework.throw(_("Parent document type is required to create a dashboard chart")) + + if self.chart_type == "Group By": + if not self.group_by_based_on: + xhiveframework.throw(_("Group By field is required to create a dashboard chart")) + if self.group_by_type in ["Sum", "Average"] and not self.aggregate_function_based_on: + xhiveframework.throw(_("Aggregate Function field is required to create a dashboard chart")) + else: + if not self.based_on: + xhiveframework.throw(_("Time series based on is required to create a dashboard chart")) + + def check_document_type(self): + if xhiveframework.get_meta(self.document_type).issingle: + xhiveframework.throw(_("You cannot create a dashboard chart from single DocTypes")) + + def validate_custom_options(self): + if self.custom_options: + try: + json.loads(self.custom_options) + except ValueError as error: + xhiveframework.throw(_("Invalid json added in the custom options: {0}").format(error)) + + +@xhiveframework.whitelist() +def get_parent_doctypes(child_type: str) -> list[str]: + """Get all parent doctypes that have the child doctype.""" + assert isinstance(child_type, str) + + standard = xhiveframework.get_all( + "DocField", + fields="parent", + filters={"fieldtype": "Table", "options": child_type}, + pluck="parent", + ) + + custom = xhiveframework.get_all( + "Custom Field", + fields="dt", + filters={"fieldtype": "Table", "options": child_type}, + pluck="dt", + ) + + return standard + custom diff --git a/xhiveframework/desk/doctype/dashboard_chart/test_dashboard_chart.py b/xhiveframework/desk/doctype/dashboard_chart/test_dashboard_chart.py new file mode 100644 index 0000000..2f35bbf --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart/test_dashboard_chart.py @@ -0,0 +1,282 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +from datetime import datetime +from unittest.mock import patch + +from dateutil.relativedelta import relativedelta + +import xhiveframework +from xhiveframework.desk.doctype.dashboard_chart.dashboard_chart import get +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import formatdate, get_last_day, getdate +from xhiveframework.utils.dateutils import get_period, get_period_ending + + +class TestDashboardChart(XhiveFrameworkTestCase): + def test_period_ending(self): + self.assertEqual(get_period_ending("2019-04-10", "Daily"), getdate("2019-04-10")) + + # week starts on monday + with patch.object(xhiveframework.utils.data, "get_first_day_of_the_week", return_value="Monday"): + self.assertEqual(get_period_ending("2019-04-10", "Weekly"), getdate("2019-04-14")) + + self.assertEqual(get_period_ending("2019-04-10", "Monthly"), getdate("2019-04-30")) + self.assertEqual(get_period_ending("2019-04-30", "Monthly"), getdate("2019-04-30")) + self.assertEqual(get_period_ending("2019-03-31", "Monthly"), getdate("2019-03-31")) + + self.assertEqual(get_period_ending("2019-04-10", "Quarterly"), getdate("2019-06-30")) + self.assertEqual(get_period_ending("2019-06-30", "Quarterly"), getdate("2019-06-30")) + self.assertEqual(get_period_ending("2019-10-01", "Quarterly"), getdate("2019-12-31")) + + def test_dashboard_chart(self): + if xhiveframework.db.exists("Dashboard Chart", "Test Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Dashboard Chart") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Dashboard Chart", + chart_type="Count", + document_type="DocType", + based_on="creation", + timespan="Last Year", + time_interval="Monthly", + filters_json="{}", + timeseries=1, + ) + ).insert() + + cur_date = datetime.now() - relativedelta(years=1) + + result = get(chart_name="Test Dashboard Chart", refresh=1) + + for idx in range(13): + month = get_last_day(cur_date) + month = formatdate(month.strftime("%Y-%m-%d")) + self.assertEqual(result.get("labels")[idx], get_period(month)) + cur_date += relativedelta(months=1) + + def test_empty_dashboard_chart(self): + if xhiveframework.db.exists("Dashboard Chart", "Test Empty Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Empty Dashboard Chart") + + xhiveframework.db.delete("Error Log") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Empty Dashboard Chart", + chart_type="Count", + document_type="Error Log", + based_on="creation", + timespan="Last Year", + time_interval="Monthly", + filters_json="[]", + timeseries=1, + ) + ).insert() + + cur_date = datetime.now() - relativedelta(years=1) + + result = get(chart_name="Test Empty Dashboard Chart", refresh=1) + + for idx in range(13): + month = get_last_day(cur_date) + month = formatdate(month.strftime("%Y-%m-%d")) + self.assertEqual(result.get("labels")[idx], get_period(month)) + cur_date += relativedelta(months=1) + + def test_chart_wih_one_value(self): + if xhiveframework.db.exists("Dashboard Chart", "Test Empty Dashboard Chart 2"): + xhiveframework.delete_doc("Dashboard Chart", "Test Empty Dashboard Chart 2") + + xhiveframework.db.delete("Error Log") + + # create one data point + xhiveframework.get_doc(dict(doctype="Error Log", creation="2018-06-01 00:00:00")).insert() + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Empty Dashboard Chart 2", + chart_type="Count", + document_type="Error Log", + based_on="creation", + timespan="Last Year", + time_interval="Monthly", + filters_json="[]", + timeseries=1, + ) + ).insert() + + cur_date = datetime.now() - relativedelta(years=1) + + result = get(chart_name="Test Empty Dashboard Chart 2", refresh=1) + + for idx in range(13): + month = get_last_day(cur_date) + month = formatdate(month.strftime("%Y-%m-%d")) + self.assertEqual(result.get("labels")[idx], get_period(month)) + cur_date += relativedelta(months=1) + + # only 1 data point with value + self.assertEqual(result.get("datasets")[0].get("values")[2], 0) + + def test_group_by_chart_type(self): + if xhiveframework.db.exists("Dashboard Chart", "Test Group By Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Group By Dashboard Chart") + + xhiveframework.get_doc({"doctype": "ToDo", "description": "test"}).insert() + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Group By Dashboard Chart", + chart_type="Group By", + document_type="ToDo", + group_by_based_on="status", + filters_json="[]", + ) + ).insert() + + result = get(chart_name="Test Group By Dashboard Chart", refresh=1) + todo_status_count = xhiveframework.db.count("ToDo", {"status": result.get("labels")[0]}) + + self.assertEqual(result.get("datasets")[0].get("values")[0], todo_status_count) + + def test_daily_dashboard_chart(self): + insert_test_records() + + if xhiveframework.db.exists("Dashboard Chart", "Test Daily Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Daily Dashboard Chart") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Daily Dashboard Chart", + chart_type="Sum", + document_type="Communication", + based_on="communication_date", + value_based_on="rating", + timespan="Select Date Range", + time_interval="Daily", + from_date=datetime(2019, 1, 6), + to_date=datetime(2019, 1, 11), + filters_json="[]", + timeseries=1, + ) + ).insert() + + result = get(chart_name="Test Daily Dashboard Chart", refresh=1) + + self.assertEqual(result.get("datasets")[0].get("values"), [200.0, 400.0, 300.0, 0.0, 100.0, 0.0]) + self.assertEqual( + result.get("labels"), + ["01-06-2019", "01-07-2019", "01-08-2019", "01-09-2019", "01-10-2019", "01-11-2019"], + ) + + def test_weekly_dashboard_chart(self): + insert_test_records() + + if xhiveframework.db.exists("Dashboard Chart", "Test Weekly Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Weekly Dashboard Chart") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Weekly Dashboard Chart", + chart_type="Sum", + document_type="Communication", + based_on="communication_date", + value_based_on="rating", + timespan="Select Date Range", + time_interval="Weekly", + from_date=datetime(2018, 12, 30), + to_date=datetime(2019, 1, 15), + filters_json="[]", + timeseries=1, + ) + ).insert() + + with patch.object(xhiveframework.utils.data, "get_first_day_of_the_week", return_value="Monday"): + result = get(chart_name="Test Weekly Dashboard Chart", refresh=1) + + self.assertEqual(result.get("datasets")[0].get("values"), [50.0, 300.0, 800.0, 0.0]) + self.assertEqual(result.get("labels"), ["12-30-2018", "01-06-2019", "01-13-2019", "01-20-2019"]) + + def test_avg_dashboard_chart(self): + insert_test_records() + + if xhiveframework.db.exists("Dashboard Chart", "Test Average Dashboard Chart"): + xhiveframework.delete_doc("Dashboard Chart", "Test Average Dashboard Chart") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Average Dashboard Chart", + chart_type="Average", + document_type="Communication", + based_on="communication_date", + value_based_on="rating", + timespan="Select Date Range", + time_interval="Weekly", + from_date=datetime(2018, 12, 30), + to_date=datetime(2019, 1, 15), + filters_json="[]", + timeseries=1, + ) + ).insert() + + with patch.object(xhiveframework.utils.data, "get_first_day_of_the_week", return_value="Monday"): + result = get(chart_name="Test Average Dashboard Chart", refresh=1) + self.assertEqual(result.get("labels"), ["12-30-2018", "01-06-2019", "01-13-2019", "01-20-2019"]) + self.assertEqual(result.get("datasets")[0].get("values"), [50.0, 150.0, 266.6666666666667, 0.0]) + + def test_user_date_label_dashboard_chart(self): + xhiveframework.delete_doc_if_exists("Dashboard Chart", "Test Dashboard Chart Date Label") + + xhiveframework.get_doc( + dict( + doctype="Dashboard Chart", + chart_name="Test Dashboard Chart Date Label", + chart_type="Count", + document_type="DocType", + based_on="creation", + timespan="Select Date Range", + time_interval="Weekly", + from_date=datetime(2018, 12, 30), + to_date=datetime(2019, 1, 15), + filters_json="[]", + timeseries=1, + ) + ).insert() + + with patch.object(xhiveframework.utils.data, "get_user_date_format", return_value="dd.mm.yyyy"): + result = get(chart_name="Test Dashboard Chart Date Label") + self.assertEqual(sorted(result.get("labels")), sorted(["05.01.2019", "12.01.2019", "19.01.2019"])) + + with patch.object(xhiveframework.utils.data, "get_user_date_format", return_value="mm-dd-yyyy"): + result = get(chart_name="Test Dashboard Chart Date Label") + self.assertEqual(sorted(result.get("labels")), sorted(["01-19-2019", "01-05-2019", "01-12-2019"])) + + +def insert_test_records(): + create_new_communication("Communication 1", datetime(2018, 12, 30), 50) + create_new_communication("Communication 2", datetime(2019, 1, 4), 100) + create_new_communication("Communication 3", datetime(2019, 1, 6), 200) + create_new_communication("Communication 4", datetime(2019, 1, 7), 400) + create_new_communication("Communication 5", datetime(2019, 1, 8), 300) + create_new_communication("Communication 6", datetime(2019, 1, 10), 100) + + +def create_new_communication(subject, date, rating): + communication = { + "doctype": "Communication", + "subject": subject, + "rating": rating, + "communication_date": date, + } + comm = xhiveframework.get_doc(communication) + if not xhiveframework.db.exists("Communication", {"subject": comm.subject}): + comm.insert() diff --git a/xhiveframework/desk/doctype/dashboard_chart_field/__init__.py b/xhiveframework/desk/doctype/dashboard_chart_field/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.json b/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.json new file mode 100644 index 0000000..6347be4 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.json @@ -0,0 +1,37 @@ +{ + "actions": [], + "creation": "2020-02-28 11:40:27.017380", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "y_field", + "color" + ], + "fields": [ + { + "fieldname": "y_field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Y Field" + }, + { + "fieldname": "color", + "fieldtype": "Color", + "in_list_view": 1, + "label": "Color" + } + ], + "istable": 1, + "links": [], + "modified": "2020-02-28 11:48:24.731946", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard Chart Field", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.py b/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.py new file mode 100644 index 0000000..a981537 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_field/dashboard_chart_field.py @@ -0,0 +1,23 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DashboardChartField(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + color: DF.Color | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + y_field: DF.Literal[None] + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/dashboard_chart_link/__init__.py b/xhiveframework/desk/doctype/dashboard_chart_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.json b/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.json new file mode 100644 index 0000000..51b5ed3 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "creation": "2019-03-12 15:00:57.052684", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "chart", + "width" + ], + "fields": [ + { + "columns": 8, + "fieldname": "chart", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Chart", + "options": "Dashboard Chart" + }, + { + "default": "Half", + "fieldname": "width", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Width", + "options": "Half\nFull" + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-13 19:23:05.561687", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard Chart Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.py b/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.py new file mode 100644 index 0000000..6fd5deb --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_link/dashboard_chart_link.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DashboardChartLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + chart: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + width: DF.Literal["Half", "Full"] + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/dashboard_chart_source/__init__.py b/xhiveframework/desk/doctype/dashboard_chart_source/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.js b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.js new file mode 100644 index 0000000..060b300 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.js @@ -0,0 +1,4 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Dashboard Chart Source", {}); diff --git a/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.json b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.json new file mode 100644 index 0000000..fbe0ae9 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.json @@ -0,0 +1,69 @@ +{ + "actions": [], + "autoname": "field:source_name", + "creation": "2019-02-06 07:55:29.579840", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "source_name", + "module", + "timeseries" + ], + "fields": [ + { + "fieldname": "source_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Source Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Module", + "options": "Module Def", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "timeseries", + "fieldtype": "Check", + "label": "Timeseries" + } + ], + "links": [], + "modified": "2020-06-26 18:00:37.421491", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard Chart Source", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.py b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.py new file mode 100644 index 0000000..476520b --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_source/dashboard_chart_source.py @@ -0,0 +1,38 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import os + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.modules import get_module_path, scrub +from xhiveframework.modules.export_file import export_to_files + + +@xhiveframework.whitelist() +def get_config(name): + doc = xhiveframework.get_doc("Dashboard Chart Source", name) + with open( + os.path.join( + get_module_path(doc.module), "dashboard_chart_source", scrub(doc.name), scrub(doc.name) + ".js" + ), + ) as f: + return f.read() + + +class DashboardChartSource(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + module: DF.Link + source_name: DF.Data + timeseries: DF.Check + + # end: auto-generated types + def on_update(self): + export_to_files(record_list=[[self.doctype, self.name]], record_module=self.module, create_init=True) diff --git a/xhiveframework/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py b/xhiveframework/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py new file mode 100644 index 0000000..c7ad25a --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_chart_source/test_dashboard_chart_source.py @@ -0,0 +1,7 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDashboardChartSource(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/dashboard_settings/__init__.py b/xhiveframework/desk/doctype/dashboard_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.js b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.js new file mode 100644 index 0000000..1f444f0 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Dashboard Settings", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.json b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.json new file mode 100644 index 0000000..504cd4b --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.json @@ -0,0 +1,53 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-03-31 19:41:45.785014", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "chart_config" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "chart_config", + "fieldtype": "Code", + "label": "Chart Configuration", + "options": "JSON", + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2023-08-28 22:23:42.722543", + "modified_by": "Administrator", + "module": "Desk", + "name": "Dashboard Settings", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.py b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.py new file mode 100644 index 0000000..af32bc0 --- /dev/null +++ b/xhiveframework/desk/doctype/dashboard_settings/dashboard_settings.py @@ -0,0 +1,58 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework + +# import xhiveframework +from xhiveframework.model.document import Document + + +class DashboardSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + chart_config: DF.Code | None + user: DF.Link | None + # end: auto-generated types + pass + + +@xhiveframework.whitelist() +def create_dashboard_settings(user): + if not xhiveframework.db.exists("Dashboard Settings", user): + doc = xhiveframework.new_doc("Dashboard Settings") + doc.name = user + doc.insert(ignore_permissions=True) + xhiveframework.db.commit() + return doc + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + return f"""(`tabDashboard Settings`.name = {xhiveframework.db.escape(user)})""" + + +@xhiveframework.whitelist() +def save_chart_config(reset, config, chart_name): + reset = xhiveframework.parse_json(reset) + doc = xhiveframework.get_doc("Dashboard Settings", xhiveframework.session.user) + chart_config = xhiveframework.parse_json(doc.chart_config) or {} + + if reset: + chart_config[chart_name] = {} + else: + config = xhiveframework.parse_json(config) + if chart_name not in chart_config: + chart_config[chart_name] = {} + chart_config[chart_name].update(config) + + xhiveframework.db.set_value("Dashboard Settings", xhiveframework.session.user, "chart_config", json.dumps(chart_config)) diff --git a/xhiveframework/desk/doctype/desktop_icon/__init__.py b/xhiveframework/desk/doctype/desktop_icon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/desktop_icon/desktop_icon.js b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.js new file mode 100644 index 0000000..1484a0e --- /dev/null +++ b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Desktop Icon", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/desk/doctype/desktop_icon/desktop_icon.json b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.json new file mode 100644 index 0000000..ef88346 --- /dev/null +++ b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.json @@ -0,0 +1,175 @@ +{ + "actions": [], + "creation": "2016-02-22 03:47:45.387068", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "module_name", + "label", + "standard", + "custom", + "column_break_3", + "app", + "description", + "category", + "hidden", + "blocked", + "force_show", + "section_break_7", + "type", + "_doctype", + "_report", + "link", + "column_break_10", + "color", + "icon", + "reverse", + "idx" + ], + "fields": [ + { + "fieldname": "module_name", + "fieldtype": "Data", + "label": "Module Name" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "label": "Label" + }, + { + "default": "0", + "fieldname": "standard", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Standard" + }, + { + "default": "0", + "fieldname": "custom", + "fieldtype": "Check", + "label": "Custom", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "app", + "fieldtype": "Data", + "label": "App", + "read_only": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "fieldname": "category", + "fieldtype": "Data", + "label": "Category" + }, + { + "default": "0", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "default": "0", + "fieldname": "blocked", + "fieldtype": "Check", + "label": "Blocked" + }, + { + "default": "0", + "fieldname": "force_show", + "fieldtype": "Check", + "label": "Force Show", + "read_only": 1 + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Type", + "options": "module\nlist\nlink\npage\nquery-report" + }, + { + "fieldname": "_doctype", + "fieldtype": "Link", + "label": "_doctype", + "options": "DocType" + }, + { + "fieldname": "_report", + "fieldtype": "Link", + "label": "_report", + "options": "Report" + }, + { + "fieldname": "link", + "fieldtype": "Small Text", + "label": "Link" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "color", + "fieldtype": "Data", + "label": "Color" + }, + { + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon" + }, + { + "default": "0", + "fieldname": "reverse", + "fieldtype": "Check", + "label": "Reverse Icon Color" + }, + { + "fieldname": "idx", + "fieldtype": "Int", + "label": "Idx" + } + ], + "in_create": 1, + "links": [], + "modified": "2022-08-03 12:20:50.577580", + "modified_by": "Administrator", + "module": "Desk", + "name": "Desktop Icon", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "module_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/desktop_icon/desktop_icon.py b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.py new file mode 100644 index 0000000..5aca1b7 --- /dev/null +++ b/xhiveframework/desk/doctype/desktop_icon/desktop_icon.py @@ -0,0 +1,572 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import json +import random + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils.user import UserPermissions + + +class DesktopIcon(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + _doctype: DF.Link | None + _report: DF.Link | None + app: DF.Data | None + blocked: DF.Check + category: DF.Data | None + color: DF.Data | None + custom: DF.Check + description: DF.SmallText | None + force_show: DF.Check + hidden: DF.Check + icon: DF.Data | None + idx: DF.Int + label: DF.Data | None + link: DF.SmallText | None + module_name: DF.Data | None + reverse: DF.Check + standard: DF.Check + type: DF.Literal["module", "list", "link", "page", "query-report"] + + # end: auto-generated types + def validate(self): + if not self.label: + self.label = self.module_name + + def on_trash(self): + clear_desktop_icons_cache() + + +def after_doctype_insert(): + xhiveframework.db.add_unique("Desktop Icon", ("module_name", "owner", "standard")) + + +def get_desktop_icons(user=None): + """Return desktop icons for user""" + if not user: + user = xhiveframework.session.user + + user_icons = xhiveframework.cache.hget("desktop_icons", user) + + if not user_icons: + fields = [ + "module_name", + "hidden", + "label", + "link", + "type", + "icon", + "color", + "description", + "category", + "_doctype", + "_report", + "idx", + "force_show", + "reverse", + "custom", + "standard", + "blocked", + ] + + active_domains = xhiveframework.get_active_domains() + + blocked_doctypes = xhiveframework.get_all( + "DocType", + filters={"ifnull(restrict_to_domain, '')": ("not in", ",".join(active_domains))}, + fields=["name"], + ) + + blocked_doctypes = [d.get("name") for d in blocked_doctypes] + + standard_icons = xhiveframework.get_all("Desktop Icon", fields=fields, filters={"standard": 1}) + + standard_map = {} + for icon in standard_icons: + if icon._doctype in blocked_doctypes: + icon.blocked = 1 + standard_map[icon.module_name] = icon + + user_icons = xhiveframework.get_all("Desktop Icon", fields=fields, filters={"standard": 0, "owner": user}) + + # update hidden property + for icon in user_icons: + standard_icon = standard_map.get(icon.module_name, None) + + if icon._doctype in blocked_doctypes: + icon.blocked = 1 + + # override properties from standard icon + if standard_icon: + for key in ("route", "label", "color", "icon", "link"): + if standard_icon.get(key): + icon[key] = standard_icon.get(key) + + if standard_icon.blocked: + icon.hidden = 1 + + # flag for modules_select dialog + icon.hidden_in_standard = 1 + + elif standard_icon.force_show: + icon.hidden = 0 + + # add missing standard icons (added via new install apps?) + user_icon_names = [icon.module_name for icon in user_icons] + for standard_icon in standard_icons: + if standard_icon.module_name not in user_icon_names: + # if blocked, hidden too! + if standard_icon.blocked: + standard_icon.hidden = 1 + standard_icon.hidden_in_standard = 1 + + user_icons.append(standard_icon) + + user_blocked_modules = xhiveframework.get_doc("User", user).get_blocked_modules() + for icon in user_icons: + if icon.module_name in user_blocked_modules: + icon.hidden = 1 + + # sort by idx + user_icons.sort(key=lambda a: a.idx) + + # translate + for d in user_icons: + if d.label: + d.label = _(d.label, context=d.parent) + + xhiveframework.cache.hset("desktop_icons", user, user_icons) + + return user_icons + + +@xhiveframework.whitelist() +def add_user_icon(_doctype, _report=None, label=None, link=None, type="link", standard=0): + """Add a new user desktop icon to the desktop""" + + if not label: + label = _doctype or _report + if not link: + link = f"List/{_doctype}" + + # find if a standard icon exists + icon_name = xhiveframework.db.exists( + "Desktop Icon", {"standard": standard, "link": link, "owner": xhiveframework.session.user} + ) + + if icon_name: + if xhiveframework.db.get_value("Desktop Icon", icon_name, "hidden"): + # if it is hidden, unhide it + xhiveframework.db.set_value("Desktop Icon", icon_name, "hidden", 0) + clear_desktop_icons_cache() + + else: + idx = ( + xhiveframework.db.sql("select max(idx) from `tabDesktop Icon` where owner=%s", xhiveframework.session.user)[0][0] + or xhiveframework.db.sql("select count(*) from `tabDesktop Icon` where standard=1")[0][0] + ) + + if not xhiveframework.db.get_value("Report", _report): + _report = None + userdefined_icon = xhiveframework.db.get_value( + "DocType", _doctype, ["icon", "color", "module"], as_dict=True + ) + else: + userdefined_icon = xhiveframework.db.get_value( + "Report", _report, ["icon", "color", "module"], as_dict=True + ) + + module_icon = xhiveframework.get_value( + "Desktop Icon", + {"standard": 1, "module_name": userdefined_icon.module}, + ["name", "icon", "color", "reverse"], + as_dict=True, + ) + + if not module_icon: + module_icon = xhiveframework._dict() + opts = random.choice(palette) + module_icon.color = opts[0] + module_icon.reverse = 0 if (len(opts) > 1) else 1 + + try: + new_icon = xhiveframework.get_doc( + { + "doctype": "Desktop Icon", + "label": label, + "module_name": label, + "link": link, + "type": type, + "_doctype": _doctype, + "_report": _report, + "icon": userdefined_icon.icon or module_icon.icon, + "color": userdefined_icon.color or module_icon.color, + "reverse": module_icon.reverse, + "idx": idx + 1, + "custom": 1, + "standard": standard, + } + ).insert(ignore_permissions=True) + clear_desktop_icons_cache() + + icon_name = new_icon.name + + except xhiveframework.UniqueValidationError: + xhiveframework.throw(_("Desktop Icon already exists")) + except Exception as e: + raise e + + return icon_name + + +@xhiveframework.whitelist() +def set_order(new_order, user=None): + """set new order by duplicating user icons (if user is set) or set global order""" + if isinstance(new_order, str): + new_order = json.loads(new_order) + for i, module_name in enumerate(new_order): + if module_name not in ("Explore",): + if user: + icon = get_user_copy(module_name, user) + else: + name = xhiveframework.db.get_value("Desktop Icon", {"standard": 1, "module_name": module_name}) + if name: + icon = xhiveframework.get_doc("Desktop Icon", name) + else: + # standard icon missing, create one for DocType + name = add_user_icon(module_name, standard=1) + icon = xhiveframework.get_doc("Desktop Icon", name) + + icon.db_set("idx", i) + + clear_desktop_icons_cache() + + +def set_desktop_icons(visible_list, ignore_duplicate=True): + """Resets all lists and makes only the given one standard, + if the desktop icon does not exist and the name is a DocType, then will create + an icon for the doctype""" + + # clear all custom only if setup is not complete + if not int(xhiveframework.defaults.get_defaults().setup_complete or 0): + xhiveframework.db.delete("Desktop Icon", {"standard": 0}) + + # set standard as blocked and hidden if setting first active domain + if not xhiveframework.flags.keep_desktop_icons: + xhiveframework.db.sql("update `tabDesktop Icon` set blocked=0, hidden=1 where standard=1") + + # set as visible if present, or add icon + for module_name in list(visible_list): + name = xhiveframework.db.get_value("Desktop Icon", {"module_name": module_name}) + if name: + xhiveframework.db.set_value("Desktop Icon", name, "hidden", 0) + else: + if xhiveframework.db.exists("DocType", module_name): + try: + add_user_icon(module_name, standard=1) + except xhiveframework.UniqueValidationError as e: + if not ignore_duplicate: + raise e + else: + visible_list.remove(module_name) + xhiveframework.clear_last_message() + + # set the order + set_order(visible_list) + + clear_desktop_icons_cache() + + +def set_hidden_list(hidden_list, user=None): + """Sets property `hidden`=1 in **Desktop Icon** for given user. + If user is None then it will set global values. + It will also set the rest of the icons as shown (`hidden` = 0)""" + if isinstance(hidden_list, str): + hidden_list = json.loads(hidden_list) + + # set as hidden + for module_name in hidden_list: + set_hidden(module_name, user, 1) + + # set as seen + for module_name in list(set(get_all_icons()) - set(hidden_list)): + set_hidden(module_name, user, 0) + + if user: + clear_desktop_icons_cache() + else: + xhiveframework.clear_cache() + + +def set_hidden(module_name, user=None, hidden=1): + """Set module hidden property for given user. If user is not specified, + hide/unhide it globally""" + if user: + icon = get_user_copy(module_name, user) + + if hidden and icon.custom: + xhiveframework.delete_doc(icon.doctype, icon.name, ignore_permissions=True) + return + + # hidden by user + icon.db_set("hidden", hidden) + else: + icon = xhiveframework.get_doc("Desktop Icon", {"standard": 1, "module_name": module_name}) + + # blocked is globally hidden + icon.db_set("blocked", hidden) + + +def get_all_icons(): + return [ + d.module_name for d in xhiveframework.get_all("Desktop Icon", filters={"standard": 1}, fields=["module_name"]) + ] + + +def clear_desktop_icons_cache(user=None): + xhiveframework.cache.hdel("desktop_icons", user or xhiveframework.session.user) + xhiveframework.cache.hdel("bootinfo", user or xhiveframework.session.user) + + +def get_user_copy(module_name, user=None): + """Return user copy (Desktop Icon) of the given module_name. If user copy does not exist, create one. + + :param module_name: Name of the module + :param user: User for which the copy is required (optional) + """ + if not user: + user = xhiveframework.session.user + + desktop_icon_name = xhiveframework.db.get_value( + "Desktop Icon", {"module_name": module_name, "owner": user, "standard": 0} + ) + + if desktop_icon_name: + return xhiveframework.get_doc("Desktop Icon", desktop_icon_name) + else: + return make_user_copy(module_name, user) + + +def make_user_copy(module_name, user): + """Insert and return the user copy of a standard Desktop Icon""" + standard_name = xhiveframework.db.get_value("Desktop Icon", {"module_name": module_name, "standard": 1}) + + if not standard_name: + xhiveframework.throw(_("{0} not found").format(module_name), xhiveframework.DoesNotExistError) + + original = xhiveframework.get_doc("Desktop Icon", standard_name) + + desktop_icon = xhiveframework.get_doc( + {"doctype": "Desktop Icon", "standard": 0, "owner": user, "module_name": module_name} + ) + + for key in ( + "app", + "label", + "route", + "type", + "_doctype", + "idx", + "reverse", + "force_show", + "link", + "icon", + "color", + ): + if original.get(key): + desktop_icon.set(key, original.get(key)) + + desktop_icon.insert(ignore_permissions=True) + + return desktop_icon + + +def sync_desktop_icons(): + """Sync desktop icons from all apps""" + for app in xhiveframework.get_installed_apps(): + sync_from_app(app) + + +def sync_from_app(app): + """Sync desktop icons from app. To be called during install""" + try: + modules = xhiveframework.get_attr(app + ".config.desktop.get_data")() or {} + except ImportError: + return [] + + if isinstance(modules, dict): + modules_list = [] + for m, desktop_icon in modules.items(): + desktop_icon["module_name"] = m + modules_list.append(desktop_icon) + else: + modules_list = modules + + for i, m in enumerate(modules_list): + desktop_icon_name = xhiveframework.db.get_value( + "Desktop Icon", {"module_name": m["module_name"], "app": app, "standard": 1} + ) + if desktop_icon_name: + desktop_icon = xhiveframework.get_doc("Desktop Icon", desktop_icon_name) + else: + # new icon + desktop_icon = xhiveframework.get_doc( + {"doctype": "Desktop Icon", "idx": i, "standard": 1, "app": app, "owner": "Administrator"} + ) + + if "doctype" in m: + m["_doctype"] = m.pop("doctype") + + desktop_icon.update(m) + try: + desktop_icon.save() + except xhiveframework.exceptions.UniqueValidationError: + pass + + return modules_list + + +@xhiveframework.whitelist() +def update_icons(hidden_list, user=None): + """update modules""" + if not user: + xhiveframework.only_for("System Manager") + + set_hidden_list(hidden_list, user) + xhiveframework.msgprint(xhiveframework._("Updated"), indicator="green", title=_("Success"), alert=True) + + +def get_context(context): + context.icons = get_user_icons(xhiveframework.session.user) + context.user = xhiveframework.session.user + + if "System Manager" in xhiveframework.get_roles(): + context.users = xhiveframework.get_all( + "User", + filters={"user_type": "System User", "enabled": 1}, + fields=["name", "first_name", "last_name"], + ) + + +@xhiveframework.whitelist() +def get_module_icons(user=None): + if user != xhiveframework.session.user: + xhiveframework.only_for("System Manager") + + if not user: + icons = xhiveframework.get_all("Desktop Icon", fields="*", filters={"standard": 1}, order_by="idx") + else: + xhiveframework.cache.hdel("desktop_icons", user) + icons = get_user_icons(user) + + for icon in icons: + icon.value = xhiveframework.db.escape(_(icon.label or icon.module_name)) + + return {"icons": icons, "user": user} + + +def get_user_icons(user): + """Get user icons for module setup page""" + user_perms = UserPermissions(user) + user_perms.build_permissions() + + from xhiveframework.boot import get_allowed_pages + + allowed_pages = get_allowed_pages() + + icons = [] + for icon in get_desktop_icons(user): + add = True + if icon.hidden_in_standard: + add = False + + if not icon.custom: + if icon.module_name == ["Help", "Settings"]: + pass + + elif icon.type == "page" and icon.link not in allowed_pages: + add = False + + elif icon.type == "module" and icon.module_name not in user_perms.allow_modules: + add = False + + if add: + icons.append(icon) + + return icons + + +palette = ( + ("#FFC4C4",), + ("#FFE8CD",), + ("#FFD2C2",), + ("#FF8989",), + ("#FFD19C",), + ("#FFA685",), + ("#FF4D4D", 1), + ("#FFB868",), + ("#FF7846", 1), + ("#A83333", 1), + ("#A87945", 1), + ("#A84F2E", 1), + ("#D2D2FF",), + ("#F8D4F8",), + ("#DAC7FF",), + ("#A3A3FF",), + ("#F3AAF0",), + ("#B592FF",), + ("#7575FF", 1), + ("#EC7DEA", 1), + ("#8E58FF", 1), + ("#4D4DA8", 1), + ("#934F92", 1), + ("#5E3AA8", 1), + ("#EBF8CC",), + ("#FFD7D7",), + ("#D2F8ED",), + ("#D9F399",), + ("#FFB1B1",), + ("#A4F3DD",), + ("#C5EC63",), + ("#FF8989", 1), + ("#77ECCA",), + ("#7B933D", 1), + ("#A85B5B", 1), + ("#49937E", 1), + ("#FFFACD",), + ("#D2F1FF",), + ("#CEF6D1",), + ("#FFF69C",), + ("#A6E4FF",), + ("#9DECA2",), + ("#FFF168",), + ("#78D6FF",), + ("#6BE273",), + ("#A89F45", 1), + ("#4F8EA8", 1), + ("#428B46", 1), +) + + +@xhiveframework.whitelist() +def hide(name, user=None): + if not user: + user = xhiveframework.session.user + + try: + set_hidden(name, user, hidden=1) + clear_desktop_icons_cache() + except Exception: + return False + + return True diff --git a/xhiveframework/desk/doctype/event/README.md b/xhiveframework/desk/doctype/event/README.md new file mode 100644 index 0000000..571c78c --- /dev/null +++ b/xhiveframework/desk/doctype/event/README.md @@ -0,0 +1 @@ +Calendar Event \ No newline at end of file diff --git a/xhiveframework/desk/doctype/event/__init__.py b/xhiveframework/desk/doctype/event/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/desk/doctype/event/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/desk/doctype/event/event.js b/xhiveframework/desk/doctype/event/event.js new file mode 100644 index 0000000..ede962b --- /dev/null +++ b/xhiveframework/desk/doctype/event/event.js @@ -0,0 +1,117 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt +xhiveframework.provide("xhiveframework.desk"); + +xhiveframework.ui.form.on("Event", { + onload: function (frm) { + frm.set_query("reference_doctype", "event_participants", function () { + return { + filters: { + issingle: 0, + }, + }; + }); + frm.set_query("google_calendar", function () { + return { + filters: { + owner: xhiveframework.session.user, + }, + }; + }); + }, + refresh: function (frm) { + if (frm.doc.event_participants) { + frm.doc.event_participants.forEach((value) => { + frm.add_custom_button( + __(value.reference_docname), + function () { + xhiveframework.set_route("Form", value.reference_doctype, value.reference_docname); + }, + __("Participants") + ); + }); + } + + frm.page.set_inner_btn_group_as_primary(__("Add Participants")); + + frm.add_custom_button( + __("Add Contacts"), + function () { + new xhiveframework.desk.eventParticipants(frm, "Contact"); + }, + __("Add Participants") + ); + + const [ends_on_date] = frm.doc.ends_on + ? frm.doc.ends_on.split(" ") + : frm.doc.starts_on?.split(" ") || []; + + if ( + ends_on_date && + frm.doc.google_meet_link && + xhiveframework.datetime.now_date() <= ends_on_date + ) { + frm.dashboard.set_headline( + __("Join video conference with {0}", [ + `Google Meet`, + ]) + ); + } + }, + repeat_on: function (frm) { + if (frm.doc.repeat_on === "Every Day") { + ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"].map( + function (v) { + frm.set_value(v, 1); + } + ); + } + }, +}); + +xhiveframework.ui.form.on("Event Participants", { + event_participants_remove: function (frm, cdt, cdn) { + if (cdt && !cdn.includes("New Event Participants")) { + xhiveframework.call({ + type: "POST", + method: "xhiveframework.desk.doctype.event.event.delete_communication", + args: { + event: frm.doc, + reference_doctype: cdt, + reference_docname: cdn, + }, + freeze: true, + callback: function (r) { + if (r.exc) { + xhiveframework.show_alert({ + message: __("{0}", [r.exc]), + indicator: "orange", + }); + } + }, + }); + } + }, +}); + +xhiveframework.desk.eventParticipants = class eventParticipants { + constructor(frm, doctype) { + this.frm = frm; + this.doctype = doctype; + this.make(); + } + + make() { + let me = this; + + let table = me.frm.get_field("event_participants").grid; + new xhiveframework.ui.form.LinkSelector({ + doctype: me.doctype, + dynamic_link_field: "reference_doctype", + dynamic_link_reference: me.doctype, + fieldname: "reference_docname", + target: table, + txt: "", + }); + } +}; diff --git a/xhiveframework/desk/doctype/event/event.json b/xhiveframework/desk/doctype/event/event.json new file mode 100644 index 0000000..3a4f192 --- /dev/null +++ b/xhiveframework/desk/doctype/event/event.json @@ -0,0 +1,339 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "EV.#####", + "creation": "2013-06-10 13:17:47", + "doctype": "DocType", + "document_type": "Document", + "email_append_to": 1, + "engine": "InnoDB", + "field_order": [ + "details", + "subject", + "event_category", + "event_type", + "color", + "send_reminder", + "repeat_this_event", + "column_break_4", + "starts_on", + "ends_on", + "status", + "sender", + "all_day", + "sync_with_google_calendar", + "add_video_conferencing", + "sb_00", + "google_calendar", + "google_calendar_id", + "cb_00", + "google_calendar_event_id", + "google_meet_link", + "pulled_from_google_calendar", + "section_break_13", + "repeat_on", + "repeat_till", + "column_break_16", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday", + "section_break_8", + "description", + "participants", + "event_participants" + ], + "fields": [ + { + "fieldname": "details", + "fieldtype": "Section Break", + "label": "Details", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "subject", + "fieldtype": "Small Text", + "in_global_search": 1, + "in_list_view": 1, + "label": "Subject", + "reqd": 1 + }, + { + "fieldname": "event_category", + "fieldtype": "Select", + "label": "Event Category", + "options": "Event\nMeeting\nCall\nSent/Received Email\nOther" + }, + { + "fieldname": "event_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Event Type", + "oldfieldname": "event_type", + "oldfieldtype": "Select", + "options": "Private\nPublic", + "reqd": 1, + "search_index": 1 + }, + { + "default": "1", + "fieldname": "send_reminder", + "fieldtype": "Check", + "label": "Send an email reminder in the morning" + }, + { + "default": "0", + "fieldname": "repeat_this_event", + "fieldtype": "Check", + "label": "Repeat this Event" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "starts_on", + "fieldtype": "Datetime", + "label": "Starts on", + "reqd": 1 + }, + { + "fieldname": "ends_on", + "fieldtype": "Datetime", + "label": "Ends on" + }, + { + "default": "0", + "fieldname": "all_day", + "fieldtype": "Check", + "label": "All Day" + }, + { + "depends_on": "repeat_this_event", + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "depends_on": "repeat_this_event", + "fieldname": "repeat_on", + "fieldtype": "Select", + "in_global_search": 1, + "label": "Repeat On", + "options": "\nDaily\nWeekly\nMonthly\nYearly" + }, + { + "depends_on": "repeat_this_event", + "description": "Leave blank to repeat always", + "fieldname": "repeat_till", + "fieldtype": "Date", + "label": "Repeat Till" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "monday", + "fieldtype": "Check", + "label": "Monday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "tuesday", + "fieldtype": "Check", + "label": "Tuesday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "wednesday", + "fieldtype": "Check", + "label": "Wednesday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "thursday", + "fieldtype": "Check", + "label": "Thursday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "friday", + "fieldtype": "Check", + "label": "Friday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "saturday", + "fieldtype": "Check", + "label": "Saturday" + }, + { + "default": "0", + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Weekly\"", + "fieldname": "sunday", + "fieldtype": "Check", + "label": "Sunday" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "color", + "fieldtype": "Color", + "label": "Color" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_global_search": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "width": "300px" + }, + { + "fieldname": "participants", + "fieldtype": "Section Break", + "label": "Participants", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "event_participants", + "fieldtype": "Table", + "label": "Event Participants", + "options": "Event Participants" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nCompleted\nClosed\nCancelled" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.sync_with_google_calendar || doc.pulled_from_google_calendar", + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Google Calendar" + }, + { + "fetch_from": "google_calendar.google_calendar_id", + "fieldname": "google_calendar_id", + "fieldtype": "Data", + "label": "Google Calendar ID", + "read_only": 1 + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "fieldname": "google_calendar_event_id", + "fieldtype": "Data", + "label": "Google Calendar Event ID", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "sync_with_google_calendar", + "fieldtype": "Check", + "label": "Sync with Google Calendar" + }, + { + "fieldname": "google_calendar", + "fieldtype": "Link", + "label": "Google Calendar", + "options": "Google Calendar" + }, + { + "default": "0", + "fieldname": "pulled_from_google_calendar", + "fieldtype": "Check", + "label": "Pulled from Google Calendar", + "read_only": 1 + }, + { + "fieldname": "sender", + "fieldtype": "Data", + "label": "Sender", + "options": "Email", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.sync_with_google_calendar", + "description": "via Google Meet", + "fieldname": "add_video_conferencing", + "fieldtype": "Check", + "label": "Add Video Conferencing" + }, + { + "fieldname": "google_meet_link", + "fieldtype": "Data", + "label": "Google Meet Link", + "no_copy": 1, + "read_only": 1 + } + ], + "icon": "fa fa-calendar", + "idx": 1, + "links": [], + "modified": "2023-06-23 10:33:15.685368", + "modified_by": "Administrator", + "module": "Desk", + "name": "Event", + "naming_rule": "Expression (old style)", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sender_field": "sender", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "subject_field": "subject", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1, + "track_views": 1 +} diff --git a/xhiveframework/desk/doctype/event/event.py b/xhiveframework/desk/doctype/event/event.py new file mode 100644 index 0000000..c29ac14 --- /dev/null +++ b/xhiveframework/desk/doctype/event/event.py @@ -0,0 +1,482 @@ +# Copyright (c) 2018, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.contacts.doctype.contact.contact import get_default_contact +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + is_email_notifications_enabled_for_type, +) +from xhiveframework.desk.reportview import get_filters_cond +from xhiveframework.model.document import Document +from xhiveframework.utils import ( + add_days, + add_months, + cint, + cstr, + date_diff, + format_datetime, + get_datetime_str, + getdate, + now_datetime, + nowdate, +) +from xhiveframework.utils.user import get_enabled_system_users + +weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] +communication_mapping = { + "": "Event", + "Event": "Event", + "Meeting": "Meeting", + "Call": "Phone", + "Sent/Received Email": "Email", + "Other": "Other", +} + + +class Event(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.event_participants.event_participants import EventParticipants + from xhiveframework.types import DF + + add_video_conferencing: DF.Check + all_day: DF.Check + color: DF.Color | None + description: DF.TextEditor | None + ends_on: DF.Datetime | None + event_category: DF.Literal["Event", "Meeting", "Call", "Sent/Received Email", "Other"] + event_participants: DF.Table[EventParticipants] + event_type: DF.Literal["Private", "Public"] + friday: DF.Check + google_calendar: DF.Link | None + google_calendar_event_id: DF.Data | None + google_calendar_id: DF.Data | None + google_meet_link: DF.Data | None + monday: DF.Check + pulled_from_google_calendar: DF.Check + repeat_on: DF.Literal["", "Daily", "Weekly", "Monthly", "Yearly"] + repeat_this_event: DF.Check + repeat_till: DF.Date | None + saturday: DF.Check + send_reminder: DF.Check + sender: DF.Data | None + starts_on: DF.Datetime + status: DF.Literal["Open", "Completed", "Closed", "Cancelled"] + subject: DF.SmallText + sunday: DF.Check + sync_with_google_calendar: DF.Check + thursday: DF.Check + tuesday: DF.Check + wednesday: DF.Check + + # end: auto-generated types + def validate(self): + if not self.starts_on: + self.starts_on = now_datetime() + + # if start == end this scenario doesn't make sense i.e. it starts and ends at the same second! + self.ends_on = None if self.starts_on == self.ends_on else self.ends_on + + if self.starts_on and self.ends_on: + self.validate_from_to_dates("starts_on", "ends_on") + + if self.repeat_on == "Daily" and self.ends_on and getdate(self.starts_on) != getdate(self.ends_on): + xhiveframework.throw(_("Daily Events should finish on the Same Day.")) + + if self.sync_with_google_calendar and not self.google_calendar: + xhiveframework.throw(_("Select Google Calendar to which event should be synced.")) + + if not self.sync_with_google_calendar: + self.add_video_conferencing = 0 + + def before_save(self): + self.set_participants_email() + + def on_update(self): + self.sync_communication() + + def on_trash(self): + communications = xhiveframework.get_all( + "Communication", dict(reference_doctype=self.doctype, reference_name=self.name) + ) + if communications: + for communication in communications: + xhiveframework.delete_doc_if_exists("Communication", communication.name) + + def sync_communication(self): + if self.event_participants: + for participant in self.event_participants: + filters = [ + ["Communication", "reference_doctype", "=", self.doctype], + ["Communication", "reference_name", "=", self.name], + ["Communication Link", "link_doctype", "=", participant.reference_doctype], + ["Communication Link", "link_name", "=", participant.reference_docname], + ] + if comms := xhiveframework.get_all("Communication", filters=filters, fields=["name"], distinct=True): + for comm in comms: + communication = xhiveframework.get_doc("Communication", comm.name) + self.update_communication(participant, communication) + else: + meta = xhiveframework.get_meta(participant.reference_doctype) + if hasattr(meta, "allow_events_in_timeline") and meta.allow_events_in_timeline == 1: + self.create_communication(participant) + + def create_communication(self, participant): + communication = xhiveframework.new_doc("Communication") + self.update_communication(participant, communication) + self.communication = communication.name + + def update_communication(self, participant, communication): + communication.communication_medium = "Event" + communication.subject = self.subject + communication.content = self.description if self.description else self.subject + communication.communication_date = self.starts_on + communication.sender = self.owner + communication.sender_full_name = xhiveframework.utils.get_fullname(self.owner) + communication.reference_doctype = self.doctype + communication.reference_name = self.name + communication.communication_medium = ( + communication_mapping.get(self.event_category) if self.event_category else "" + ) + communication.status = "Linked" + communication.add_link(participant.reference_doctype, participant.reference_docname) + communication.save(ignore_permissions=True) + + def add_participant(self, doctype, docname): + """Add a single participant to event participants + + Args: + doctype (string): Reference Doctype + docname (string): Reference Docname + """ + self.append( + "event_participants", + { + "reference_doctype": doctype, + "reference_docname": docname, + }, + ) + + def add_participants(self, participants): + """Add participant entry + + Args: + participants ([Array]): Array of a dict with doctype and docname + """ + for participant in participants: + self.add_participant(participant["doctype"], participant["docname"]) + + def set_participants_email(self): + for participant in self.event_participants: + if participant.email: + continue + + if participant.reference_doctype != "Contact": + participant_contact = get_default_contact( + participant.reference_doctype, participant.reference_docname + ) + else: + participant_contact = participant.reference_docname + + participant.email = ( + xhiveframework.get_value("Contact", participant_contact, "email_id") if participant_contact else None + ) + + +@xhiveframework.whitelist() +def delete_communication(event, reference_doctype, reference_docname): + deleted_participant = xhiveframework.get_doc(reference_doctype, reference_docname) + if isinstance(event, str): + event = json.loads(event) + + filters = [ + ["Communication", "reference_doctype", "=", event.get("doctype")], + ["Communication", "reference_name", "=", event.get("name")], + ["Communication Link", "link_doctype", "=", deleted_participant.reference_doctype], + ["Communication Link", "link_name", "=", deleted_participant.reference_docname], + ] + + comms = xhiveframework.get_list("Communication", filters=filters, fields=["name"]) + + if comms: + deletion = [] + for comm in comms: + delete = xhiveframework.get_doc("Communication", comm.name).delete() + deletion.append(delete) + + return deletion + + return {} + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + return f"""(`tabEvent`.`event_type`='Public' or `tabEvent`.`owner`={xhiveframework.db.escape(user)})""" + + +def has_permission(doc, user): + if doc.event_type == "Public" or doc.owner == user: + return True + + return False + + +def send_event_digest(): + today = nowdate() + + # select only those users that have event reminder email notifications enabled + users = [ + user + for user in get_enabled_system_users() + if is_email_notifications_enabled_for_type(user.name, "Event Reminders") + ] + + for user in users: + events = get_events(today, today, user.name, for_reminder=True) + if events: + xhiveframework.set_user_lang(user.name, user.language) + + for e in events: + e.starts_on = format_datetime(e.starts_on, "hh:mm a") + if e.all_day: + e.starts_on = "All Day" + + xhiveframework.sendmail( + recipients=user.email, + subject=xhiveframework._("Upcoming Events for Today"), + template="upcoming_events", + args={ + "events": events, + }, + header=[xhiveframework._("Events in Today's Calendar"), "blue"], + ) + + +@xhiveframework.whitelist() +def get_events(start, end, user=None, for_reminder=False, filters=None) -> list[xhiveframework._dict]: + if not user: + user = xhiveframework.session.user + + if isinstance(filters, str): + filters = json.loads(filters) + + filter_condition = get_filters_cond("Event", filters, []) + + tables = ["`tabEvent`"] + if "`tabEvent Participants`" in filter_condition: + tables.append("`tabEvent Participants`") + + events = xhiveframework.db.sql( + """ + SELECT `tabEvent`.name, + `tabEvent`.subject, + `tabEvent`.description, + `tabEvent`.color, + `tabEvent`.starts_on, + `tabEvent`.ends_on, + `tabEvent`.owner, + `tabEvent`.all_day, + `tabEvent`.event_type, + `tabEvent`.repeat_this_event, + `tabEvent`.repeat_on, + `tabEvent`.repeat_till, + `tabEvent`.monday, + `tabEvent`.tuesday, + `tabEvent`.wednesday, + `tabEvent`.thursday, + `tabEvent`.friday, + `tabEvent`.saturday, + `tabEvent`.sunday + FROM {tables} + WHERE ( + ( + (date(`tabEvent`.starts_on) BETWEEN date(%(start)s) AND date(%(end)s)) + OR (date(`tabEvent`.ends_on) BETWEEN date(%(start)s) AND date(%(end)s)) + OR ( + date(`tabEvent`.starts_on) <= date(%(start)s) + AND date(`tabEvent`.ends_on) >= date(%(end)s) + ) + ) + OR ( + date(`tabEvent`.starts_on) <= date(%(start)s) + AND `tabEvent`.repeat_this_event=1 + AND coalesce(`tabEvent`.repeat_till, '3000-01-01') > date(%(start)s) + ) + ) + {reminder_condition} + {filter_condition} + AND ( + `tabEvent`.event_type='Public' + OR `tabEvent`.owner=%(user)s + OR EXISTS( + SELECT `tabDocShare`.name + FROM `tabDocShare` + WHERE `tabDocShare`.share_doctype='Event' + AND `tabDocShare`.share_name=`tabEvent`.name + AND `tabDocShare`.user=%(user)s + ) + ) + AND `tabEvent`.status='Open' + ORDER BY `tabEvent`.starts_on""".format( + tables=", ".join(tables), + filter_condition=filter_condition, + reminder_condition="AND coalesce(`tabEvent`.send_reminder, 0)=1" if for_reminder else "", + ), + { + "start": start, + "end": end, + "user": user, + }, + as_dict=1, + ) + + # process recurring events + start = start.split(" ", 1)[0] + end = end.split(" ", 1)[0] + add_events = [] + remove_events = [] + + def add_event(e, date): + new_event = e.copy() + + enddate = ( + add_days(date, int(date_diff(e.ends_on.split(" ", 1)[0], e.starts_on.split(" ", 1)[0]))) + if (e.starts_on and e.ends_on) + else date + ) + + new_event.starts_on = date + " " + e.starts_on.split(" ")[1] + new_event.ends_on = new_event.ends_on = enddate + " " + e.ends_on.split(" ")[1] if e.ends_on else None + + add_events.append(new_event) + + for e in events: + if e.repeat_this_event: + e.starts_on = get_datetime_str(e.starts_on) + e.ends_on = get_datetime_str(e.ends_on) if e.ends_on else None + + event_start, time_str = get_datetime_str(e.starts_on).split(" ") + + repeat = "3000-01-01" if cstr(e.repeat_till) == "" else e.repeat_till + + if e.repeat_on == "Yearly": + start_year = cint(start.split("-", 1)[0]) + end_year = cint(end.split("-", 1)[0]) + + # creates a string with date (27) and month (07) eg: 07-27 + event_start = "-".join(event_start.split("-")[1:]) + + # repeat for all years in period + for year in range(start_year, end_year + 1): + date = str(year) + "-" + event_start + if ( + getdate(date) >= getdate(start) + and getdate(date) <= getdate(end) + and getdate(date) <= getdate(repeat) + ): + add_event(e, date) + + remove_events.append(e) + + if e.repeat_on == "Monthly": + # creates a string with date (27) and month (07) and year (2019) eg: 2019-07-27 + year, month = start.split("-", maxsplit=2)[:2] + date = f"{year}-{month}-" + event_start.split("-", maxsplit=3)[2] + + # last day of month issue, start from prev month! + try: + getdate(date) + except Exception: + date = date.split("-") + date = date[0] + "-" + str(cint(date[1]) - 1) + "-" + date[2] + + start_from = date + for i in range(int(date_diff(end, start) / 30) + 3): + if ( + getdate(date) >= getdate(start) + and getdate(date) <= getdate(end) + and getdate(date) <= getdate(repeat) + and getdate(date) >= getdate(event_start) + ): + add_event(e, date) + + date = add_months(start_from, i + 1) + remove_events.append(e) + + if e.repeat_on == "Weekly": + for cnt in range(date_diff(end, start) + 1): + date = add_days(start, cnt) + if ( + getdate(date) >= getdate(start) + and getdate(date) <= getdate(end) + and getdate(date) <= getdate(repeat) + and getdate(date) >= getdate(event_start) + and e[weekdays[getdate(date).weekday()]] + ): + add_event(e, date) + + remove_events.append(e) + + if e.repeat_on == "Daily": + for cnt in range(date_diff(end, start) + 1): + date = add_days(start, cnt) + if ( + getdate(date) >= getdate(event_start) + and getdate(date) <= getdate(end) + and getdate(date) <= getdate(repeat) + ): + add_event(e, date) + + remove_events.append(e) + + for e in remove_events: + events.remove(e) + + events = events + add_events + + for e in events: + # remove weekday properties (to reduce message size) + for w in weekdays: + del e[w] + + return events + + +def delete_events(ref_type, ref_name, delete_event=False): + participations = xhiveframework.get_all( + "Event Participants", + filters={"reference_doctype": ref_type, "reference_docname": ref_name, "parenttype": "Event"}, + fields=["parent", "name"], + ) + + if participations: + for participation in participations: + if delete_event: + xhiveframework.delete_doc("Event", participation.parent, for_reload=True) + else: + total_participants = xhiveframework.get_all( + "Event Participants", filters={"parenttype": "Event", "parent": participation.parent} + ) + + if len(total_participants) <= 1: + xhiveframework.db.delete("Event", {"name": participation.parent}) + xhiveframework.db.delete("Event Participants", {"name": participation.name}) + + +# Close events if ends_on or repeat_till is less than now_datetime +def set_status_of_events(): + events = xhiveframework.get_list("Event", filters={"status": "Open"}, fields=["name", "ends_on", "repeat_till"]) + for event in events: + if (event.ends_on and getdate(event.ends_on) < getdate(nowdate())) or ( + event.repeat_till and getdate(event.repeat_till) < getdate(nowdate()) + ): + xhiveframework.db.set_value("Event", event.name, "status", "Closed") diff --git a/xhiveframework/desk/doctype/event/event_calendar.js b/xhiveframework/desk/doctype/event/event_calendar.js new file mode 100644 index 0000000..ceaba7a --- /dev/null +++ b/xhiveframework/desk/doctype/event/event_calendar.js @@ -0,0 +1,16 @@ +xhiveframework.views.calendar["Event"] = { + field_map: { + start: "starts_on", + end: "ends_on", + id: "name", + allDay: "all_day", + title: "subject", + status: "event_type", + color: "color", + }, + style_map: { + Public: "success", + Private: "info", + }, + get_events_method: "xhiveframework.desk.doctype.event.event.get_events", +}; diff --git a/xhiveframework/desk/doctype/event/event_list.js b/xhiveframework/desk/doctype/event/event_list.js new file mode 100644 index 0000000..f60115a --- /dev/null +++ b/xhiveframework/desk/doctype/event/event_list.js @@ -0,0 +1,8 @@ +xhiveframework.listview_settings["Event"] = { + add_fields: ["starts_on", "ends_on"], + onload: function () { + xhiveframework.route_options = { + status: "Open", + }; + }, +}; diff --git a/xhiveframework/desk/doctype/event/test_event.py b/xhiveframework/desk/doctype/event/test_event.py new file mode 100644 index 0000000..556f251 --- /dev/null +++ b/xhiveframework/desk/doctype/event/test_event.py @@ -0,0 +1,138 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +"""Use blog post test to test user permissions logic""" + +import json + +import xhiveframework +import xhiveframework.defaults +from xhiveframework.desk.doctype.event.event import get_events +from xhiveframework.test_runner import make_test_objects +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Event") + + +class TestEvent(XhiveFrameworkTestCase): + def setUp(self): + xhiveframework.db.delete("Event") + make_test_objects("Event", reset=True) + + self.test_records = xhiveframework.get_test_records("Event") + self.test_user = "test1@example.com" + + def tearDown(self): + xhiveframework.set_user("Administrator") + + def test_allowed_public(self): + xhiveframework.set_user(self.test_user) + doc = xhiveframework.get_doc("Event", xhiveframework.db.get_value("Event", {"subject": "_Test Event 1"})) + self.assertTrue(xhiveframework.has_permission("Event", doc=doc)) + + def test_not_allowed_private(self): + xhiveframework.set_user(self.test_user) + doc = xhiveframework.get_doc("Event", xhiveframework.db.get_value("Event", {"subject": "_Test Event 2"})) + self.assertFalse(xhiveframework.has_permission("Event", doc=doc)) + + def test_allowed_private_if_in_event_user(self): + name = xhiveframework.db.get_value("Event", {"subject": "_Test Event 3"}) + xhiveframework.share.add("Event", name, self.test_user, "read") + xhiveframework.set_user(self.test_user) + doc = xhiveframework.get_doc("Event", name) + self.assertTrue(xhiveframework.has_permission("Event", doc=doc)) + xhiveframework.set_user("Administrator") + xhiveframework.share.remove("Event", name, self.test_user) + + def test_event_list(self): + xhiveframework.set_user(self.test_user) + res = xhiveframework.get_list( + "Event", filters=[["Event", "subject", "like", "_Test Event%"]], fields=["name", "subject"] + ) + self.assertEqual(len(res), 1) + subjects = [r.subject for r in res] + self.assertTrue("_Test Event 1" in subjects) + self.assertFalse("_Test Event 3" in subjects) + self.assertFalse("_Test Event 2" in subjects) + + def test_revert_logic(self): + ev = xhiveframework.get_doc(self.test_records[0]).insert() + name = ev.name + + xhiveframework.delete_doc("Event", ev.name) + + # insert again + ev = xhiveframework.get_doc(self.test_records[0]).insert() + + # the name should be same! + self.assertEqual(ev.name, name) + + def test_assign(self): + from xhiveframework.desk.form.assign_to import add + + ev = xhiveframework.get_doc(self.test_records[0]).insert() + + add( + { + "assign_to": ["test@example.com"], + "doctype": "Event", + "name": ev.name, + "description": "Test Assignment", + } + ) + + ev = xhiveframework.get_doc("Event", ev.name) + + self.assertEqual(ev._assign, json.dumps(["test@example.com"])) + + # add another one + add( + { + "assign_to": [self.test_user], + "doctype": "Event", + "name": ev.name, + "description": "Test Assignment", + } + ) + + ev = xhiveframework.get_doc("Event", ev.name) + + self.assertEqual(set(json.loads(ev._assign)), {"test@example.com", self.test_user}) + + # Remove an assignment + todo = xhiveframework.get_doc( + "ToDo", + {"reference_type": ev.doctype, "reference_name": ev.name, "allocated_to": self.test_user}, + ) + todo.status = "Cancelled" + todo.save() + + ev = xhiveframework.get_doc("Event", ev.name) + self.assertEqual(ev._assign, json.dumps(["test@example.com"])) + + # cleanup + ev.delete() + + def test_recurring(self): + ev = xhiveframework.get_doc( + { + "doctype": "Event", + "subject": "_Test Event", + "starts_on": "2014-02-01", + "event_type": "Public", + "repeat_this_event": 1, + "repeat_on": "Yearly", + } + ) + ev.insert() + + ev_list = get_events("2014-02-01", "2014-02-01", "Administrator", for_reminder=True) + self.assertTrue(bool(list(filter(lambda e: e.name == ev.name, ev_list)))) + + ev_list1 = get_events("2015-01-20", "2015-01-20", "Administrator", for_reminder=True) + self.assertFalse(bool(list(filter(lambda e: e.name == ev.name, ev_list1)))) + + ev_list2 = get_events("2014-02-20", "2014-02-20", "Administrator", for_reminder=True) + self.assertFalse(bool(list(filter(lambda e: e.name == ev.name, ev_list2)))) + + ev_list3 = get_events("2015-02-01", "2015-02-01", "Administrator", for_reminder=True) + self.assertTrue(bool(list(filter(lambda e: e.name == ev.name, ev_list3)))) diff --git a/xhiveframework/desk/doctype/event/test_records.json b/xhiveframework/desk/doctype/event/test_records.json new file mode 100644 index 0000000..41d5803 --- /dev/null +++ b/xhiveframework/desk/doctype/event/test_records.json @@ -0,0 +1,23 @@ +[ + { + "doctype": "Event", + "subject":"_Test Event 1", + "starts_on": "2014-01-01", + "event_type": "Public", + "creation": "2014-01-01" + }, + { + "doctype": "Event", + "subject":"_Test Event 2", + "starts_on": "2014-01-01", + "event_type": "Private", + "creation": "2014-01-01" + }, + { + "doctype": "Event", + "subject": "_Test Event 3", + "starts_on": "2014-02-01", + "event_type": "Private", + "creation": "2014-02-01" + } +] diff --git a/xhiveframework/desk/doctype/event_participants/__init__.py b/xhiveframework/desk/doctype/event_participants/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/event_participants/event_participants.json b/xhiveframework/desk/doctype/event_participants/event_participants.json new file mode 100644 index 0000000..bbb0a24 --- /dev/null +++ b/xhiveframework/desk/doctype/event_participants/event_participants.json @@ -0,0 +1,49 @@ +{ + "actions": [], + "creation": "2018-09-21 15:44:58.836156", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_doctype", + "reference_docname", + "email" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "options": "reference_doctype", + "reqd": 1 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "options": "Email" + } + ], + "istable": 1, + "links": [], + "modified": "2022-10-18 17:49:33.549459", + "modified_by": "Administrator", + "module": "Desk", + "name": "Event Participants", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/event_participants/event_participants.py b/xhiveframework/desk/doctype/event_participants/event_participants.py new file mode 100644 index 0000000..ffc5eb0 --- /dev/null +++ b/xhiveframework/desk/doctype/event_participants/event_participants.py @@ -0,0 +1,22 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +from xhiveframework.model.document import Document + + +class EventParticipants(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + reference_docname: DF.DynamicLink + reference_doctype: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/form_tour/__init__.py b/xhiveframework/desk/doctype/form_tour/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/form_tour/form_tour.js b/xhiveframework/desk/doctype/form_tour/form_tour.js new file mode 100644 index 0000000..57ff3c9 --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour/form_tour.js @@ -0,0 +1,255 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Form Tour", { + refresh(frm) { + if (frm.doc.is_standard && !xhiveframework.boot.developer_mode) { + frm.trigger("disable_form"); + } + frm.set_query("reference_doctype", () => { + return { filters: { istable: 0 } }; + }); + frm.trigger("reference_doctype"); + frm.set_query("report_name", () => { + if (frm.doc.reference_doctype) { + return { + filters: { + ref_doctype: frm.doc.reference_doctype, + }, + }; + } + return {}; + }); + !frm.is_new() && add_custom_button(frm); + }, + async report_name(frm) { + if (!frm.doc.ui_tour || !frm.doc.report_name) return; + let { message } = await xhiveframework.db.get_value("Report", frm.doc.report_name, "ref_doctype"); + frm.set_value("reference_doctype", message?.ref_doctype || ""); + }, + async before_save(frm) { + if ( + frm.doc.select_view == "List" && + frm.doc.list_name == "Dashboard" && + frm.doc.dashboard_name && + frm.doc.reference_doctype + ) { + xhiveframework.throw( + __("Referance Doctype and Dashboard Name both can't be used at the same time.") + ); + } + frm.doc.ui_tour && (frm.doc.page_route = JSON.stringify(await get_path(frm))); + }, + disable_form: function (frm) { + frm.set_read_only(); + frm.fields + .filter((field) => field.has_input) + .forEach((field) => { + frm.set_df_property(field.df.fieldname, "read_only", "1"); + }); + frm.disable_save(); + }, + + reference_doctype(frm) { + if (!frm.doc.reference_doctype) return; + + frm.set_fields_as_options("fieldname", frm.doc.reference_doctype, (df) => !df.hidden).then( + (options) => { + frm.fields_dict.steps.grid.update_docfield_property( + "fieldname", + "options", + [""].concat(options) + ); + } + ); + + frm.set_fields_as_options( + "parent_fieldname", + frm.doc.reference_doctype, + (df) => df.fieldtype == "Table" && !df.hidden + ).then((options) => { + frm.fields_dict.steps.grid.update_docfield_property( + "parent_fieldname", + "options", + [""].concat(options) + ); + }); + if (!frm.doc.ui_tour) { + // remove report name if reference doctype is changed and report name is not valid. + xhiveframework.db + .get_list( + "Report", + { + filters: { + ref_doctype: frm.doc.reference_doctype, + }, + }, + { fields: ["name"] } + ) + .then((reports) => { + if (reports.findIndex((r) => r.name == frm.doc.report_name) == -1) { + frm.set_value("report_name", ""); + frm.refresh_field("report_name"); + } + }); + } + }, +}); + +let add_custom_button = (frm) => { + if (frm.doc.ui_tour) { + frm.add_custom_button(__("Reset"), function () { + xhiveframework.confirm( + __("This will reset this tour and show it to all users. Are you sure?"), + function () { + xhiveframework.call({ + method: "xhiveframework.desk.doctype.form_tour.form_tour.reset_tour", + args: { + tour_name: frm.doc.name, + }, + }); + delete xhiveframework.boot.user.onboarding_status[frm.doc.name]; + } + ); + }); + } else { + frm.add_custom_button(__("Show Tour"), async () => { + const issingle = await check_if_single(frm.doc.reference_doctype); + let route_changed = null; + + if (issingle) { + route_changed = xhiveframework.set_route("Form", frm.doc.reference_doctype); + } else if (frm.doc.first_document) { + const name = await get_first_document(frm.doc.reference_doctype); + route_changed = xhiveframework.set_route("Form", frm.doc.reference_doctype, name); + } else { + route_changed = xhiveframework.set_route("Form", frm.doc.reference_doctype, "new"); + } + route_changed.then(() => { + const tour_name = frm.doc.name; + cur_frm.tour.init({ tour_name }).then(() => cur_frm.tour.start()); + }); + }); + } +}; + +xhiveframework.ui.form.on("Form Tour Step", { + form_render(frm, cdt, cdn) { + if (locals[cdt][cdn].is_table_field) { + frm.trigger("parent_fieldname", cdt, cdn); + } + }, + parent_fieldname(frm, cdt, cdn) { + const child_row = locals[cdt][cdn]; + + const parent_fieldname_df = xhiveframework + .get_meta(frm.doc.reference_doctype) + .fields.find((df) => df.fieldname == child_row.parent_fieldname); + + frm.set_fields_as_options( + "fieldname", + parent_fieldname_df.options, + (df) => !df.hidden + ).then((options) => { + frm.fields_dict.steps.grid.update_docfield_property( + "fieldname", + "options", + [""].concat(options) + ); + if (child_row.fieldname) { + xhiveframework.model.set_value(cdt, cdn, "fieldname", child_row.fieldname); + } + }); + }, +}); + +async function check_if_single(doctype) { + const { message } = await xhiveframework.db.get_value("DocType", doctype, "issingle"); + return message.issingle || 0; +} +async function check_if_private_workspace(name) { + const { message } = await xhiveframework.db.get_value("Workspace", name, "public"); + return !message.public || 0; +} + +async function get_first_document(doctype) { + let docname; + + await xhiveframework.db.get_list(doctype, { order_by: "creation" }).then((res) => { + if (Array.isArray(res) && res.length) docname = res[0].name; + }); + + return docname || "new"; +} + +async function get_path(frm) { + let route = [frm.doc.view_name]; + switch (route[0]) { + case "Workspaces": + frm.doc.list_name = ""; + frm.doc.new_document_form = 0; + frm.doc.report_name = ""; + frm.doc.page_name = ""; + frm.doc.dashboard_name = ""; + frm.doc.reference_doctype = ""; + if (!frm.doc.workspace_name) { + route.push("*"); + return route; + } + if (await check_if_private_workspace(frm.doc.workspace_name)) { + route.push("private"); + } + route.push(frm.doc.workspace_name); + return route; + case "List": + frm.doc.workspace_name = ""; + frm.doc.new_document_form = 0; + frm.doc.list_name != "Report" && (frm.doc.report_name = ""); + frm.doc.list_name != "Dashboard" && (frm.doc.dashboard_name = ""); + frm.doc.page_name = ""; + if (frm.doc.list_name == "File") return ["List", "File"]; + if (!frm.doc.reference_doctype) { + if (frm.doc.list_name == "Dashboard") + return ["dashboard-view", frm.doc.dashboard_name || "*"]; + route.push("*"); + } else { + route.push(frm.doc.reference_doctype); + } + route.push(frm.doc.list_name); + return route; + case "Form": + frm.doc.workspace_name = ""; + frm.doc.list_name = ""; + frm.doc.report_name = ""; + frm.doc.page_name = ""; + frm.doc.dashboard_name = ""; + if (!frm.doc.reference_doctype) { + route.push("*"); + frm.doc.new_document_form && route.push("new-*"); + return route; + } + route.push(frm.doc.reference_doctype); + if (await check_if_single(frm.doc.reference_doctype)) { + route.push(frm.doc.reference_doctype); + } else if (frm.doc.new_document_form) { + route.push("new-" + xhiveframework.router.slug(frm.doc.reference_doctype)); + } + return route; + case "Tree": + frm.doc.workspace_name = ""; + frm.doc.list_name = ""; + frm.doc.new_document_form = 0; + frm.doc.report_name = ""; + frm.doc.page_name = ""; + frm.doc.dashboard_name = ""; + return route; + case "Page": + frm.doc.workspace_name = ""; + frm.doc.list_name = ""; + frm.doc.new_document_form = 0; + frm.doc.report_name = ""; + frm.doc.dashboard_name = ""; + frm.doc.reference_doctype = ""; + return [frm.doc.page_name]; + } +} diff --git a/xhiveframework/desk/doctype/form_tour/form_tour.json b/xhiveframework/desk/doctype/form_tour/form_tour.json new file mode 100644 index 0000000..6c04999 --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour/form_tour.json @@ -0,0 +1,209 @@ +{ + "actions": [], + "autoname": "field:title", + "creation": "2021-05-21 23:02:52.242721", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "view_name", + "workspace_name", + "list_name", + "report_name", + "dashboard_name", + "new_document_form", + "page_name", + "reference_doctype", + "module", + "column_break_6", + "ui_tour", + "track_steps", + "is_standard", + "save_on_complete", + "first_document", + "include_name_field", + "page_route", + "section_break_3", + "steps" + ], + "fields": [ + { + "depends_on": "eval:(!doc.ui_tour || doc.ui_tour && [\"Workspaces\", \"Page\", \"Tree\"].indexOf(doc.view_name) == -1);", + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document", + "mandatory_depends_on": "eval:(!doc.ui_tour)", + "options": "DocType" + }, + { + "depends_on": "eval:(doc.ui_tour || doc.reference_doctype)", + "fieldname": "steps", + "fieldtype": "Table", + "label": "Steps", + "options": "Form Tour Step", + "reqd": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "save_on_complete", + "fieldtype": "Check", + "label": "Save on Completion" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard" + }, + { + "depends_on": "eval: doc.ui_tour && doc.is_standard", + "fetch_from": "reference_doctype.module", + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def", + "read_only": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "first_document", + "fieldtype": "Check", + "label": "Show First Document Tour" + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour && !doc.first_document)", + "fieldname": "include_name_field", + "fieldtype": "Check", + "label": "Include Name Field" + }, + { + "default": "0", + "fieldname": "ui_tour", + "fieldtype": "Check", + "label": "UI Tour", + "set_only_once": 1 + }, + { + "fieldname": "page_route", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Page Route" + }, + { + "depends_on": "eval:(doc.ui_tour && doc.view_name == \"List\" && doc.list_name == \"Dashboard\")", + "fetch_from": ".", + "fieldname": "dashboard_name", + "fieldtype": "Link", + "label": "Select Dashboard", + "options": "Dashboard" + }, + { + "depends_on": "ui_tour", + "fieldname": "view_name", + "fieldtype": "Select", + "label": "View", + "mandatory_depends_on": "ui_tour", + "options": "Workspaces\nList\nForm\nTree\nPage" + }, + { + "depends_on": "eval:(doc.ui_tour && doc.view_name == \"Workspaces\")", + "fetch_from": ".", + "fieldname": "workspace_name", + "fieldtype": "Link", + "label": "Select Workspace", + "options": "Workspace" + }, + { + "depends_on": "eval:(doc.ui_tour && doc.view_name == \"Page\")", + "fetch_from": ".", + "fieldname": "page_name", + "fieldtype": "Link", + "label": "Select Page", + "mandatory_depends_on": "eval:(doc.ui_tour && doc.view_name == \"Page\")", + "options": "Page" + }, + { + "default": "List", + "depends_on": "eval:(doc.ui_tour && doc.view_name == \"List\")", + "fetch_from": ".", + "fieldname": "list_name", + "fieldtype": "Select", + "label": "Select List View", + "mandatory_depends_on": "eval:(doc.ui_tour && doc.view_name == \"List\")", + "options": "List\nReport\nDashboard\nKanban\nGantt\nCalendar\nFile\nImage\nInbox\nMap" + }, + { + "depends_on": "eval:(doc.ui_tour && doc.view_name == \"List\" && doc.list_name == \"Report\")", + "fetch_from": ".", + "fieldname": "report_name", + "fieldtype": "Link", + "label": "Select Report", + "options": "Report" + }, + { + "default": "0", + "depends_on": "ui_tour", + "description": "The next tour will start from where the user left off.", + "fieldname": "track_steps", + "fieldtype": "Check", + "label": "Track Steps" + }, + { + "default": "0", + "depends_on": "eval: (doc.ui_tour && doc.view_name == \"Form\")", + "fieldname": "new_document_form", + "fieldtype": "Check", + "label": "New Document Form" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 20:24:42.594360", + "modified_by": "Administrator", + "module": "Desk", + "name": "Form Tour", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Desk User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/form_tour/form_tour.py b/xhiveframework/desk/doctype/form_tour/form_tour.py new file mode 100644 index 0000000..4e8024c --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour/form_tour.py @@ -0,0 +1,113 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.modules.export_file import export_to_files + + +class FormTour(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.form_tour_step.form_tour_step import FormTourStep + from xhiveframework.types import DF + + dashboard_name: DF.Link | None + first_document: DF.Check + include_name_field: DF.Check + is_standard: DF.Check + list_name: DF.Literal[ + "List", "Report", "Dashboard", "Kanban", "Gantt", "Calendar", "File", "Image", "Inbox", "Map" + ] + module: DF.Link | None + new_document_form: DF.Check + page_name: DF.Link | None + page_route: DF.SmallText | None + reference_doctype: DF.Link | None + report_name: DF.Link | None + save_on_complete: DF.Check + steps: DF.Table[FormTourStep] + title: DF.Data + track_steps: DF.Check + ui_tour: DF.Check + view_name: DF.Literal["Workspaces", "List", "Form", "Tree", "Page"] + workspace_name: DF.Link | None + + # end: auto-generated types + def before_save(self): + if self.is_standard and not self.module: + if self.workspace_name: + self.module = xhiveframework.db.get_value("Workspace", self.workspace_name, "module") + elif self.dashboard_name: + dashboard_doctype = xhiveframework.db.get_value("Dashboard", self.dashboard_name, "module") + self.module = xhiveframework.db.get_value("DocType", dashboard_doctype, "module") + else: + self.module = "Desk" + if not self.ui_tour: + meta = xhiveframework.get_meta(self.reference_doctype) + for step in self.steps: + if step.is_table_field and step.parent_fieldname: + parent_field_df = meta.get_field(step.parent_fieldname) + step.child_doctype = parent_field_df.options + field_df = xhiveframework.get_meta(step.child_doctype).get_field(step.fieldname) + step.label = field_df.label + step.fieldtype = field_df.fieldtype + else: + field_df = meta.get_field(step.fieldname) + step.label = field_df.label + step.fieldtype = field_df.fieldtype + + def on_update(self): + xhiveframework.cache.delete_key("bootinfo") + + if xhiveframework.conf.developer_mode and self.is_standard: + export_to_files([["Form Tour", self.name]], self.module) + + def on_trash(self): + xhiveframework.cache.delete_key("bootinfo") + + +@xhiveframework.whitelist() +def reset_tour(tour_name): + for user in xhiveframework.get_all("User", pluck="name"): + onboarding_status = xhiveframework.parse_json(xhiveframework.db.get_value("User", user, "onboarding_status")) + onboarding_status.pop(tour_name, None) + xhiveframework.db.set_value( + "User", user, "onboarding_status", xhiveframework.as_json(onboarding_status), update_modified=False + ) + xhiveframework.cache.hdel("bootinfo", user) + + xhiveframework.msgprint(_("Successfully reset onboarding status for all users."), alert=True) + + +@xhiveframework.whitelist() +def update_user_status(value, step): + from xhiveframework.utils.telemetry import capture + + step = xhiveframework.parse_json(step) + tour = xhiveframework.parse_json(value) + + capture( + xhiveframework.scrub(f"{step.parent}_{step.title}"), + app="xhiveframework_ui_tours", + properties={"is_completed": tour.is_completed}, + ) + xhiveframework.db.set_value("User", xhiveframework.session.user, "onboarding_status", value, update_modified=False) + + xhiveframework.cache.hdel("bootinfo", xhiveframework.session.user) + + +def get_onboarding_ui_tours(): + if not xhiveframework.get_system_settings("enable_onboarding"): + return [] + + ui_tours = xhiveframework.get_all("Form Tour", filters={"ui_tour": 1}, fields=["page_route", "name"]) + + return [[tour.name, json.loads(tour.page_route)] for tour in ui_tours] diff --git a/xhiveframework/desk/doctype/form_tour/patches/__init__.py b/xhiveframework/desk/doctype/form_tour/patches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/form_tour/patches/introduce_ui_tours.py b/xhiveframework/desk/doctype/form_tour/patches/introduce_ui_tours.py new file mode 100644 index 0000000..4ba07b6 --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour/patches/introduce_ui_tours.py @@ -0,0 +1,13 @@ +import json + +import xhiveframework + + +def execute(): + """Handle introduction of UI tours""" + completed = {} + for tour in xhiveframework.get_all("Form Tour", {"ui_tour": 1}, pluck="name"): + completed[tour] = {"is_complete": True} + + User = xhiveframework.qb.DocType("User") + xhiveframework.qb.update(User).set("onboarding_status", json.dumps(completed)).run() diff --git a/xhiveframework/desk/doctype/form_tour/test_form_tour.py b/xhiveframework/desk/doctype/form_tour/test_form_tour.py new file mode 100644 index 0000000..e594ef8 --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour/test_form_tour.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestFormTour(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/form_tour_step/__init__.py b/xhiveframework/desk/doctype/form_tour_step/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/form_tour_step/form_tour_step.json b/xhiveframework/desk/doctype/form_tour_step/form_tour_step.json new file mode 100644 index 0000000..26209cc --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour_step/form_tour_step.json @@ -0,0 +1,230 @@ +{ + "actions": [], + "creation": "2021-05-21 23:05:45.342114", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ui_tour", + "is_table_field", + "section_break_2", + "title", + "parent_fieldname", + "fieldname", + "element_selector", + "parent_element_selector", + "description", + "ondemand_description", + "column_break_2", + "position", + "hide_buttons", + "popover_element", + "modal_trigger", + "offset_x", + "offset_y", + "next_on_click", + "label", + "fieldtype", + "has_next_condition", + "next_step_condition", + "next_form_tour", + "section_break_13", + "child_doctype" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "columns": 4, + "fieldname": "description", + "fieldtype": "HTML Editor", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Description", + "reqd": 1 + }, + { + "depends_on": "eval: (!doc.ui_tour && (!doc.is_table_field || (doc.is_table_field && doc.parent_fieldname)))", + "fieldname": "fieldname", + "fieldtype": "Select", + "label": "Fieldname", + "mandatory_depends_on": "eval: (!doc.ui_tour)" + }, + { + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Bottom", + "fieldname": "position", + "fieldtype": "Select", + "label": "Position", + "options": "Left\nLeft Center\nLeft Bottom\nTop\nTop Center\nTop Right\nRight\nRight Center\nRight Bottom\nBottom\nBottom Center\nBottom Right\nMid Center" + }, + { + "depends_on": "has_next_condition", + "fieldname": "next_step_condition", + "fieldtype": "Code", + "label": "Next Step Condition", + "oldfieldname": "condition", + "options": "JS" + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "has_next_condition", + "fieldtype": "Check", + "label": "Has Next Condition" + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "fieldtype", + "fieldtype": "Data", + "label": "Fieldtype", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:(!doc.ui_tour)", + "fieldname": "is_table_field", + "fieldtype": "Check", + "label": "Is Table Field" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "section_break_13", + "fieldtype": "Section Break", + "hidden": 1, + "label": "Hidden Fields" + }, + { + "fieldname": "child_doctype", + "fieldtype": "Data", + "hidden": 1, + "label": "Child Doctype", + "read_only": 1 + }, + { + "depends_on": "eval: (!doc.ui_tour || doc.is_table_field)", + "fieldname": "parent_fieldname", + "fieldtype": "Select", + "label": "Parent Field", + "mandatory_depends_on": "is_table_field" + }, + { + "default": "0", + "fetch_from": "next_form_tour.ui_tour", + "fieldname": "ui_tour", + "fieldtype": "Check", + "in_list_view": 1, + "label": "UI Tour" + }, + { + "depends_on": "eval:(doc.ui_tour)", + "description": "CSS selector for the element you want to highlight.", + "fieldname": "element_selector", + "fieldtype": "Data", + "label": "Element Selector", + "mandatory_depends_on": "eval:(doc.ui_tour)" + }, + { + "depends_on": "eval:(doc.ui_tour)", + "description": "Mozilla doesn't support :has() so you can pass parent selector here as workaround", + "fieldname": "parent_element_selector", + "fieldtype": "Data", + "label": "Parent Element Selector" + }, + { + "depends_on": "eval:(doc.ui_tour)", + "fieldname": "next_form_tour", + "fieldtype": "Link", + "label": "Next Form Tour", + "options": "Form Tour" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "description": "Hide Previous, Next and Close button on highlight dialog.", + "fieldname": "hide_buttons", + "fieldtype": "Check", + "label": "Hide Buttons" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "description": "Move to next step when clicked inside highlighted area.", + "fieldname": "next_on_click", + "fieldtype": "Check", + "label": "Next on Click" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "description": "when clicked on element it will focus popover if present.", + "fieldname": "popover_element", + "fieldtype": "Check", + "label": "Popover Element" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "fieldname": "offset_x", + "fieldtype": "Int", + "label": "Offset X" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "fieldname": "offset_y", + "fieldtype": "Int", + "label": "Offset Y" + }, + { + "default": "0", + "depends_on": "eval:(doc.ui_tour)", + "description": "Enable if on click\nopens modal.", + "fieldname": "modal_trigger", + "fieldtype": "Check", + "label": "Modal Trigger" + }, + { + "columns": 4, + "depends_on": "eval: (doc.popover_element || doc.modal_trigger)", + "fieldname": "ondemand_description", + "fieldtype": "HTML Editor", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Popover or Modal Description" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-05-23 13:09:15.923043", + "modified_by": "Administrator", + "module": "Desk", + "name": "Form Tour Step", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/form_tour_step/form_tour_step.py b/xhiveframework/desk/doctype/form_tour_step/form_tour_step.py new file mode 100644 index 0000000..fc2768e --- /dev/null +++ b/xhiveframework/desk/doctype/form_tour_step/form_tour_step.py @@ -0,0 +1,57 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class FormTourStep(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + child_doctype: DF.Data | None + description: DF.HTMLEditor + element_selector: DF.Data | None + fieldname: DF.Literal[None] + fieldtype: DF.Data | None + has_next_condition: DF.Check + hide_buttons: DF.Check + is_table_field: DF.Check + label: DF.Data | None + modal_trigger: DF.Check + next_form_tour: DF.Link | None + next_on_click: DF.Check + next_step_condition: DF.Code | None + offset_x: DF.Int + offset_y: DF.Int + ondemand_description: DF.HTMLEditor | None + parent: DF.Data + parent_element_selector: DF.Data | None + parent_fieldname: DF.Literal[None] + parentfield: DF.Data + parenttype: DF.Data + popover_element: DF.Check + position: DF.Literal[ + "Left", + "Left Center", + "Left Bottom", + "Top", + "Top Center", + "Top Right", + "Right", + "Right Center", + "Right Bottom", + "Bottom", + "Bottom Center", + "Bottom Right", + "Mid Center", + ] + title: DF.Data + ui_tour: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/global_search_doctype/__init__.py b/xhiveframework/desk/doctype/global_search_doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.json b/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.json new file mode 100644 index 0000000..648e8f1 --- /dev/null +++ b/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.json @@ -0,0 +1,29 @@ +{ + "creation": "2019-09-13 21:33:55.551941", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "options": "DocType" + } + ], + "istable": 1, + "modified": "2019-09-18 17:59:44.354052", + "modified_by": "Administrator", + "module": "Desk", + "name": "Global Search DocType", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.py b/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.py new file mode 100644 index 0000000..4591f27 --- /dev/null +++ b/xhiveframework/desk/doctype/global_search_doctype/global_search_doctype.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class GlobalSearchDocType(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document_type: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/global_search_settings/__init__.py b/xhiveframework/desk/doctype/global_search_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/global_search_settings/global_search_settings.js b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.js new file mode 100644 index 0000000..789f32f --- /dev/null +++ b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.js @@ -0,0 +1,32 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Global Search Settings", { + refresh: function (frm) { + xhiveframework.realtime.on("global_search_settings", (data) => { + if (data.progress) { + frm.dashboard.show_progress( + "Setting up Global Search", + (data.progress / data.total) * 100, + data.msg + ); + if (data.progress === data.total) { + frm.dashboard.hide_progress("Setting up Global Search"); + } + } + }); + + frm.add_custom_button(__("Reset"), function () { + xhiveframework.call({ + method: "xhiveframework.desk.doctype.global_search_settings.global_search_settings.reset_global_search_settings_doctypes", + callback: function () { + xhiveframework.show_alert({ + message: __("Global Search Document Types Reset."), + indicator: "green", + }); + frm.refresh(); + }, + }); + }); + }, +}); diff --git a/xhiveframework/desk/doctype/global_search_settings/global_search_settings.json b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.json new file mode 100644 index 0000000..6fa25f7 --- /dev/null +++ b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.json @@ -0,0 +1,39 @@ +{ + "creation": "2019-09-03 16:08:21.333698", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "allowed_in_global_search" + ], + "fields": [ + { + "fieldname": "allowed_in_global_search", + "fieldtype": "Table", + "label": "Search Priorities", + "options": "Global Search DocType" + } + ], + "issingle": 1, + "modified": "2019-10-10 22:05:02.692689", + "modified_by": "Administrator", + "module": "Desk", + "name": "Global Search Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/global_search_settings/global_search_settings.py b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.py new file mode 100644 index 0000000..0f6939f --- /dev/null +++ b/xhiveframework/desk/doctype/global_search_settings/global_search_settings.py @@ -0,0 +1,103 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class GlobalSearchSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.global_search_doctype.global_search_doctype import GlobalSearchDocType + from xhiveframework.types import DF + + allowed_in_global_search: DF.Table[GlobalSearchDocType] + + # end: auto-generated types + def validate(self): + dts, core_dts, repeated_dts = [], [], [] + + for dt in self.allowed_in_global_search: + if dt.document_type in dts: + repeated_dts.append(dt.document_type) + + if xhiveframework.get_meta(dt.document_type).module == "Core": + core_dts.append(dt.document_type) + + dts.append(dt.document_type) + + if core_dts: + core_dts = ", ".join(xhiveframework.bold(dt) for dt in core_dts) + xhiveframework.throw(_("Core Modules {0} cannot be searched in Global Search.").format(core_dts)) + + if repeated_dts: + repeated_dts = ", ".join([xhiveframework.bold(dt) for dt in repeated_dts]) + xhiveframework.throw(_("Document Type {0} has been repeated.").format(repeated_dts)) + + # reset cache + xhiveframework.cache.hdel("global_search", "search_priorities") + + +def get_doctypes_for_global_search(): + def get_from_db(): + doctypes = xhiveframework.get_all("Global Search DocType", fields=["document_type"], order_by="idx ASC") + return [d.document_type for d in doctypes] or [] + + return xhiveframework.cache.hget("global_search", "search_priorities", get_from_db) + + +@xhiveframework.whitelist() +def reset_global_search_settings_doctypes(): + update_global_search_doctypes() + + +def update_global_search_doctypes(): + global_search_doctypes = [] + show_message(1, _("Fetching default Global Search documents.")) + + installed_apps = [app for app in xhiveframework.get_installed_apps() if app] + active_domains = [domain for domain in xhiveframework.get_active_domains() if domain] + active_domains.append("Default") + + for app in installed_apps: + search_doctypes = xhiveframework.get_hooks(hook="global_search_doctypes", app_name=app) + if not search_doctypes: + continue + + for domain in active_domains: + if search_doctypes.get(domain): + global_search_doctypes.extend(search_doctypes.get(domain)) + + doctype_list = {dt.name for dt in xhiveframework.get_all("DocType")} + allowed_in_global_search = [] + + for dt in global_search_doctypes: + if dt.get("index") is not None: + allowed_in_global_search.insert(dt.get("index"), dt.get("doctype")) + continue + + allowed_in_global_search.append(dt.get("doctype")) + + show_message(2, _("Setting up Global Search documents.")) + global_search_settings = xhiveframework.get_single("Global Search Settings") + global_search_settings.allowed_in_global_search = [] + for dt in allowed_in_global_search: + if dt not in doctype_list: + continue + + global_search_settings.append("allowed_in_global_search", {"document_type": dt}) + global_search_settings.save(ignore_permissions=True) + show_message(3, "Global Search Documents have been reset.") + + +def show_message(progress, msg): + xhiveframework.publish_realtime( + "global_search_settings", + {"progress": progress, "total": 3, "msg": msg}, + user=xhiveframework.session.user, + ) diff --git a/xhiveframework/desk/doctype/kanban_board/__init__.py b/xhiveframework/desk/doctype/kanban_board/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/kanban_board/kanban_board.js b/xhiveframework/desk/doctype/kanban_board/kanban_board.js new file mode 100644 index 0000000..1dc75f0 --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board/kanban_board.js @@ -0,0 +1,45 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Kanban Board", { + onload: function (frm) { + frm.trigger("reference_doctype"); + }, + refresh: function (frm) { + if (frm.is_new()) return; + frm.add_custom_button("Show Board", function () { + xhiveframework.set_route("List", frm.doc.reference_doctype, "Kanban", frm.doc.name); + }); + }, + reference_doctype: function (frm) { + // set field options + if (!frm.doc.reference_doctype) return; + + xhiveframework.model.with_doctype(frm.doc.reference_doctype, function () { + var options = $.map(xhiveframework.get_meta(frm.doc.reference_doctype).fields, function (d) { + if ( + d.fieldname && + d.fieldtype === "Select" && + xhiveframework.model.no_value_type.indexOf(d.fieldtype) === -1 + ) { + return d.fieldname; + } + return null; + }); + frm.set_df_property("field_name", "options", options); + frm.get_field("field_name").refresh(); + }); + }, + field_name: function (frm) { + var field = xhiveframework.meta.get_field(frm.doc.reference_doctype, frm.doc.field_name); + frm.doc.columns = []; + field.options && + field.options.split("\n").forEach(function (o) { + o = o.trim(); + if (!o) return; + var d = frm.add_child("columns"); + d.column_name = o; + }); + frm.refresh(); + }, +}); diff --git a/xhiveframework/desk/doctype/kanban_board/kanban_board.json b/xhiveframework/desk/doctype/kanban_board/kanban_board.json new file mode 100644 index 0000000..296aa01 --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board/kanban_board.json @@ -0,0 +1,124 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:kanban_board_name", + "creation": "2016-10-19 12:26:04.809812", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "kanban_board_name", + "reference_doctype", + "field_name", + "column_break_4", + "private", + "show_labels", + "section_break_3", + "columns", + "filters", + "fields" + ], + "fields": [ + { + "fieldname": "kanban_board_name", + "fieldtype": "Data", + "label": "Kanban Board Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "field_name", + "fieldtype": "Select", + "label": "Field Name", + "reqd": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "columns", + "fieldtype": "Table", + "label": "Columns", + "options": "Kanban Board Column" + }, + { + "fieldname": "filters", + "fieldtype": "Code", + "label": "Filters", + "options": "JSON", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "private", + "fieldtype": "Check", + "label": "Private", + "read_only": 1 + }, + { + "fieldname": "fields", + "fieldtype": "Code", + "label": "Fields", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "show_labels", + "fieldtype": "Check", + "label": "Show Labels", + "read_only": 1 + } + ], + "links": [], + "modified": "2023-08-28 22:29:29.569670", + "modified_by": "Administrator", + "module": "Desk", + "name": "Kanban Board", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "read": 1, + "role": "Desk User" + }, + { + "create": 1, + "delete": 1, + "if_owner": 1, + "read": 1, + "role": "Desk User", + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/kanban_board/kanban_board.py b/xhiveframework/desk/doctype/kanban_board/kanban_board.py new file mode 100644 index 0000000..df38f22 --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board/kanban_board.py @@ -0,0 +1,290 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class KanbanBoard(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.kanban_board_column.kanban_board_column import KanbanBoardColumn + from xhiveframework.types import DF + + columns: DF.Table[KanbanBoardColumn] + field_name: DF.Literal[None] + fields: DF.Code | None + filters: DF.Code | None + kanban_board_name: DF.Data + private: DF.Check + reference_doctype: DF.Link + show_labels: DF.Check + + # end: auto-generated types + def validate(self): + self.validate_column_name() + + def on_change(self): + xhiveframework.clear_cache(doctype=self.reference_doctype) + xhiveframework.cache.delete_keys("_user_settings") + + def before_insert(self): + for column in self.columns: + column.order = get_order_for_column(self, column.column_name) + + def validate_column_name(self): + for column in self.columns: + if not column.column_name: + xhiveframework.msgprint(_("Column Name cannot be empty"), raise_exception=True) + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + return "" + + return f"""(`tabKanban Board`.private=0 or `tabKanban Board`.owner={xhiveframework.db.escape(user)})""" + + +def has_permission(doc, ptype, user): + if doc.private == 0 or user == "Administrator": + return True + + if user == doc.owner: + return True + + return False + + +@xhiveframework.whitelist() +def get_kanban_boards(doctype): + """Get Kanban Boards for doctype to show in List View""" + return xhiveframework.get_list( + "Kanban Board", + fields=["name", "filters", "reference_doctype", "private"], + filters={"reference_doctype": doctype}, + ) + + +@xhiveframework.whitelist() +def add_column(board_name, column_title): + """Adds new column to Kanban Board""" + doc = xhiveframework.get_doc("Kanban Board", board_name) + for col in doc.columns: + if column_title == col.column_name: + xhiveframework.throw(_("Column {0} already exist.").format(column_title)) + + doc.append("columns", dict(column_name=column_title)) + doc.save() + return doc.columns + + +@xhiveframework.whitelist() +def archive_restore_column(board_name, column_title, status): + """Set column's status to status""" + doc = xhiveframework.get_doc("Kanban Board", board_name) + for col in doc.columns: + if column_title == col.column_name: + col.status = status + + doc.save() + return doc.columns + + +@xhiveframework.whitelist() +def update_order(board_name, order): + """Save the order of cards in columns""" + board = xhiveframework.get_doc("Kanban Board", board_name) + doctype = board.reference_doctype + updated_cards = [] + + if not xhiveframework.has_permission(doctype, "write"): + # Return board data from db + return board, updated_cards + + fieldname = board.field_name + order_dict = json.loads(order) + + for col_name, cards in order_dict.items(): + for card in cards: + column = xhiveframework.get_value(doctype, {"name": card}, fieldname) + if column != col_name: + xhiveframework.set_value(doctype, card, fieldname, col_name) + updated_cards.append(dict(name=card, column=col_name)) + + for column in board.columns: + if column.column_name == col_name: + column.order = json.dumps(cards) + + return board.save(ignore_permissions=True), updated_cards + + +@xhiveframework.whitelist() +def update_order_for_single_card(board_name, docname, from_colname, to_colname, old_index, new_index): + """Save the order of cards in columns""" + board = xhiveframework.get_doc("Kanban Board", board_name) + doctype = board.reference_doctype + + xhiveframework.has_permission(doctype, "write", throw=True) + + fieldname = board.field_name + old_index = xhiveframework.parse_json(old_index) + new_index = xhiveframework.parse_json(new_index) + + # save current order and index of columns to be updated + from_col_order, from_col_idx = get_kanban_column_order_and_index(board, from_colname) + to_col_order, to_col_idx = get_kanban_column_order_and_index(board, to_colname) + + if from_colname == to_colname: + from_col_order = to_col_order + + to_col_order.insert(new_index, from_col_order.pop(old_index)) + + # save updated order + board.columns[from_col_idx].order = xhiveframework.as_json(from_col_order) + board.columns[to_col_idx].order = xhiveframework.as_json(to_col_order) + board.save(ignore_permissions=True) + + # update changed value in doc + xhiveframework.set_value(doctype, docname, fieldname, to_colname) + + return board + + +def get_kanban_column_order_and_index(board, colname): + for i, col in enumerate(board.columns): + if col.column_name == colname: + col_order = xhiveframework.parse_json(col.order) + col_idx = i + + return col_order, col_idx + + +@xhiveframework.whitelist() +def add_card(board_name, docname, colname): + board = xhiveframework.get_doc("Kanban Board", board_name) + + xhiveframework.has_permission(board.reference_doctype, "write", throw=True) + + col_order, col_idx = get_kanban_column_order_and_index(board, colname) + col_order.insert(0, docname) + + board.columns[col_idx].order = xhiveframework.as_json(col_order) + + return board.save(ignore_permissions=True) + + +@xhiveframework.whitelist() +def quick_kanban_board(doctype, board_name, field_name, project=None): + """Create new KanbanBoard quickly with default options""" + + doc = xhiveframework.new_doc("Kanban Board") + meta = xhiveframework.get_meta(doctype) + + doc.kanban_board_name = board_name + doc.reference_doctype = doctype + doc.field_name = field_name + + if project: + doc.filters = f'[["Task","project","=","{project}"]]' + + options = "" + for field in meta.fields: + if field.fieldname == field_name: + options = field.options + + columns = [] + if options: + columns = options.split("\n") + + for column in columns: + if not column: + continue + doc.append("columns", dict(column_name=column)) + + if doctype in ["Note", "ToDo"]: + doc.private = 1 + + doc.save() + return doc + + +def get_order_for_column(board, colname): + filters = [[board.reference_doctype, board.field_name, "=", colname]] + if board.filters: + filters.append(xhiveframework.parse_json(board.filters)[0]) + + return xhiveframework.as_json(xhiveframework.get_list(board.reference_doctype, filters=filters, pluck="name")) + + +@xhiveframework.whitelist() +def update_column_order(board_name, order): + """Set the order of columns in Kanban Board""" + board = xhiveframework.get_doc("Kanban Board", board_name) + order = json.loads(order) + old_columns = board.columns + new_columns = [] + + for col in order: + for column in list(old_columns): + if col == column.column_name: + new_columns.append(column) + old_columns.remove(column) + + new_columns.extend(old_columns) + + board.columns = [] + for col in new_columns: + board.append( + "columns", + dict( + column_name=col.column_name, + status=col.status, + order=col.order, + indicator=col.indicator, + ), + ) + + board.save() + return board + + +@xhiveframework.whitelist() +def set_indicator(board_name, column_name, indicator): + """Set the indicator color of column""" + board = xhiveframework.get_doc("Kanban Board", board_name) + + for column in board.columns: + if column.column_name == column_name: + column.indicator = indicator + + board.save() + return board + + +@xhiveframework.whitelist() +def save_settings(board_name: str, settings: str) -> Document: + settings = json.loads(settings) + doc = xhiveframework.get_doc("Kanban Board", board_name) + + fields = settings["fields"] + if not isinstance(fields, str): + fields = json.dumps(fields) + + doc.fields = fields + doc.show_labels = settings["show_labels"] + doc.save() + + resp = doc.as_dict() + resp["fields"] = xhiveframework.parse_json(resp["fields"]) + + return resp diff --git a/xhiveframework/desk/doctype/kanban_board/test_kanban_board.py b/xhiveframework/desk/doctype/kanban_board/test_kanban_board.py new file mode 100644 index 0000000..bfa974d --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board/test_kanban_board.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Kanban Board') + + +class TestKanbanBoard(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/kanban_board_column/__init__.py b/xhiveframework/desk/doctype/kanban_board_column/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.json b/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.json new file mode 100644 index 0000000..c0acde5 --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2016-10-19 12:26:42.569185", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "column_name", + "status", + "indicator", + "order" + ], + "fields": [ + { + "fieldname": "column_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Column Name" + }, + { + "default": "Active", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Active\nArchived" + }, + { + "default": "Gray", + "fieldname": "indicator", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Indicator", + "options": "Blue\nCyan\nGray\nGreen\nLight Blue\nOrange\nPink\nPurple\nRed\nRed\nYellow" + }, + { + "fieldname": "order", + "fieldtype": "Code", + "label": "Order" + } + ], + "istable": 1, + "links": [], + "modified": "2021-12-14 13:13:38.804259", + "modified_by": "Administrator", + "module": "Desk", + "name": "Kanban Board Column", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.py b/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.py new file mode 100644 index 0000000..0492ce9 --- /dev/null +++ b/xhiveframework/desk/doctype/kanban_board_column/kanban_board_column.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class KanbanBoardColumn(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + column_name: DF.Data | None + indicator: DF.Literal[ + "Blue", + "Cyan", + "Gray", + "Green", + "Light Blue", + "Orange", + "Pink", + "Purple", + "Red", + "Red", + "Yellow", + ] + order: DF.Code | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + status: DF.Literal["Active", "Archived"] + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/list_filter/__init__.py b/xhiveframework/desk/doctype/list_filter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/list_filter/list_filter.json b/xhiveframework/desk/doctype/list_filter/list_filter.json new file mode 100644 index 0000000..c897aef --- /dev/null +++ b/xhiveframework/desk/doctype/list_filter/list_filter.json @@ -0,0 +1,62 @@ +{ + "actions": [], + "creation": "2018-02-22 15:10:24.401801", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "filter_name", + "reference_doctype", + "for_user", + "filters" + ], + "fields": [ + { + "fieldname": "filter_name", + "fieldtype": "Data", + "label": "Filter Name" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "for_user", + "fieldtype": "Link", + "label": "For User", + "options": "User" + }, + { + "fieldname": "filters", + "fieldtype": "Long Text", + "label": "Filters" + } + ], + "in_create": 1, + "links": [], + "modified": "2023-08-28 22:32:51.465521", + "modified_by": "Administrator", + "module": "Desk", + "name": "List Filter", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/list_filter/list_filter.py b/xhiveframework/desk/doctype/list_filter/list_filter.py new file mode 100644 index 0000000..f3e89db --- /dev/null +++ b/xhiveframework/desk/doctype/list_filter/list_filter.py @@ -0,0 +1,21 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class ListFilter(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + filter_name: DF.Data | None + filters: DF.LongText | None + for_user: DF.Link | None + reference_doctype: DF.Link | None + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/list_view_settings/__init__.py b/xhiveframework/desk/doctype/list_view_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/list_view_settings/list_view_settings.js b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.js new file mode 100644 index 0000000..377e937 --- /dev/null +++ b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("List View Settings", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/desk/doctype/list_view_settings/list_view_settings.json b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.json new file mode 100644 index 0000000..69ea379 --- /dev/null +++ b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.json @@ -0,0 +1,85 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2019-10-23 15:00:48.392374", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "disable_count", + "disable_comment_count", + "disable_sidebar_stats", + "disable_auto_refresh", + "total_fields", + "fields_html", + "fields" + ], + "fields": [ + { + "default": "0", + "fieldname": "disable_count", + "fieldtype": "Check", + "label": "Disable Count" + }, + { + "default": "0", + "fieldname": "disable_sidebar_stats", + "fieldtype": "Check", + "label": "Disable Sidebar Stats" + }, + { + "default": "0", + "fieldname": "disable_auto_refresh", + "fieldtype": "Check", + "label": "Disable Auto Refresh" + }, + { + "fieldname": "total_fields", + "fieldtype": "Select", + "label": "Maximum Number of Fields", + "options": "\n4\n5\n6\n7\n8\n9\n10" + }, + { + "fieldname": "fields_html", + "fieldtype": "HTML", + "label": "Fields" + }, + { + "fieldname": "fields", + "fieldtype": "Code", + "hidden": 1, + "label": "Fields", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "disable_comment_count", + "fieldtype": "Check", + "label": "Disable Comment Count" + } + ], + "links": [], + "modified": "2023-02-14 14:46:43.764229", + "modified_by": "Administrator", + "module": "Desk", + "name": "List View Settings", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/list_view_settings/list_view_settings.py b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.py new file mode 100644 index 0000000..02eab60 --- /dev/null +++ b/xhiveframework/desk/doctype/list_view_settings/list_view_settings.py @@ -0,0 +1,99 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class ListViewSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + disable_auto_refresh: DF.Check + disable_comment_count: DF.Check + disable_count: DF.Check + disable_sidebar_stats: DF.Check + fields: DF.Code | None + total_fields: DF.Literal["", "4", "5", "6", "7", "8", "9", "10"] + # end: auto-generated types + pass + + +@xhiveframework.whitelist() +def save_listview_settings(doctype, listview_settings, removed_listview_fields): + listview_settings = xhiveframework.parse_json(listview_settings) + removed_listview_fields = xhiveframework.parse_json(removed_listview_fields) + + if xhiveframework.get_all("List View Settings", filters={"name": doctype}): + doc = xhiveframework.get_doc("List View Settings", doctype) + doc.update(listview_settings) + doc.save() + else: + doc = xhiveframework.new_doc("List View Settings") + doc.name = doctype + doc.update(listview_settings) + doc.insert() + + set_listview_fields(doctype, listview_settings.get("fields"), removed_listview_fields) + + return {"meta": xhiveframework.get_meta(doctype, False), "listview_settings": doc} + + +def set_listview_fields(doctype, listview_fields, removed_listview_fields): + meta = xhiveframework.get_meta(doctype) + + listview_fields = [f.get("fieldname") for f in xhiveframework.parse_json(listview_fields) if f.get("fieldname")] + + for field in removed_listview_fields: + set_in_list_view_property(doctype, meta.get_field(field), "0") + + for field in listview_fields: + set_in_list_view_property(doctype, meta.get_field(field), "1") + + +def set_in_list_view_property(doctype, field, value): + if not field or field.fieldname == "status_field": + return + + property_setter = xhiveframework.db.get_value( + "Property Setter", + {"doc_type": doctype, "field_name": field.fieldname, "property": "in_list_view"}, + ) + if property_setter: + doc = xhiveframework.get_doc("Property Setter", property_setter) + doc.value = value + doc.save() + else: + xhiveframework.make_property_setter( + { + "doctype": doctype, + "doctype_or_field": "DocField", + "fieldname": field.fieldname, + "property": "in_list_view", + "value": value, + "property_type": "Check", + }, + ignore_validate=True, + ) + + +@xhiveframework.whitelist() +def get_default_listview_fields(doctype): + meta = xhiveframework.get_meta(doctype) + path = xhiveframework.get_module_path( + xhiveframework.scrub(meta.module), "doctype", xhiveframework.scrub(meta.name), xhiveframework.scrub(meta.name) + ".json" + ) + doctype_json = xhiveframework.get_file_json(path) + + fields = [f.get("fieldname") for f in doctype_json.get("fields") if f.get("in_list_view")] + + if meta.title_field: + if meta.title_field.strip() not in fields: + fields.append(meta.title_field.strip()) + + return fields diff --git a/xhiveframework/desk/doctype/list_view_settings/test_list_view_settings.py b/xhiveframework/desk/doctype/list_view_settings/test_list_view_settings.py new file mode 100644 index 0000000..456d4ec --- /dev/null +++ b/xhiveframework/desk/doctype/list_view_settings/test_list_view_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestListViewSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/module_onboarding/__init__.py b/xhiveframework/desk/doctype/module_onboarding/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/module_onboarding/module_onboarding.js b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.js new file mode 100644 index 0000000..ac807b7 --- /dev/null +++ b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.js @@ -0,0 +1,31 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Module Onboarding", { + refresh: function (frm) { + xhiveframework.boot.developer_mode && + frm.set_intro( + __( + "Saving this will export this document as well as the steps linked here as json." + ), + true + ); + if (!xhiveframework.boot.developer_mode) { + frm.trigger("disable_form"); + } + + frm.add_custom_button(__("Reset"), () => { + frm.call("reset_progress"); + }); + }, + + disable_form: function (frm) { + frm.set_read_only(); + frm.fields + .filter((field) => field.has_input) + .forEach((field) => { + frm.set_df_property(field.df.fieldname, "read_only", "1"); + }); + frm.disable_save(); + }, +}); diff --git a/xhiveframework/desk/doctype/module_onboarding/module_onboarding.json b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.json new file mode 100644 index 0000000..1eca835 --- /dev/null +++ b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.json @@ -0,0 +1,118 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2020-04-24 13:58:14.948024", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "subtitle", + "module", + "allow_roles", + "column_break_4", + "success_message", + "documentation_url", + "is_complete", + "section_break_6", + "steps" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "subtitle", + "fieldtype": "Data", + "label": "Subtitle", + "reqd": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "success_message", + "fieldtype": "Data", + "label": "Success Message", + "reqd": 1 + }, + { + "fieldname": "documentation_url", + "fieldtype": "Data", + "label": "Documentation URL", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "is_complete", + "fieldtype": "Check", + "label": "Is Complete", + "read_only": 1 + }, + { + "fieldname": "steps", + "fieldtype": "Table", + "label": "Steps", + "options": "Onboarding Step Map", + "reqd": 1 + }, + { + "description": "System managers are allowed by default", + "fieldname": "allow_roles", + "fieldtype": "Table MultiSelect", + "label": "Allow Roles", + "options": "Onboarding Permission", + "reqd": 1 + } + ], + "links": [], + "modified": "2023-08-28 22:24:02.233272", + "modified_by": "Administrator", + "module": "Desk", + "name": "Module Onboarding", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/module_onboarding/module_onboarding.py b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.py new file mode 100644 index 0000000..bb61cc1 --- /dev/null +++ b/xhiveframework/desk/doctype/module_onboarding/module_onboarding.py @@ -0,0 +1,84 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.modules.export_file import export_to_files + + +class ModuleOnboarding(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.onboarding_permission.onboarding_permission import OnboardingPermission + from xhiveframework.desk.doctype.onboarding_step_map.onboarding_step_map import OnboardingStepMap + from xhiveframework.types import DF + + allow_roles: DF.TableMultiSelect[OnboardingPermission] + documentation_url: DF.Data + is_complete: DF.Check + module: DF.Link + steps: DF.Table[OnboardingStepMap] + subtitle: DF.Data + success_message: DF.Data + title: DF.Data + + # end: auto-generated types + def on_update(self): + if xhiveframework.conf.developer_mode: + export_to_files(record_list=[["Module Onboarding", self.name]], record_module=self.module) + + for step in self.steps: + export_to_files(record_list=[["Onboarding Step", step.step]], record_module=self.module) + + def get_steps(self): + return [xhiveframework.get_doc("Onboarding Step", step.step) for step in self.steps] + + def get_allowed_roles(self): + all_roles = [role.role for role in self.allow_roles] + if "System Manager" not in all_roles: + all_roles.append("System Manager") + + return all_roles + + def check_completion(self): + if self.is_complete: + return True + + steps = self.get_steps() + is_complete = [bool(step.is_complete or step.is_skipped) for step in steps] + if all(is_complete): + self.is_complete = True + self.save(ignore_permissions=True) + return True + + return False + + @xhiveframework.whitelist() + def reset_progress(self): + self.db_set("is_complete", 0) + + for step in self.get_steps(): + step.db_set("is_complete", 0) + step.db_set("is_skipped", 0) + + xhiveframework.msgprint(_("Module onboarding progress reset"), alert=True) + + def before_export(self, doc): + doc.is_complete = 0 + + def reset_onboarding(self): + xhiveframework.only_for("Administrator") + + self.is_complete = 0 + steps = self.get_steps() + for step in steps: + step.is_complete = 0 + step.is_skipped = 0 + step.save() + + self.save() diff --git a/xhiveframework/desk/doctype/module_onboarding/test_module_onboarding.py b/xhiveframework/desk/doctype/module_onboarding/test_module_onboarding.py new file mode 100644 index 0000000..484e596 --- /dev/null +++ b/xhiveframework/desk/doctype/module_onboarding/test_module_onboarding.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestModuleOnboarding(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/note/README.md b/xhiveframework/desk/doctype/note/README.md new file mode 100644 index 0000000..95d7b33 --- /dev/null +++ b/xhiveframework/desk/doctype/note/README.md @@ -0,0 +1 @@ +Shared Note. (Page with standard information, links, attachments). \ No newline at end of file diff --git a/xhiveframework/desk/doctype/note/__init__.py b/xhiveframework/desk/doctype/note/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/note/note.js b/xhiveframework/desk/doctype/note/note.js new file mode 100644 index 0000000..a431df1 --- /dev/null +++ b/xhiveframework/desk/doctype/note/note.js @@ -0,0 +1,54 @@ +xhiveframework.ui.form.on("Note", { + refresh: function (frm) { + if (!frm.is_new()) { + frm.is_note_editable = false; + frm.events.set_editable(frm); + } + }, + set_editable: function (frm) { + if (frm.has_perm("write")) { + const read_label = __("Read mode"); + const edit_label = __("Edit mode"); + frm.remove_custom_button(frm.is_note_editable ? edit_label : read_label); + frm.add_custom_button(frm.is_note_editable ? read_label : edit_label, function () { + frm.is_note_editable = !frm.is_note_editable; + frm.events.set_editable(frm); + }); + } + // toggle "read_only" for content and "hidden" of all other fields + + // content read_only + frm.set_df_property("content", "read_only", frm.is_note_editable ? 0 : 1); + + // hide all other fields + for (const field of frm.meta.fields) { + if (field.fieldname !== "content") { + frm.set_df_property( + field.fieldname, + "hidden", + frm.is_note_editable && !field.hidden && frm.get_perm(field.permlevel, "write") + ? 0 + : 1 + ); + } + } + + // no label, description for content either + frm.get_field("content").toggle_label(frm.is_note_editable); + frm.get_field("content").toggle_description(frm.is_note_editable); + }, +}); + +xhiveframework.tour["Note"] = [ + { + fieldname: "title", + title: "Title of the Note", + description: "This is the name by which the note will be saved, you can change this later", + }, + { + fieldname: "public", + title: "Sets the Note to Public", + description: + "You can change the visibility of the note with this, setting it to public will allow other users to view it.", + }, +]; diff --git a/xhiveframework/desk/doctype/note/note.json b/xhiveframework/desk/doctype/note/note.json new file mode 100644 index 0000000..4d60393 --- /dev/null +++ b/xhiveframework/desk/doctype/note/note.json @@ -0,0 +1,148 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2013-05-24 13:41:00", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "title", + "public", + "notify_on_login", + "notify_on_every_login", + "expire_notification_on", + "content", + "seen_by_section", + "seen_by" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "bold": 1, + "default": "0", + "fieldname": "public", + "fieldtype": "Check", + "label": "Public", + "permlevel": 1, + "print_hide": 1 + }, + { + "bold": 1, + "default": "0", + "depends_on": "public", + "fieldname": "notify_on_login", + "fieldtype": "Check", + "label": "Notify users with a popup when they log in", + "permlevel": 1 + }, + { + "bold": 1, + "default": "0", + "depends_on": "notify_on_login", + "description": "If enabled, users will be notified every time they login. If not enabled, users will only be notified once.", + "fieldname": "notify_on_every_login", + "fieldtype": "Check", + "label": "Notify Users On Every Login", + "permlevel": 1 + }, + { + "depends_on": "eval:doc.notify_on_login && doc.public", + "fieldname": "expire_notification_on", + "fieldtype": "Date", + "label": "Expire Notification On", + "permlevel": 1, + "search_index": 1 + }, + { + "bold": 1, + "description": "Help: To link to another record in the system, use \"/app/note/[Note Name]\" as the Link URL. (don't use \"http://\")", + "fieldname": "content", + "fieldtype": "Text Editor", + "in_global_search": 1, + "label": "Content" + }, + { + "collapsible": 1, + "depends_on": "notify_on_login", + "fieldname": "seen_by_section", + "fieldtype": "Section Break", + "label": "Seen By", + "permlevel": 1 + }, + { + "fieldname": "seen_by", + "fieldtype": "Table", + "label": "Seen By Table", + "options": "Note Seen By", + "permlevel": 1 + } + ], + "icon": "fa fa-file-text", + "idx": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Desk", + "name": "Note", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "permlevel": 2, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "if_owner": 1, + "role": "Desk User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Desk User" + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "title", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/note/note.py b/xhiveframework/desk/doctype/note/note.py new file mode 100644 index 0000000..b04ccc9 --- /dev/null +++ b/xhiveframework/desk/doctype/note/note.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class Note(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.note_seen_by.note_seen_by import NoteSeenBy + from xhiveframework.types import DF + + content: DF.TextEditor | None + expire_notification_on: DF.Date | None + notify_on_every_login: DF.Check + notify_on_login: DF.Check + public: DF.Check + seen_by: DF.Table[NoteSeenBy] + title: DF.Data + + # end: auto-generated types + def validate(self): + if self.notify_on_login and not self.expire_notification_on: + # expire this notification in a week (default) + self.expire_notification_on = xhiveframework.utils.add_days(self.creation, 7) + + if not self.public and self.notify_on_login: + self.notify_on_login = 0 + + if not self.content: + self.content = "" + + def before_print(self, settings=None): + self.print_heading = self.name + self.sub_heading = "" + + def mark_seen_by(self, user: str) -> None: + if user in [d.user for d in self.seen_by]: + return + + self.append("seen_by", {"user": user}) + + +@xhiveframework.whitelist() +def mark_as_seen(note: str): + note: Note = xhiveframework.get_doc("Note", note) + note.mark_seen_by(xhiveframework.session.user) + note.save(ignore_permissions=True, ignore_version=True) + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + return f"(`tabNote`.owner = {xhiveframework.db.escape(user)} or `tabNote`.public = 1)" + + +def has_permission(doc, user): + return doc.public or doc.owner == user diff --git a/xhiveframework/desk/doctype/note/note_list.js b/xhiveframework/desk/doctype/note/note_list.js new file mode 100644 index 0000000..15b7171 --- /dev/null +++ b/xhiveframework/desk/doctype/note/note_list.js @@ -0,0 +1,11 @@ +xhiveframework.listview_settings["Note"] = { + hide_name_column: true, + add_fields: ["public"], + get_indicator: function (doc) { + if (doc.public) { + return [__("Public"), "green", "public,=,Yes"]; + } else { + return [__("Private"), "gray", "public,=,No"]; + } + }, +}; diff --git a/xhiveframework/desk/doctype/note/test_note.py b/xhiveframework/desk/doctype/note/test_note.py new file mode 100644 index 0000000..c4cd83b --- /dev/null +++ b/xhiveframework/desk/doctype/note/test_note.py @@ -0,0 +1,75 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Note") + + +class TestNote(XhiveFrameworkTestCase): + def insert_note(self): + xhiveframework.db.delete("Version") + xhiveframework.db.delete("Note") + xhiveframework.db.delete("Note Seen By") + + return xhiveframework.get_doc(dict(doctype="Note", title="test note", content="test note content")).insert() + + def test_version(self): + note = self.insert_note() + note.title = "test note 1" + note.content = "1" + note.save(ignore_version=False) + + version = xhiveframework.get_doc("Version", dict(docname=note.name)) + data = version.get_data() + + self.assertTrue(("title", "test note", "test note 1"), data["changed"]) + self.assertTrue(("content", "test note content", "1"), data["changed"]) + + def test_rows(self): + note = self.insert_note() + + # test add + note.append("seen_by", {"user": "Administrator"}) + note.save(ignore_version=False) + + version = xhiveframework.get_doc("Version", dict(docname=note.name)) + data = version.get_data() + + self.assertEqual(len(data.get("added")), 1) + self.assertEqual(len(data.get("removed")), 0) + self.assertEqual(len(data.get("changed")), 0) + + for row in data.get("added"): + self.assertEqual(row[0], "seen_by") + self.assertEqual(row[1]["user"], "Administrator") + + # test row change + note.seen_by[0].user = "Guest" + note.save(ignore_version=False) + + version = xhiveframework.get_doc("Version", dict(docname=note.name)) + data = version.get_data() + + self.assertEqual(len(data.get("row_changed")), 1) + for row in data.get("row_changed"): + self.assertEqual(row[0], "seen_by") + self.assertEqual(row[1], 0) + self.assertEqual(row[2], note.seen_by[0].name) + self.assertEqual(row[3], [["user", "Administrator", "Guest"]]) + + # test remove + note.seen_by = [] + note.save(ignore_version=False) + + version = xhiveframework.get_doc("Version", dict(docname=note.name)) + data = version.get_data() + + self.assertEqual(len(data.get("removed")), 1) + for row in data.get("removed"): + self.assertEqual(row[0], "seen_by") + self.assertEqual(row[1]["user"], "Guest") + + # self.assertTrue(('title', 'test note', 'test note 1'), data['changed']) + # self.assertTrue(('content', 'test note content', '1'), data['changed']) diff --git a/xhiveframework/desk/doctype/note/test_records.json b/xhiveframework/desk/doctype/note/test_records.json new file mode 100644 index 0000000..f3d7cff --- /dev/null +++ b/xhiveframework/desk/doctype/note/test_records.json @@ -0,0 +1,7 @@ +[ + { + "doctype": "Note", + "name": "_Test Note 1", + "title": "Test Note Title" + } +] diff --git a/xhiveframework/desk/doctype/note_seen_by/__init__.py b/xhiveframework/desk/doctype/note_seen_by/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/note_seen_by/note_seen_by.json b/xhiveframework/desk/doctype/note_seen_by/note_seen_by.json new file mode 100644 index 0000000..905a043 --- /dev/null +++ b/xhiveframework/desk/doctype/note_seen_by/note_seen_by.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2016-08-29 05:29:16.726172", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "permlevel": 2 + } + ], + "istable": 1, + "links": [], + "modified": "2023-04-24 16:14:53.684098", + "modified_by": "Administrator", + "module": "Desk", + "name": "Note Seen By", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/note_seen_by/note_seen_by.py b/xhiveframework/desk/doctype/note_seen_by/note_seen_by.py new file mode 100644 index 0000000..96c0274 --- /dev/null +++ b/xhiveframework/desk/doctype/note_seen_by/note_seen_by.py @@ -0,0 +1,21 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class NoteSeenBy(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + user: DF.Link | None + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/notification_log/__init__.py b/xhiveframework/desk/doctype/notification_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/notification_log/notification_log.js b/xhiveframework/desk/doctype/notification_log/notification_log.js new file mode 100644 index 0000000..20678c4 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_log/notification_log.js @@ -0,0 +1,49 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Notification Log", { + refresh: function (frm) { + if (frm.doc.attached_file) { + frm.trigger("set_attachment"); + } else { + frm.get_field("attachment_link").$wrapper.empty(); + } + }, + + open_reference_document: function (frm) { + if (frm.doc?.link) { + xhiveframework.set_route(frm.doc.link); + return; + } + const dt = frm.doc.document_type; + const dn = frm.doc.document_name; + xhiveframework.set_route("Form", dt, dn); + }, + + set_attachment: function (frm) { + const attachment = JSON.parse(frm.doc.attached_file); + + const $wrapper = frm.get_field("attachment_link").$wrapper; + $wrapper.html(` + + `); + + $wrapper.find(".attached-file-link").click(() => { + const w = window.open( + xhiveframework.urllib.get_full_url(`/api/method/xhiveframework.utils.print_format.download_pdf? + doctype=${encodeURIComponent(attachment.doctype)} + &name=${encodeURIComponent(attachment.name)} + &format=${encodeURIComponent(attachment.print_format)} + &lang=${encodeURIComponent(attachment.lang)}`) + ); + if (!w) { + xhiveframework.msgprint(__("Please enable pop-ups")); + } + }); + }, +}); diff --git a/xhiveframework/desk/doctype/notification_log/notification_log.json b/xhiveframework/desk/doctype/notification_log/notification_log.json new file mode 100644 index 0000000..9fbe732 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_log/notification_log.json @@ -0,0 +1,127 @@ +{ + "actions": [], + "creation": "2019-08-26 13:37:34.165254", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "subject", + "for_user", + "type", + "email_content", + "document_type", + "read", + "document_name", + "attached_file", + "attachment_link", + "open_reference_document", + "from_user", + "link" + ], + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Subject" + }, + { + "fieldname": "for_user", + "fieldtype": "Link", + "hidden": 1, + "label": "For User", + "options": "User", + "search_index": 1 + }, + { + "fieldname": "type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Type", + "options": "Mention\nEnergy Point\nAssignment\nShare\nAlert" + }, + { + "fieldname": "email_content", + "fieldtype": "Text Editor", + "label": "Message" + }, + { + "fieldname": "document_type", + "fieldtype": "Link", + "hidden": 1, + "label": "Document Type", + "options": "DocType" + }, + { + "fieldname": "document_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Document Link", + "search_index": 1 + }, + { + "fieldname": "from_user", + "fieldtype": "Link", + "hidden": 1, + "label": "From User", + "options": "User" + }, + { + "default": "0", + "fieldname": "read", + "fieldtype": "Check", + "hidden": 1, + "ignore_user_permissions": 1, + "label": "Read" + }, + { + "fieldname": "open_reference_document", + "fieldtype": "Button", + "label": "Open Reference Document" + }, + { + "fieldname": "attached_file", + "fieldtype": "Code", + "hidden": 1, + "label": "Attached File", + "options": "JSON" + }, + { + "fieldname": "attachment_link", + "fieldtype": "HTML", + "label": "Attachment Link" + }, + { + "fieldname": "link", + "fieldtype": "Data", + "hidden": 1, + "label": "Link" + } + ], + "hide_toolbar": 1, + "in_create": 1, + "links": [], + "modified": "2023-11-18 22:40:12.145940", + "modified_by": "Administrator", + "module": "Desk", + "name": "Notification Log", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "subject", + "track_seen": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/notification_log/notification_log.py b/xhiveframework/desk/doctype/notification_log/notification_log.py new file mode 100644 index 0000000..c246f48 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_log/notification_log.py @@ -0,0 +1,210 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + is_email_notifications_enabled_for_type, + is_notifications_enabled, +) +from xhiveframework.model.document import Document + + +class NotificationLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + attached_file: DF.Code | None + document_name: DF.Data | None + document_type: DF.Link | None + email_content: DF.TextEditor | None + for_user: DF.Link | None + from_user: DF.Link | None + link: DF.Data | None + read: DF.Check + subject: DF.Text | None + type: DF.Literal["Mention", "Energy Point", "Assignment", "Share", "Alert"] + + # end: auto-generated types + def after_insert(self): + xhiveframework.publish_realtime("notification", after_commit=True, user=self.for_user) + set_notifications_as_unseen(self.for_user) + if is_email_notifications_enabled_for_type(self.for_user, self.type): + try: + send_notification_email(self) + except xhiveframework.OutgoingEmailError: + self.log_error(_("Failed to send notification email")) + + @staticmethod + def clear_old_logs(days=180): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Notification Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + +def get_permission_query_conditions(for_user): + if not for_user: + for_user = xhiveframework.session.user + + if for_user == "Administrator": + return + + return f"""(`tabNotification Log`.for_user = {xhiveframework.db.escape(for_user)})""" + + +def get_title(doctype, docname, title_field=None): + if not title_field: + title_field = xhiveframework.get_meta(doctype).get_title_field() + return docname if title_field == "name" else xhiveframework.db.get_value(doctype, docname, title_field) + + +def get_title_html(title): + return f'{title}' + + +def enqueue_create_notification(users: list[str] | str, doc: dict): + """Send notification to users. + + users: list of user emails or string of users with comma separated emails + doc: contents of `Notification` doc + """ + + # During installation of new site, enqueue_create_notification tries to connect to Redis. + # This breaks new site creation if Redis server is not running. + # We do not need any notifications in fresh installation + if xhiveframework.flags.in_install: + return + + doc = xhiveframework._dict(doc) + + if isinstance(users, str): + users = [user.strip() for user in users.split(",") if user.strip()] + users = list(set(users)) + + xhiveframework.enqueue( + "xhiveframework.desk.doctype.notification_log.notification_log.make_notification_logs", + doc=doc, + users=users, + now=xhiveframework.flags.in_test, + ) + + +def make_notification_logs(doc, users): + for user in _get_user_ids(users): + notification = xhiveframework.new_doc("Notification Log") + notification.update(doc) + notification.for_user = user + if ( + notification.for_user != notification.from_user + or doc.type == "Energy Point" + or doc.type == "Alert" + ): + notification.insert(ignore_permissions=True) + + +def _get_user_ids(user_emails): + user_names = xhiveframework.db.get_values( + "User", {"enabled": 1, "email": ("in", user_emails)}, "name", pluck=True + ) + return [user for user in user_names if is_notifications_enabled(user)] + + +def send_notification_email(doc: NotificationLog): + if doc.type == "Energy Point" and doc.email_content is None: + return + + from xhiveframework.utils import get_url_to_form, strip_html + + user = xhiveframework.db.get_value("User", doc.for_user, fieldname=["email", "language"], as_dict=True) + if not user: + return + + header = get_email_header(doc, user.language) + email_subject = strip_html(doc.subject) + args = { + "body_content": doc.subject, + "description": doc.email_content, + } + if doc.link: + args["doc_link"] = doc.link + else: + args["document_type"] = doc.document_type + args["document_name"] = doc.document_name + args["doc_link"] = get_url_to_form(doc.document_type, doc.document_name) + + xhiveframework.sendmail( + recipients=user.email, + subject=email_subject, + template="new_notification", + args=args, + header=[header, "orange"], + now=xhiveframework.flags.in_test, + ) + + +def get_email_header(doc, language: str | None = None): + docname = doc.document_name + header_map = { + "Default": _("New Notification", lang=language), + "Mention": _("New Mention on {0}", lang=language).format(docname), + "Assignment": _("Assignment Update on {0}", lang=language).format(docname), + "Share": _("New Document Shared {0}", lang=language).format(docname), + "Energy Point": _("Energy Point Update on {0}", lang=language).format(docname), + } + + return header_map[doc.type or "Default"] + + +@xhiveframework.whitelist() +def get_notification_logs(limit=20): + notification_logs = xhiveframework.db.get_list( + "Notification Log", fields=["*"], limit=limit, order_by="modified desc" + ) + + users = [log.from_user for log in notification_logs] + users = [*set(users)] # remove duplicates + user_info = xhiveframework._dict() + + for user in users: + xhiveframework.utils.add_user_info(user, user_info) + + return {"notification_logs": notification_logs, "user_info": user_info} + + +@xhiveframework.whitelist() +def mark_all_as_read(): + unread_docs_list = xhiveframework.get_all( + "Notification Log", filters={"read": 0, "for_user": xhiveframework.session.user} + ) + unread_docnames = [doc.name for doc in unread_docs_list] + if unread_docnames: + filters = {"name": ["in", unread_docnames]} + xhiveframework.db.set_value("Notification Log", filters, "read", 1, update_modified=False) + + +@xhiveframework.whitelist() +def mark_as_read(docname: str): + if xhiveframework.flags.read_only: + return + + if docname: + xhiveframework.db.set_value("Notification Log", str(docname), "read", 1, update_modified=False) + + +@xhiveframework.whitelist() +def trigger_indicator_hide(): + xhiveframework.publish_realtime("indicator_hide", user=xhiveframework.session.user) + + +def set_notifications_as_unseen(user): + try: + xhiveframework.db.set_value("Notification Settings", user, "seen", 0, update_modified=False) + except xhiveframework.DoesNotExistError: + return diff --git a/xhiveframework/desk/doctype/notification_log/notification_log_list.js b/xhiveframework/desk/doctype/notification_log/notification_log_list.js new file mode 100644 index 0000000..d9488be --- /dev/null +++ b/xhiveframework/desk/doctype/notification_log/notification_log_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Notification Log"] = { + onload: function (listview) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(cur_list.doctype); + }); + }, +}; diff --git a/xhiveframework/desk/doctype/notification_log/test_notification_log.py b/xhiveframework/desk/doctype/notification_log/test_notification_log.py new file mode 100644 index 0000000..c1a5d40 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_log/test_notification_log.py @@ -0,0 +1,51 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.user.user import get_system_users +from xhiveframework.desk.form.assign_to import add as assign_task +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNotificationLog(XhiveFrameworkTestCase): + def test_assignment(self): + todo = get_todo() + user = get_user() + + assign_task( + {"assign_to": [user], "doctype": "ToDo", "name": todo.name, "description": todo.description} + ) + log_type = xhiveframework.db.get_value( + "Notification Log", {"document_type": "ToDo", "document_name": todo.name}, "type" + ) + self.assertEqual(log_type, "Assignment") + + def test_share(self): + todo = get_todo() + user = get_user() + + xhiveframework.share.add("ToDo", todo.name, user, notify=1) + log_type = xhiveframework.db.get_value( + "Notification Log", {"document_type": "ToDo", "document_name": todo.name}, "type" + ) + self.assertEqual(log_type, "Share") + + email = get_last_email_queue() + content = f"Subject: {xhiveframework.utils.get_fullname(xhiveframework.session.user)} shared a document ToDo" + self.assertTrue(content in email.message) + + +def get_last_email_queue(): + res = xhiveframework.get_all("Email Queue", fields=["message"], order_by="creation desc", limit=1) + return res[0] + + +def get_todo(): + if not xhiveframework.get_all("ToDo"): + return xhiveframework.get_doc({"doctype": "ToDo", "description": "Test for Notification"}).insert() + + res = xhiveframework.get_all("ToDo", limit=1) + return xhiveframework.get_cached_doc("ToDo", res[0].name) + + +def get_user(): + return get_system_users(limit=1)[0] diff --git a/xhiveframework/desk/doctype/notification_settings/__init__.py b/xhiveframework/desk/doctype/notification_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/notification_settings/notification_settings.js b/xhiveframework/desk/doctype/notification_settings/notification_settings.js new file mode 100644 index 0000000..6871ae5 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_settings/notification_settings.js @@ -0,0 +1,22 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Notification Settings", { + onload: (frm) => { + frm.set_query("subscribed_documents", () => { + return { + filters: { + istable: 0, + }, + }; + }); + }, + + refresh: (frm) => { + if (xhiveframework.user.has_role("System Manager")) { + frm.add_custom_button(__("Go to Notification Settings List"), () => { + xhiveframework.set_route("List", "Notification Settings"); + }); + } + }, +}); diff --git a/xhiveframework/desk/doctype/notification_settings/notification_settings.json b/xhiveframework/desk/doctype/notification_settings/notification_settings.json new file mode 100644 index 0000000..b4ea0fd --- /dev/null +++ b/xhiveframework/desk/doctype/notification_settings/notification_settings.json @@ -0,0 +1,146 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2019-09-11 22:15:44.851526", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "subscribed_documents", + "column_break_3", + "enable_email_notifications", + "enable_email_mention", + "enable_email_assignment", + "enable_email_threads_on_assigned_document", + "enable_email_energy_point", + "enable_email_share", + "enable_email_event_reminders", + "user", + "seen", + "system_notifications_section", + "energy_points_system_notifications" + ], + "fields": [ + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "subscribed_documents", + "fieldtype": "Table MultiSelect", + "label": "Open Documents", + "options": "Notification Subscribed Document" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Section Break", + "label": "Email Settings" + }, + { + "default": "1", + "fieldname": "enable_email_notifications", + "fieldtype": "Check", + "label": "Enable Email Notifications" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "fieldname": "enable_email_mention", + "fieldtype": "Check", + "label": "Mentions" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "fieldname": "enable_email_assignment", + "fieldtype": "Check", + "label": "Assignments" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "fieldname": "enable_email_energy_point", + "fieldtype": "Check", + "label": "Energy Points" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "fieldname": "enable_email_share", + "fieldtype": "Check", + "label": "Document Share" + }, + { + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "hidden": 1, + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "hidden": 1, + "label": "Seen" + }, + { + "fieldname": "system_notifications_section", + "fieldtype": "Section Break", + "label": "System Notifications" + }, + { + "default": "1", + "fieldname": "energy_points_system_notifications", + "fieldtype": "Check", + "label": "Energy Points" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "fieldname": "enable_email_event_reminders", + "fieldtype": "Check", + "label": "Event Reminders" + }, + { + "default": "1", + "depends_on": "enable_email_notifications", + "description": "Get notified when an email is received on any of the documents assigned to you.", + "fieldname": "enable_email_threads_on_assigned_document", + "fieldtype": "Check", + "label": "Email Threads on Assigned Document" + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-12-01 12:46:15.490640", + "modified_by": "Administrator", + "module": "Desk", + "name": "Notification Settings", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/notification_settings/notification_settings.py b/xhiveframework/desk/doctype/notification_settings/notification_settings.py new file mode 100644 index 0000000..f1f03a7 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_settings/notification_settings.py @@ -0,0 +1,138 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class NotificationSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.desk.doctype.notification_subscribed_document.notification_subscribed_document import ( + NotificationSubscribedDocument, + ) + from xhiveframework.types import DF + + enable_email_assignment: DF.Check + enable_email_energy_point: DF.Check + enable_email_event_reminders: DF.Check + enable_email_mention: DF.Check + enable_email_notifications: DF.Check + enable_email_share: DF.Check + enable_email_threads_on_assigned_document: DF.Check + enabled: DF.Check + energy_points_system_notifications: DF.Check + seen: DF.Check + subscribed_documents: DF.TableMultiSelect[NotificationSubscribedDocument] + user: DF.Link | None + + # end: auto-generated types + def on_update(self): + from xhiveframework.desk.notifications import clear_notification_config + + clear_notification_config(xhiveframework.session.user) + + +def is_notifications_enabled(user): + enabled = xhiveframework.db.get_value("Notification Settings", user, "enabled") + if enabled is None: + return True + return enabled + + +def is_email_notifications_enabled(user): + enabled = xhiveframework.db.get_value("Notification Settings", user, "enable_email_notifications") + if enabled is None: + return True + return enabled + + +def is_email_notifications_enabled_for_type(user, notification_type): + if not is_email_notifications_enabled(user): + return False + + if notification_type == "Alert": + return False + + fieldname = "enable_email_" + xhiveframework.scrub(notification_type) + enabled = xhiveframework.db.get_value("Notification Settings", user, fieldname) + if enabled is None: + return True + return enabled + + +def create_notification_settings(user): + if not xhiveframework.db.exists("Notification Settings", user): + _doc = xhiveframework.new_doc("Notification Settings") + _doc.name = user + _doc.insert(ignore_permissions=True) + + +def toggle_notifications(user: str, enable: bool = False): + try: + settings = xhiveframework.get_doc("Notification Settings", user) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_last_message() + return + + if settings.enabled != enable: + settings.enabled = enable + settings.save() + + +@xhiveframework.whitelist() +def get_subscribed_documents(): + if not xhiveframework.session.user: + return [] + + try: + if xhiveframework.db.exists("Notification Settings", xhiveframework.session.user): + doc = xhiveframework.get_doc("Notification Settings", xhiveframework.session.user) + return [item.document for item in doc.subscribed_documents] + # Notification Settings is fetched even before sync doctype is called + # but it will throw an ImportError, we can ignore it in migrate + except ImportError: + pass + + return [] + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + return + + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return """(`tabNotification Settings`.name != 'Administrator')""" + + return f"""(`tabNotification Settings`.name = {xhiveframework.db.escape(user)})""" + + +def has_permission(doc, ptype="read", user=None): + # - Administrator can access everything. + # - System managers can access everything except admin. + # - Everyone else can only access their document. + user = user or xhiveframework.session.user + + if user == "Administrator": + return True + + if "System Manager" in xhiveframework.get_roles(user): + return doc.name != "Administrator" + + return doc.name == user + + +@xhiveframework.whitelist() +def set_seen_value(value, user): + if xhiveframework.flags.read_only: + return + + xhiveframework.db.set_value("Notification Settings", user, "seen", value, update_modified=False) diff --git a/xhiveframework/desk/doctype/notification_settings/test_notification_settings.py b/xhiveframework/desk/doctype/notification_settings/test_notification_settings.py new file mode 100644 index 0000000..d6fe61f --- /dev/null +++ b/xhiveframework/desk/doctype/notification_settings/test_notification_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNotificationSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/notification_subscribed_document/__init__.py b/xhiveframework/desk/doctype/notification_subscribed_document/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.json b/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.json new file mode 100644 index 0000000..b3f4046 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.json @@ -0,0 +1,30 @@ +{ + "creation": "2019-10-09 15:04:39.504787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document" + ], + "fields": [ + { + "fieldname": "document", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document", + "options": "DocType", + "reqd": 1 + } + ], + "istable": 1, + "modified": "2019-10-09 16:02:00.049237", + "modified_by": "Administrator", + "module": "Desk", + "name": "Notification Subscribed Document", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.py b/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.py new file mode 100644 index 0000000..e31d351 --- /dev/null +++ b/xhiveframework/desk/doctype/notification_subscribed_document/notification_subscribed_document.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class NotificationSubscribedDocument(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/number_card/__init__.py b/xhiveframework/desk/doctype/number_card/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/number_card/number_card.js b/xhiveframework/desk/doctype/number_card/number_card.js new file mode 100644 index 0000000..38ea177 --- /dev/null +++ b/xhiveframework/desk/doctype/number_card/number_card.js @@ -0,0 +1,451 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Number Card", { + refresh: function (frm) { + if (!xhiveframework.boot.developer_mode && frm.doc.is_standard) { + frm.disable_save(); + } else { + frm.enable_save(); + } + + frm.set_df_property("filters_section", "hidden", 1); + frm.set_df_property("dynamic_filters_section", "hidden", 1); + frm.trigger("set_options"); + + if (!frm.doc.type) { + frm.set_value("type", "Document Type"); + } + + if (frm.doc.type == "Report" && frm.doc.report_name) { + frm.trigger("set_report_filters"); + } + + if (frm.doc.type == "Custom") { + frm.filters = eval(frm.doc.filters_config); + frm.trigger("render_filters_table"); + } + frm.trigger("set_parent_document_type"); + + if (!frm.is_new()) { + frm.trigger("create_add_to_dashboard_button"); + } + }, + + create_add_to_dashboard_button: function (frm) { + frm.add_custom_button("Add Card to Dashboard", () => { + const dialog = xhiveframework.dashboard_utils.get_add_to_dashboard_dialog( + frm.doc.name, + "Number Card", + "xhiveframework.desk.doctype.number_card.number_card.add_card_to_dashboard" + ); + + if (!frm.doc.name) { + xhiveframework.msgprint(__("Please create Card first")); + } else { + dialog.show(); + } + }); + }, + + before_save: function (frm) { + let dynamic_filters = JSON.parse(frm.doc.dynamic_filters_json || "null"); + let static_filters = JSON.parse(frm.doc.filters_json || "null"); + static_filters = xhiveframework.dashboard_utils.remove_common_static_filter_values( + static_filters, + dynamic_filters + ); + + frm.set_value("filters_json", JSON.stringify(static_filters)); + frm.trigger("render_filters_table"); + frm.trigger("render_dynamic_filters_table"); + }, + + is_standard: function (frm) { + frm.trigger("render_dynamic_filters_table"); + frm.set_df_property("dynamic_filters_section", "hidden", 1); + }, + + type: function (frm) { + if (frm.doc.type == "Report") { + frm.set_query("report_name", () => { + return { + filters: { + report_type: ["!=", "Report Builder"], + }, + }; + }); + } + }, + + report_name: function (frm) { + frm.filters = []; + frm.set_value("filters_json", "{}"); + frm.set_value("dynamic_filters_json", "{}"); + frm.set_df_property("report_field", "options", []); + frm.trigger("set_report_filters"); + }, + + filters_config: function (frm) { + frm.filters = eval(frm.doc.filters_config); + const filter_values = xhiveframework.report_utils.get_filter_values(frm.filters); + frm.set_value("filters_json", JSON.stringify(filter_values)); + frm.trigger("render_filters_table"); + }, + + document_type: function (frm) { + frm.set_query("document_type", function () { + return { + filters: { + issingle: false, + }, + }; + }); + frm.set_value("filters_json", "[]"); + frm.set_value("dynamic_filters_json", "[]"); + frm.set_value("aggregate_function_based_on", ""); + frm.set_value("parent_document_type", ""); + frm.trigger("set_options"); + frm.trigger("set_parent_document_type"); + }, + + set_options: function (frm) { + if (frm.doc.type !== "Document Type") { + return; + } + + let aggregate_based_on_fields = []; + const doctype = frm.doc.document_type; + + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => { + xhiveframework.get_meta(doctype).fields.map((df) => { + if (xhiveframework.model.numeric_fieldtypes.includes(df.fieldtype)) { + if (df.fieldtype == "Currency") { + if (!df.options || df.options !== "Company:company:default_currency") { + return; + } + } + aggregate_based_on_fields.push({ label: df.label, value: df.fieldname }); + } + }); + + frm.set_df_property( + "aggregate_function_based_on", + "options", + aggregate_based_on_fields + ); + }); + frm.trigger("render_filters_table"); + frm.trigger("render_dynamic_filters_table"); + } + }, + + set_report_filters: function (frm) { + const report_name = frm.doc.report_name; + if (report_name) { + xhiveframework.report_utils.get_report_filters(report_name).then((filters) => { + if (filters) { + frm.filters = filters; + const filter_values = xhiveframework.report_utils.get_filter_values(filters); + if (frm.doc.filters_json.length <= 2) { + frm.set_value("filters_json", JSON.stringify(filter_values)); + } + } + frm.trigger("render_filters_table"); + frm.trigger("set_report_field_options"); + frm.trigger("render_dynamic_filters_table"); + }); + } + }, + + set_report_field_options: function (frm) { + let filters = frm.doc.filters_json.length > 2 ? JSON.parse(frm.doc.filters_json) : null; + if (frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2) { + filters = xhiveframework.dashboard_utils.get_all_filters(frm.doc); + } + xhiveframework + .xcall("xhiveframework.desk.query_report.run", { + report_name: frm.doc.report_name, + filters: filters, + ignore_prepared_report: 1, + }) + .then((data) => { + if (data.result.length) { + frm.field_options = xhiveframework.report_utils.get_field_options_from_report( + data.columns, + data + ); + frm.set_df_property( + "report_field", + "options", + frm.field_options.numeric_fields + ); + if (!frm.field_options.numeric_fields.length) { + xhiveframework.msgprint( + __("Report has no numeric fields, please change the Report Name") + ); + } + } else { + xhiveframework.msgprint( + __( + "Report has no data, please modify the filters or change the Report Name" + ) + ); + } + }); + }, + + render_filters_table: function (frm) { + frm.set_df_property("filters_section", "hidden", 0); + let is_document_type = frm.doc.type == "Document Type"; + let is_dynamic_filter = (f) => ["Date", "DateRange"].includes(f.fieldtype) && f.default; + + let wrapper = $(frm.get_field("filters_json").wrapper).empty(); + let table = $(` + + + + + + + + +
    ${__("Filter")}${__("Condition")}${__("Value")}
    `).appendTo(wrapper); + $(`

    ${__("Click table to edit")}

    `).appendTo(wrapper); + + let filters = JSON.parse(frm.doc.filters_json || "[]"); + let filters_set = false; + + // Set dynamic filters for reports + if (frm.doc.type == "Report") { + let set_filters = false; + frm.filters.forEach((f) => { + if (is_dynamic_filter(f)) { + filters[f.fieldname] = f.default; + set_filters = true; + } + }); + set_filters && frm.set_value("filters_json", JSON.stringify(filters)); + } + + let fields = []; + if (is_document_type) { + fields = [ + { + fieldtype: "HTML", + fieldname: "filter_area", + }, + ]; + + if (filters.length) { + filters.forEach((filter) => { + const filter_row = $(` + ${filter[1]} + ${filter[2] || ""} + ${filter[3]} + `); + + table.find("tbody").append(filter_row); + }); + filters_set = true; + } + } else if (frm.filters.length) { + fields = frm.filters.filter((f) => f.fieldname); + fields.map((f) => { + if (filters[f.fieldname]) { + let condition = "="; + const filter_row = $(` + ${f.label} + ${condition} + ${filters[f.fieldname] || ""} + `); + table.find("tbody").append(filter_row); + if (!filters_set) filters_set = true; + } + }); + } + + if (!filters_set) { + const filter_row = $(` + ${__("Click to Set Filters")}`); + table.find("tbody").append(filter_row); + } + + table.on("click", () => { + let dialog = new xhiveframework.ui.Dialog({ + title: __("Set Filters"), + fields: fields.filter((f) => !is_dynamic_filter(f)), + primary_action: function () { + let values = this.get_values(); + if (values) { + this.hide(); + if (is_document_type) { + let filters = frm.filter_group.get_filters(); + frm.set_value("filters_json", JSON.stringify(filters)); + } else { + frm.set_value("filters_json", JSON.stringify(values)); + } + frm.trigger("render_filters_table"); + } + }, + primary_action_label: "Set", + }); + + if (is_document_type) { + frm.filter_group = new xhiveframework.ui.FilterGroup({ + parent: dialog.get_field("filter_area").$wrapper, + doctype: frm.doc.document_type, + parent_doctype: frm.doc.parent_document_type, + on_change: () => {}, + }); + filters && frm.filter_group.add_filters_to_filter_group(filters); + } + + dialog.show(); + + if (frm.doc.type == "Report") { + //Set query report object so that it can be used while fetching filter values in the report + xhiveframework.query_report = new xhiveframework.views.QueryReport({ + filters: dialog.fields_list, + }); + xhiveframework.query_reports[frm.doc.report_name] && + xhiveframework.query_reports[frm.doc.report_name].onload && + xhiveframework.query_reports[frm.doc.report_name].onload(xhiveframework.query_report); + } + + dialog.set_values(filters); + }); + }, + + render_dynamic_filters_table(frm) { + if (!xhiveframework.boot.developer_mode || !frm.doc.is_standard || frm.doc.type == "Custom") { + return; + } + + frm.set_df_property("dynamic_filters_section", "hidden", 0); + + let is_document_type = frm.doc.type == "Document Type"; + + let wrapper = $(frm.get_field("dynamic_filters_json").wrapper).empty(); + + frm.dynamic_filter_table = + $(` + + + + + + + + +
    ${__("Filter")}${__("Condition")}${__("Value")}
    `).appendTo(wrapper); + + frm.dynamic_filters = + frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2 + ? JSON.parse(frm.doc.dynamic_filters_json) + : null; + + frm.trigger("set_dynamic_filters_in_table"); + + let filters = JSON.parse(frm.doc.filters_json || "[]"); + + let fields = xhiveframework.dashboard_utils.get_fields_for_dynamic_filter_dialog( + is_document_type, + filters, + frm.dynamic_filters + ); + + frm.dynamic_filter_table.on("click", () => { + let dialog = new xhiveframework.ui.Dialog({ + title: __("Set Dynamic Filters"), + fields: fields, + primary_action: () => { + let values = dialog.get_values(); + dialog.hide(); + let dynamic_filters = []; + for (let key of Object.keys(values)) { + if (is_document_type) { + let [doctype, fieldname] = key.split(":"); + dynamic_filters.push([doctype, fieldname, "=", values[key]]); + } + } + + if (is_document_type) { + frm.set_value("dynamic_filters_json", JSON.stringify(dynamic_filters)); + } else { + frm.set_value("dynamic_filters_json", JSON.stringify(values)); + } + frm.trigger("set_dynamic_filters_in_table"); + }, + primary_action_label: "Set", + }); + + dialog.show(); + dialog.set_values(frm.dynamic_filters); + }); + }, + + set_dynamic_filters_in_table: function (frm) { + frm.dynamic_filters = + frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2 + ? JSON.parse(frm.doc.dynamic_filters_json) + : null; + + if (!frm.dynamic_filters) { + const filter_row = $(` + ${__("Click to Set Dynamic Filters")}`); + frm.dynamic_filter_table.find("tbody").html(filter_row); + } else { + let filter_rows = ""; + if ($.isArray(frm.dynamic_filters)) { + frm.dynamic_filters.forEach((filter) => { + filter_rows += ` + ${filter[1]} + ${filter[2] || ""} + ${filter[3]} + `; + }); + } else { + let condition = "="; + for (let [key, val] of Object.entries(frm.dynamic_filters)) { + filter_rows += ` + ${key} + ${condition} + ${val || ""} + `; + } + } + + frm.dynamic_filter_table.find("tbody").html(filter_rows); + } + }, + + set_parent_document_type: async function (frm) { + let document_type = frm.doc.document_type; + let doc_is_table = + document_type && + (await xhiveframework.db.get_value("DocType", document_type, "istable")).message.istable; + + frm.set_df_property("parent_document_type", "hidden", !doc_is_table); + + if (document_type && doc_is_table) { + let parents = await xhiveframework.xcall( + "xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.get_parent_doctypes", + { child_type: document_type } + ); + + frm.set_query("parent_document_type", function () { + return { + filters: { + name: ["in", parents], + }, + }; + }); + + if (parents.length === 1) { + frm.set_value("parent_document_type", parents[0]); + } + } + }, +}); diff --git a/xhiveframework/desk/doctype/number_card/number_card.json b/xhiveframework/desk/doctype/number_card/number_card.json new file mode 100644 index 0000000..cdc357e --- /dev/null +++ b/xhiveframework/desk/doctype/number_card/number_card.json @@ -0,0 +1,252 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2020-04-15 18:06:39.444683", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_standard", + "module", + "label", + "type", + "report_name", + "method", + "function", + "aggregate_function_based_on", + "column_break_2", + "document_type", + "parent_document_type", + "report_field", + "report_function", + "is_public", + "custom_configuration_section", + "filters_config", + "stats_section", + "show_percentage_stats", + "stats_time_interval", + "filters_section", + "filters_json", + "dynamic_filters_section", + "dynamic_filters_json", + "section_break_16", + "color" + ], + "fields": [ + { + "depends_on": "eval: doc.type == 'Document Type'", + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Document Type", + "mandatory_depends_on": "eval: doc.type == 'Document Type'", + "options": "DocType" + }, + { + "depends_on": "eval: doc.type == 'Document Type'", + "fieldname": "function", + "fieldtype": "Select", + "label": "Function", + "mandatory_depends_on": "eval: doc.type == 'Document Type'", + "options": "Count\nSum\nAverage\nMinimum\nMaximum" + }, + { + "depends_on": "eval: doc.type === 'Document Type' && doc.function !== 'Count'", + "fieldname": "aggregate_function_based_on", + "fieldtype": "Select", + "label": "Aggregate Function Based On", + "mandatory_depends_on": "eval: doc.function !== 'Count'" + }, + { + "fieldname": "filters_json", + "fieldtype": "Code", + "label": "Filters JSON", + "options": "JSON" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "fieldname": "color", + "fieldtype": "Color", + "label": "Color" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "filters_section", + "fieldtype": "Section Break", + "label": "Filters Section" + }, + { + "default": "0", + "description": "This card will be available to all Users if this is set", + "fieldname": "is_public", + "fieldtype": "Check", + "label": "Is Public" + }, + { + "default": "1", + "fieldname": "show_percentage_stats", + "fieldtype": "Check", + "label": "Show Percentage Stats" + }, + { + "default": "Daily", + "depends_on": "eval: doc.show_percentage_stats", + "description": "Show percentage difference according to this time interval", + "fieldname": "stats_time_interval", + "fieldtype": "Select", + "label": "Stats Time Interval", + "options": "Daily\nWeekly\nMonthly\nYearly" + }, + { + "depends_on": "eval: doc.type == 'Document Type'", + "fieldname": "stats_section", + "fieldtype": "Section Break", + "label": "Stats" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "no_copy": 1, + "read_only_depends_on": "eval: !xhiveframework.boot.developer_mode" + }, + { + "depends_on": "eval: doc.is_standard", + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "mandatory_depends_on": "eval: doc.is_standard", + "options": "Module Def" + }, + { + "fieldname": "dynamic_filters_json", + "fieldtype": "Code", + "label": "Dynamic Filters JSON", + "options": "JSON" + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, + { + "fieldname": "dynamic_filters_section", + "fieldtype": "Section Break", + "label": "Dynamic Filters Section" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "label": "Type", + "options": "Document Type\nReport\nCustom" + }, + { + "depends_on": "eval: doc.type == 'Report'", + "fieldname": "report_name", + "fieldtype": "Link", + "label": "Report Name", + "mandatory_depends_on": "eval: doc.type == 'Report'", + "options": "Report" + }, + { + "depends_on": "eval: doc.type == 'Report'", + "fieldname": "report_field", + "fieldtype": "Select", + "label": "Field", + "mandatory_depends_on": "eval: doc.type == 'Report'" + }, + { + "depends_on": "eval: doc.type == 'Custom'", + "description": "Set the path to a whitelisted function that will return the data for the number card in the format:\n\n
    \n{\n\t\"value\": value,\n\t\"fieldtype\": \"Currency\",\n\t\"route_options\": {\"from_date\": \"2023-05-23\"},\n\t\"route\": [\"query-report\", \"Permitted Documents For User\"]\n}
    ", + "fieldname": "method", + "fieldtype": "Data", + "label": "Method", + "mandatory_depends_on": "eval: doc.type == 'Custom'" + }, + { + "depends_on": "eval: doc.type == 'Custom'", + "fieldname": "custom_configuration_section", + "fieldtype": "Section Break", + "label": "Custom Configuration" + }, + { + "description": "Set the filters here. For example:\n
    \n[{\n\tfieldname: \"company\",\n\tlabel: __(\"Company\"),\n\tfieldtype: \"Link\",\n\toptions: \"Company\",\n\tdefault: xhiveframework.defaults.get_user_default(\"Company\"),\n\treqd: 1\n},\n{\n\tfieldname: \"account\",\n\tlabel: __(\"Account\"),\n\tfieldtype: \"Link\",\n\toptions: \"Account\",\n\treqd: 1\n}]\n
    ", + "fieldname": "filters_config", + "fieldtype": "Code", + "label": "Filters Configuration", + "options": "JSON" + }, + { + "depends_on": "eval: doc.type == 'Report'", + "fieldname": "report_function", + "fieldtype": "Select", + "label": "Function", + "mandatory_depends_on": "eval: doc.type == 'Report'", + "options": "Sum\nAverage\nMinimum\nMaximum" + }, + { + "depends_on": "eval: doc.type === 'Document Type'", + "description": "The document type selected is a child table, so the parent document type is required.", + "fieldname": "parent_document_type", + "fieldtype": "Link", + "label": "Parent Document Type", + "options": "DocType" + } + ], + "links": [], + "modified": "2023-11-09 13:44:00.280846", + "modified_by": "Administrator", + "module": "Desk", + "name": "Number Card", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Dashboard Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "search_fields": "label, document_type", + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "label", + "track_changes": 1 +} diff --git a/xhiveframework/desk/doctype/number_card/number_card.py b/xhiveframework/desk/doctype/number_card/number_card.py new file mode 100644 index 0000000..dd1ecc4 --- /dev/null +++ b/xhiveframework/desk/doctype/number_card/number_card.py @@ -0,0 +1,260 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.boot import get_allowed_report_names +from xhiveframework.config import get_modules_from_all_apps_for_user +from xhiveframework.model.document import Document +from xhiveframework.model.naming import append_number_if_name_exists +from xhiveframework.modules.export_file import export_to_files +from xhiveframework.query_builder import Criterion +from xhiveframework.query_builder.utils import DocType +from xhiveframework.utils import cint, flt + + +class NumberCard(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + aggregate_function_based_on: DF.Literal[None] + color: DF.Color | None + document_type: DF.Link | None + dynamic_filters_json: DF.Code | None + filters_config: DF.Code | None + filters_json: DF.Code | None + function: DF.Literal["Count", "Sum", "Average", "Minimum", "Maximum"] + is_public: DF.Check + is_standard: DF.Check + label: DF.Data + method: DF.Data | None + module: DF.Link | None + parent_document_type: DF.Link | None + report_field: DF.Literal[None] + report_function: DF.Literal["Sum", "Average", "Minimum", "Maximum"] + report_name: DF.Link | None + show_percentage_stats: DF.Check + stats_time_interval: DF.Literal["Daily", "Weekly", "Monthly", "Yearly"] + type: DF.Literal["Document Type", "Report", "Custom"] + + # end: auto-generated types + def autoname(self): + if not self.name: + self.name = self.label + + if xhiveframework.db.exists("Number Card", self.name): + self.name = append_number_if_name_exists("Number Card", self.name) + + def validate(self): + if self.type == "Document Type": + if not (self.document_type and self.function): + xhiveframework.throw(_("Document Type and Function are required to create a number card")) + + if self.function != "Count" and not self.aggregate_function_based_on: + xhiveframework.throw(_("Aggregate Field is required to create a number card")) + + if xhiveframework.get_meta(self.document_type).istable and not self.parent_document_type: + xhiveframework.throw(_("Parent Document Type is required to create a number card")) + + elif self.type == "Report": + if not (self.report_name and self.report_field and self.function): + xhiveframework.throw(_("Report Name, Report Field and Fucntion are required to create a number card")) + + elif self.type == "Custom": + if not self.method: + xhiveframework.throw(_("Method is required to create a number card")) + + def on_update(self): + if xhiveframework.conf.developer_mode and self.is_standard: + export_to_files(record_list=[["Number Card", self.name]], record_module=self.module) + + +def get_permission_query_conditions(user=None): + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + return + + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return None + + doctype_condition = False + module_condition = False + + allowed_doctypes = [xhiveframework.db.escape(doctype) for doctype in xhiveframework.permissions.get_doctypes_with_read()] + allowed_modules = [ + xhiveframework.db.escape(module.get("module_name")) for module in get_modules_from_all_apps_for_user() + ] + + if allowed_doctypes: + doctype_condition = "`tabNumber Card`.`document_type` in ({allowed_doctypes})".format( + allowed_doctypes=",".join(allowed_doctypes) + ) + if allowed_modules: + module_condition = """`tabNumber Card`.`module` in ({allowed_modules}) + or `tabNumber Card`.`module` is NULL""".format(allowed_modules=",".join(allowed_modules)) + + return f""" + {doctype_condition} + and + {module_condition} + """ + + +def has_permission(doc, ptype, user): + roles = xhiveframework.get_roles(user) + if "System Manager" in roles: + return True + + if doc.type == "Report": + if doc.report_name in get_allowed_report_names(): + return True + else: + allowed_doctypes = tuple(xhiveframework.permissions.get_doctypes_with_read()) + if doc.document_type in allowed_doctypes: + return True + + return False + + +@xhiveframework.whitelist() +def get_result(doc, filters, to_date=None): + doc = xhiveframework.parse_json(doc) + fields = [] + sql_function_map = { + "Count": "count", + "Sum": "sum", + "Average": "avg", + "Minimum": "min", + "Maximum": "max", + } + + function = sql_function_map[doc.function] + + if function == "count": + fields = [f"{function}(*) as result"] + else: + fields = [f"{function}({doc.aggregate_function_based_on}) as result"] + + if not filters: + filters = [] + elif isinstance(filters, str): + filters = xhiveframework.parse_json(filters) + + if to_date: + filters.append([doc.document_type, "creation", "<", to_date]) + + res = xhiveframework.get_list( + doc.document_type, fields=fields, filters=filters, parent_doctype=doc.parent_document_type + ) + number = res[0]["result"] if res else 0 + + return flt(number) + + +@xhiveframework.whitelist() +def get_percentage_difference(doc, filters, result): + doc = xhiveframework.parse_json(doc) + result = xhiveframework.parse_json(result) + + doc = xhiveframework.get_doc("Number Card", doc.name) + + if not doc.get("show_percentage_stats"): + return + + previous_result = calculate_previous_result(doc, filters) + if previous_result == 0: + return None + else: + if result == previous_result: + return 0 + else: + return ((result / previous_result) - 1) * 100.0 + + +def calculate_previous_result(doc, filters): + from xhiveframework.utils import add_to_date + + current_date = xhiveframework.utils.now() + if doc.stats_time_interval == "Daily": + previous_date = add_to_date(current_date, days=-1) + elif doc.stats_time_interval == "Weekly": + previous_date = add_to_date(current_date, weeks=-1) + elif doc.stats_time_interval == "Monthly": + previous_date = add_to_date(current_date, months=-1) + else: + previous_date = add_to_date(current_date, years=-1) + + return get_result(doc, filters, previous_date) + + +@xhiveframework.whitelist() +def create_number_card(args): + args = xhiveframework.parse_json(args) + doc = xhiveframework.new_doc("Number Card") + + doc.update(args) + doc.insert(ignore_permissions=True) + return doc + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_cards_for_user(doctype, txt, searchfield, start, page_len, filters): + meta = xhiveframework.get_meta(doctype) + searchfields = meta.get_search_fields() + search_conditions = [] + + if not xhiveframework.db.exists("DocType", doctype): + return + + numberCard = DocType("Number Card") + + if txt: + search_conditions = [numberCard[field].like(f"%{txt}%") for field in searchfields] + + condition_query = xhiveframework.qb.get_query( + doctype, + filters=filters, + validate_filters=True, + ) + + return ( + condition_query.select(numberCard.name, numberCard.label, numberCard.document_type) + .where((numberCard.owner == xhiveframework.session.user) | (numberCard.is_public == 1)) + .where(Criterion.any(search_conditions)) + ).run() + + +@xhiveframework.whitelist() +def create_report_number_card(args): + card = create_number_card(args) + args = xhiveframework.parse_json(args) + args.name = card.name + if args.dashboard: + add_card_to_dashboard(xhiveframework.as_json(args)) + + +@xhiveframework.whitelist() +def add_card_to_dashboard(args): + args = xhiveframework.parse_json(args) + + dashboard = xhiveframework.get_doc("Dashboard", args.dashboard) + dashboard_link = xhiveframework.new_doc("Number Card Link") + dashboard_link.card = args.name + + if args.set_standard and dashboard.is_standard: + card = xhiveframework.get_doc("Number Card", dashboard_link.card) + card.is_standard = 1 + card.module = dashboard.module + card.save() + + dashboard.append("cards", dashboard_link) + dashboard.save() diff --git a/xhiveframework/desk/doctype/number_card/test_number_card.py b/xhiveframework/desk/doctype/number_card/test_number_card.py new file mode 100644 index 0000000..48024e3 --- /dev/null +++ b/xhiveframework/desk/doctype/number_card/test_number_card.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNumberCard(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/number_card_link/__init__.py b/xhiveframework/desk/doctype/number_card_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/number_card_link/number_card_link.json b/xhiveframework/desk/doctype/number_card_link/number_card_link.json new file mode 100644 index 0000000..ac035b3 --- /dev/null +++ b/xhiveframework/desk/doctype/number_card_link/number_card_link.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2020-04-19 17:43:50.858343", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "card" + ], + "fields": [ + { + "fieldname": "card", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Card", + "options": "Number Card" + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-19 17:45:11.878472", + "modified_by": "Administrator", + "module": "Desk", + "name": "Number Card Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/number_card_link/number_card_link.py b/xhiveframework/desk/doctype/number_card_link/number_card_link.py new file mode 100644 index 0000000..2913df6 --- /dev/null +++ b/xhiveframework/desk/doctype/number_card_link/number_card_link.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class NumberCardLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + card: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/onboarding_permission/__init__.py b/xhiveframework/desk/doctype/onboarding_permission/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.js b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.js new file mode 100644 index 0000000..2ab83cf --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.js @@ -0,0 +1,7 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Onboarding Permission", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.json b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.json new file mode 100644 index 0000000..f2a9dc3 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2020-04-30 18:27:48.255489", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "role" + ], + "fields": [ + { + "fieldname": "role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role", + "options": "Role", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-30 18:28:40.423802", + "modified_by": "Administrator", + "module": "Desk", + "name": "Onboarding Permission", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.py b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.py new file mode 100644 index 0000000..d625a57 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_permission/onboarding_permission.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class OnboardingPermission(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + role: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/onboarding_permission/test_onboarding_permission.py b/xhiveframework/desk/doctype/onboarding_permission/test_onboarding_permission.py new file mode 100644 index 0000000..0119d06 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_permission/test_onboarding_permission.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestOnboardingPermission(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/onboarding_step/__init__.py b/xhiveframework/desk/doctype/onboarding_step/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/onboarding_step/onboarding_step.js b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.js new file mode 100644 index 0000000..18300f9 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.js @@ -0,0 +1,86 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Onboarding Step", { + setup: function (frm) { + frm.set_query("form_tour", function () { + return { + filters: { + reference_doctype: frm.doc.reference_document, + }, + }; + }); + }, + + refresh: function (frm) { + xhiveframework.boot.developer_mode && + frm.set_intro( + __( + "To export this step as JSON, link it in a Onboarding document and save the document." + ), + true + ); + if (frm.doc.reference_document && frm.doc.action == "Update Settings") { + setup_fields(frm); + } + + if (!xhiveframework.boot.developer_mode) { + frm.trigger("disable_form"); + } + }, + + reference_document: function (frm) { + if (frm.doc.reference_document && frm.doc.action == "Update Settings") { + setup_fields(frm); + } + }, + + action: function (frm) { + if (frm.doc.action == "Show Form Tour") { + frm.fields_dict.reference_document + .set_description(`You need to add the steps in the contoller JS file. For example: note.js +
    
    +xhiveframework.tour['Note'] = [
    +	{
    +		fieldname: "title",
    +		title: "Title of the Note",
    +		description: "...",
    +	}
    +];
    +
    + `); + } else { + frm.fields_dict.reference_document.set_description(null); + } + }, + + disable_form: function (frm) { + frm.set_read_only(); + frm.fields + .filter((field) => field.has_input) + .forEach((field) => { + frm.set_df_property(field.df.fieldname, "read_only", "1"); + }); + frm.disable_save(); + }, +}); + +function setup_fields(frm) { + if (frm.doc.reference_document && frm.doc.action == "Update Settings") { + xhiveframework.model.with_doctype(frm.doc.reference_document, () => { + let fields = xhiveframework + .get_meta(frm.doc.reference_document) + .fields.filter((df) => { + return ["Data", "Check", "Int", "Link", "Select"].includes(df.fieldtype); + }) + .map((df) => { + return { + label: `${__(df.label, null, df.parent)} (${df.fieldname})`, + value: df.fieldname, + }; + }); + + frm.set_df_property("field", "options", fields); + }); + } +} diff --git a/xhiveframework/desk/doctype/onboarding_step/onboarding_step.json b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.json new file mode 100644 index 0000000..f81550b --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.json @@ -0,0 +1,255 @@ +{ + "actions": [], + "autoname": "prompt", + "creation": "2020-04-14 15:50:25.782387", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "column_break_2", + "is_complete", + "is_skipped", + "description_section", + "description", + "intro_video_url", + "section_break_5", + "action", + "action_label", + "column_break_7", + "reference_document", + "show_full_form", + "show_form_tour", + "form_tour", + "is_single", + "reference_report", + "report_reference_doctype", + "report_type", + "report_description", + "path", + "callback_title", + "callback_message", + "validate_action", + "field", + "value_to_validate", + "video_url" + ], + "fields": [ + { + "default": "0", + "fieldname": "is_complete", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Complete" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "action", + "fieldtype": "Select", + "label": "Action", + "options": "Create Entry\nUpdate Settings\nShow Form Tour\nView Report\nGo to Page\nWatch Video", + "reqd": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.action == \"Create Entry\" || doc.action == \"Update Settings\" || doc.action == \"Create Entry\" || doc.action == \"Show Form Tour\"", + "fieldname": "reference_document", + "fieldtype": "Link", + "label": "Reference Document", + "mandatory_depends_on": "eval:doc.action == \"Create Entry\" || doc.action == \"Update Settings\" || doc.action == \"Create Entry\" || doc.action == \"Show Form Tour\"", + "options": "DocType" + }, + { + "depends_on": "eval:doc.action == \"View Report\"", + "fieldname": "reference_report", + "fieldtype": "Link", + "label": "Reference Report", + "mandatory_depends_on": "eval:doc.action == \"View Report\"", + "options": "Report" + }, + { + "depends_on": "eval:doc.action == \"Watch Video\"", + "fieldname": "video_url", + "fieldtype": "Data", + "label": "Video URL", + "mandatory_depends_on": "eval:doc.action == \"Watch Video\"" + }, + { + "depends_on": "eval:doc.action == \"View Report\"", + "fetch_from": "reference_report.report_type", + "fieldname": "report_type", + "fieldtype": "Data", + "label": "Report Type", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_skipped", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Is Skipped" + }, + { + "depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action", + "fieldname": "field", + "fieldtype": "Select", + "label": "Field", + "mandatory_depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action" + }, + { + "depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action", + "description": "Use % for any non empty value.", + "fieldname": "value_to_validate", + "fieldtype": "Data", + "label": "Value to Validate", + "mandatory_depends_on": "eval:doc.action == \"Update Settings\" && doc.validate_action" + }, + { + "depends_on": "eval:doc.action == \"View Report\"", + "description": "This will be shown to the user in a dialog after routing to the report", + "fieldname": "report_description", + "fieldtype": "Data", + "label": "Report Description", + "mandatory_depends_on": "eval:doc.action == \"View Report\"" + }, + { + "fetch_from": "reference_report.ref_doctype", + "fieldname": "report_reference_doctype", + "fieldtype": "Data", + "label": "Report Reference Doctype", + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.action == \"Create Entry\" || doc.action == \"Update Settings\" || doc.action == \"Create Entry\" || doc.action == \"Show Form Tour\"", + "fetch_from": "reference_document.issingle", + "fieldname": "is_single", + "fieldtype": "Check", + "label": "Is Single" + }, + { + "depends_on": "eval:doc.action == \"Go to Page\"", + "description": "Example: #Tree/Account", + "fieldname": "path", + "fieldtype": "Data", + "label": "Path", + "mandatory_depends_on": "eval:doc.action == \"Go to Page\"" + }, + { + "depends_on": "eval:doc.action == \"Go to Page\"", + "fieldname": "callback_title", + "fieldtype": "Data", + "label": "Callback Title" + }, + { + "depends_on": "eval:doc.action == \"Go to Page\"", + "description": "This will be shown in a modal after routing", + "fieldname": "callback_message", + "fieldtype": "Small Text", + "label": "Callback Message" + }, + { + "default": "1", + "depends_on": "eval:doc.action == \"Update Settings\"", + "fieldname": "validate_action", + "fieldtype": "Check", + "label": "Validate Field" + }, + { + "default": "0", + "depends_on": "eval:doc.action == \"Create Entry\"", + "description": "Show full form instead of a quick entry modal", + "fieldname": "show_full_form", + "fieldtype": "Check", + "label": "Show Full Form?" + }, + { + "description": "Description to inform the user about any action that is going to be performed", + "fieldname": "description_section", + "fieldtype": "Section Break", + "label": "Description" + }, + { + "fieldname": "description", + "fieldtype": "Markdown Editor", + "label": "Description" + }, + { + "fieldname": "intro_video_url", + "fieldtype": "Data", + "label": "Intro Video URL" + }, + { + "fieldname": "action_label", + "fieldtype": "Data", + "label": "Action Label" + }, + { + "default": "0", + "depends_on": "eval:doc.action==\"Create Entry\" && doc.show_full_form", + "fieldname": "show_form_tour", + "fieldtype": "Check", + "label": "Show Form Tour" + }, + { + "depends_on": "show_form_tour", + "fieldname": "form_tour", + "fieldtype": "Link", + "label": "Form Tour", + "options": "Form Tour" + } + ], + "links": [], + "modified": "2023-08-28 22:23:48.174317", + "modified_by": "Administrator", + "module": "Desk", + "name": "Onboarding Step", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "quick_entry": 1, + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/onboarding_step/onboarding_step.py b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.py new file mode 100644 index 0000000..a84c380 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step/onboarding_step.py @@ -0,0 +1,65 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class OnboardingStep(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action: DF.Literal[ + "Create Entry", "Update Settings", "Show Form Tour", "View Report", "Go to Page", "Watch Video" + ] + action_label: DF.Data | None + callback_message: DF.SmallText | None + callback_title: DF.Data | None + description: DF.MarkdownEditor | None + field: DF.Literal[None] + form_tour: DF.Link | None + intro_video_url: DF.Data | None + is_complete: DF.Check + is_single: DF.Check + is_skipped: DF.Check + path: DF.Data | None + reference_document: DF.Link | None + reference_report: DF.Link | None + report_description: DF.Data | None + report_reference_doctype: DF.Data | None + report_type: DF.Data | None + show_form_tour: DF.Check + show_full_form: DF.Check + title: DF.Data + validate_action: DF.Check + value_to_validate: DF.Data | None + video_url: DF.Data | None + + # end: auto-generated types + def before_export(self, doc): + doc.is_complete = 0 + doc.is_skipped = 0 + + +@xhiveframework.whitelist() +def get_onboarding_steps(ob_steps): + steps = [] + for s in json.loads(ob_steps): + doc = xhiveframework.get_doc("Onboarding Step", s.get("step")) + step = doc.as_dict().copy() + step.label = _(doc.title) + if step.action == "Create Entry": + step.is_submittable = xhiveframework.db.get_value( + "DocType", step.reference_document, "is_submittable", cache=True + ) + steps.append(step) + + return steps diff --git a/xhiveframework/desk/doctype/onboarding_step/test_onboarding_step.py b/xhiveframework/desk/doctype/onboarding_step/test_onboarding_step.py new file mode 100644 index 0000000..0f3dc98 --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step/test_onboarding_step.py @@ -0,0 +1,8 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestOnboardingStep(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/onboarding_step_map/__init__.py b/xhiveframework/desk/doctype/onboarding_step_map/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.json b/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.json new file mode 100644 index 0000000..e501a0b --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "creation": "2020-04-28 22:06:08.544187", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "step" + ], + "fields": [ + { + "fieldname": "step", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Step", + "options": "Onboarding Step", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-04-28 22:06:09.503406", + "modified_by": "Administrator", + "module": "Desk", + "name": "Onboarding Step Map", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.py b/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.py new file mode 100644 index 0000000..e2675fb --- /dev/null +++ b/xhiveframework/desk/doctype/onboarding_step_map/onboarding_step_map.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class OnboardingStepMap(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + step: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/route_history/__init__.py b/xhiveframework/desk/doctype/route_history/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/route_history/route_history.js b/xhiveframework/desk/doctype/route_history/route_history.js new file mode 100644 index 0000000..a647e27 --- /dev/null +++ b/xhiveframework/desk/doctype/route_history/route_history.js @@ -0,0 +1,6 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Route History", { + refresh: function () {}, +}); diff --git a/xhiveframework/desk/doctype/route_history/route_history.json b/xhiveframework/desk/doctype/route_history/route_history.json new file mode 100644 index 0000000..0b96277 --- /dev/null +++ b/xhiveframework/desk/doctype/route_history/route_history.json @@ -0,0 +1,54 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2018-10-05 11:26:04.601113", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "route", + "user" + ], + "fields": [ + { + "fieldname": "route", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Route", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "User", + "no_copy": 1, + "options": "User", + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2023-12-04 04:41:32.448331", + "modified_by": "Administrator", + "module": "Desk", + "name": "Route History", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "route" +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/route_history/route_history.py b/xhiveframework/desk/doctype/route_history/route_history.py new file mode 100644 index 0000000..fb87b36 --- /dev/null +++ b/xhiveframework/desk/doctype/route_history/route_history.py @@ -0,0 +1,54 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.deferred_insert import deferred_insert as _deferred_insert +from xhiveframework.model.document import Document + + +class RouteHistory(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + route: DF.Data | None + user: DF.Link | None + # end: auto-generated types + + @staticmethod + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Route History") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + +@xhiveframework.whitelist() +def deferred_insert(routes): + routes = [ + { + "user": xhiveframework.session.user, + "route": route.get("route"), + "creation": route.get("creation"), + } + for route in xhiveframework.parse_json(routes) + ] + + _deferred_insert("Route History", routes) + + +@xhiveframework.whitelist() +def frequently_visited_links(): + return xhiveframework.get_all( + "Route History", + fields=["route", "count(name) as count"], + filters={"user": xhiveframework.session.user}, + group_by="route", + order_by="count desc", + limit=5, + ) diff --git a/xhiveframework/desk/doctype/route_history/route_history_list.js b/xhiveframework/desk/doctype/route_history/route_history_list.js new file mode 100644 index 0000000..6e52d5c --- /dev/null +++ b/xhiveframework/desk/doctype/route_history/route_history_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Route History"] = { + onload: function (listview) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(cur_list.doctype); + }); + }, +}; diff --git a/xhiveframework/desk/doctype/system_console/__init__.py b/xhiveframework/desk/doctype/system_console/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/system_console/system_console.js b/xhiveframework/desk/doctype/system_console/system_console.js new file mode 100644 index 0000000..5e75e31 --- /dev/null +++ b/xhiveframework/desk/doctype/system_console/system_console.js @@ -0,0 +1,115 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("System Console", { + onload: function (frm) { + xhiveframework.ui.keys.add_shortcut({ + shortcut: "shift+enter", + action: () => frm.page.btn_primary.trigger("click"), + page: frm.page, + description: __("Execute Console script"), + ignore_inputs: true, + }); + }, + + refresh: function (frm) { + frm.disable_save(); + frm.page.set_primary_action(__("Execute"), ($btn) => { + $btn.text(__("Executing...")); + return frm + .execute_action("Execute") + .then(() => frm.trigger("render_sql_output")) + .finally(() => $btn.text(__("Execute"))); + }); + if ( + window.localStorage.getItem("system_console_code") && + window.localStorage.getItem("system_console_type") + ) { + frm.set_value("type", localStorage.getItem("system_console_type")); + frm.set_value("console", localStorage.getItem("system_console_code")); + frm.set_value("output", ""); + window.localStorage.removeItem("system_console_code"); + window.localStorage.removeItem("system_console_type"); + } + }, + + type: function (frm) { + if (frm.doc.type == "Python") { + frm.set_value("output", ""); + if (frm.sql_output) { + frm.sql_output.destroy(); + frm.get_field("sql_output").html(""); + } + } + }, + + render_sql_output: function (frm) { + if (frm.doc.type !== "SQL") return; + if (frm.sql_output) { + frm.sql_output.destroy(); + frm.get_field("sql_output").html(""); + } + + if (frm.doc.output.startsWith("Traceback")) { + return; + } + + let result = JSON.parse(frm.doc.output); + frm.set_value("output", `${result.length} ${result.length == 1 ? "row" : "rows"}`); + + if (result.length) { + let columns = Object.keys(result[0]); + frm.sql_output = new DataTable(frm.get_field("sql_output").$wrapper.get(0), { + columns, + data: result, + }); + } + }, + + show_processlist: function (frm) { + if (frm.doc.show_processlist) { + // keep refreshing every 5 seconds + frm.events.refresh_processlist(frm); + frm.processlist_interval = setInterval( + () => frm.events.refresh_processlist(frm), + 5000 + ); + } else { + if (frm.processlist_interval) { + // end it + clearInterval(frm.processlist_interval); + frm.get_field("processlist").html(""); + } + } + }, + + refresh_processlist: function (frm) { + let timestamp = new Date(); + xhiveframework + .call("xhiveframework.desk.doctype.system_console.system_console.show_processlist") + .then((r) => { + let rows = ""; + for (let row of r.message) { + rows += ` + ${row.Id} + ${row.Time} + ${row.State} + ${row.Info} + ${row.Progress} + `; + } + + frm.get_field("processlist").html(` +

    Requested on: ${timestamp}

    + + + + ${rows}`); + }); + }, +}); diff --git a/xhiveframework/desk/doctype/system_console/system_console.json b/xhiveframework/desk/doctype/system_console/system_console.json new file mode 100644 index 0000000..d367a93 --- /dev/null +++ b/xhiveframework/desk/doctype/system_console/system_console.json @@ -0,0 +1,109 @@ +{ + "actions": [ + { + "action": "/app/console-log", + "action_type": "Route", + "label": "Logs" + }, + { + "action": "xhiveframework.desk.doctype.system_console.system_console.execute_code", + "action_type": "Server Action", + "hidden": 1, + "label": "Execute" + } + ], + "creation": "2020-08-18 17:44:35.647815", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "execute_section", + "type", + "console", + "commit", + "output", + "sql_output", + "database_processes_section", + "show_processlist", + "processlist" + ], + "fields": [ + { + "description": "To print output use log(text)", + "fieldname": "console", + "fieldtype": "Code", + "label": "Console", + "options": "Python" + }, + { + "fieldname": "output", + "fieldtype": "Code", + "label": "Output", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "commit", + "fieldtype": "Check", + "label": "Commit" + }, + { + "fieldname": "execute_section", + "fieldtype": "Section Break", + "label": "Execute" + }, + { + "fieldname": "database_processes_section", + "fieldtype": "Section Break", + "label": "Database Processes" + }, + { + "default": "0", + "fieldname": "show_processlist", + "fieldtype": "Check", + "label": "Show Processlist" + }, + { + "fieldname": "processlist", + "fieldtype": "HTML", + "label": "processlist" + }, + { + "default": "Python", + "fieldname": "type", + "fieldtype": "Select", + "label": "Type", + "options": "Python\nSQL" + }, + { + "depends_on": "eval:doc.type == 'SQL'", + "fieldname": "sql_output", + "fieldtype": "HTML", + "label": "SQL Output" + } + ], + "hide_toolbar": 1, + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2022-04-15 14:15:58.398590", + "modified_by": "Administrator", + "module": "Desk", + "name": "System Console", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/desk/doctype/system_console/system_console.py b/xhiveframework/desk/doctype/system_console/system_console.py new file mode 100644 index 0000000..08dcad5 --- /dev/null +++ b/xhiveframework/desk/doctype/system_console/system_console.py @@ -0,0 +1,73 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.utils.safe_exec import read_sql, safe_exec + + +class SystemConsole(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + commit: DF.Check + console: DF.Code | None + output: DF.Code | None + show_processlist: DF.Check + type: DF.Literal["Python", "SQL"] + + # end: auto-generated types + def run(self): + xhiveframework.only_for("System Manager") + try: + xhiveframework.local.debug_log = [] + if self.type == "Python": + safe_exec(self.console, script_filename="System Console") + self.output = "\n".join(xhiveframework.debug_log) + elif self.type == "SQL": + self.output = xhiveframework.as_json(read_sql(self.console, as_dict=1)) + except Exception: + self.commit = False + self.output = xhiveframework.get_traceback() + + if self.commit: + xhiveframework.db.commit() + else: + xhiveframework.db.rollback() + xhiveframework.get_doc( + dict(doctype="Console Log", script=self.console, type=self.type, committed=self.commit) + ).insert() + xhiveframework.db.commit() + + +@xhiveframework.whitelist() +def execute_code(doc): + console = xhiveframework.get_doc(json.loads(doc)) + console.run() + return console.as_dict() + + +@xhiveframework.whitelist() +def show_processlist(): + xhiveframework.only_for("System Manager") + + return xhiveframework.db.multisql( + { + "postgres": """ + SELECT pid AS "Id", + query_start AS "Time", + state AS "State", + query AS "Info", + wait_event AS "Progress" + FROM pg_stat_activity""", + "mariadb": "show full processlist", + }, + as_dict=True, + ) diff --git a/xhiveframework/desk/doctype/system_console/test_system_console.py b/xhiveframework/desk/doctype/system_console/test_system_console.py new file mode 100644 index 0000000..f2c8cc6 --- /dev/null +++ b/xhiveframework/desk/doctype/system_console/test_system_console.py @@ -0,0 +1,36 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSystemConsole(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls) -> None: + cls.enable_safe_exec() + return super().setUpClass() + + def test_system_console(self): + system_console = xhiveframework.get_doc("System Console") + system_console.console = 'log("hello")' + system_console.run() + + self.assertEqual(system_console.output, "hello") + + system_console.console = 'log(xhiveframework.db.get_value("DocType", "DocType", "module"))' + system_console.run() + + self.assertEqual(system_console.output, "Core") + + def test_system_console_sql(self): + system_console = xhiveframework.get_doc("System Console") + system_console.type = "SQL" + system_console.console = "select 'test'" + system_console.run() + + self.assertIn("test", system_console.output) + + system_console.console = "update `tabDocType` set is_virtual = 1 where name = 'xyz'" + system_console.run() + + self.assertIn("PermissionError", system_console.output) diff --git a/xhiveframework/desk/doctype/tag/__init__.py b/xhiveframework/desk/doctype/tag/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/tag/tag.js b/xhiveframework/desk/doctype/tag/tag.js new file mode 100644 index 0000000..00a9f47 --- /dev/null +++ b/xhiveframework/desk/doctype/tag/tag.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Tag", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/desk/doctype/tag/tag.json b/xhiveframework/desk/doctype/tag/tag.json new file mode 100644 index 0000000..ad9838d --- /dev/null +++ b/xhiveframework/desk/doctype/tag/tag.json @@ -0,0 +1,50 @@ +{ + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2016-05-25 09:43:44.767581", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "description" + ], + "fields": [ + { + "fieldname": "description", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Description" + } + ], + "modified": "2019-09-25 17:47:41.712237", + "modified_by": "Administrator", + "module": "Desk", + "name": "Tag", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} diff --git a/xhiveframework/desk/doctype/tag/tag.py b/xhiveframework/desk/doctype/tag/tag.py new file mode 100644 index 0000000..e5a05f3 --- /dev/null +++ b/xhiveframework/desk/doctype/tag/tag.py @@ -0,0 +1,205 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.query_builder import DocType +from xhiveframework.utils import unique + + +class Tag(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + description: DF.SmallText | None + # end: auto-generated types + pass + + +def check_user_tags(dt): + "if the user does not have a tags column, then it creates one" + try: + doctype = DocType(dt) + xhiveframework.qb.from_(doctype).select(doctype._user_tags).limit(1).run() + except Exception as e: + if xhiveframework.db.is_column_missing(e): + DocTags(dt).setup() + + +@xhiveframework.whitelist() +def add_tag(tag, dt, dn, color=None): + "adds a new tag to a record, and creates the Tag master" + DocTags(dt).add(dn, tag) + + return tag + + +@xhiveframework.whitelist() +def add_tags(tags, dt, docs, color=None): + "adds a new tag to a record, and creates the Tag master" + tags = xhiveframework.parse_json(tags) + docs = xhiveframework.parse_json(docs) + for doc in docs: + for tag in tags: + DocTags(dt).add(doc, tag) + + +@xhiveframework.whitelist() +def remove_tag(tag, dt, dn): + "removes tag from the record" + DocTags(dt).remove(dn, tag) + + +@xhiveframework.whitelist() +def get_tagged_docs(doctype, tag): + xhiveframework.has_permission(doctype, throw=True) + doctype = DocType(doctype) + return (xhiveframework.qb.from_(doctype).where(doctype._user_tags.like(tag)).select(doctype.name)).run() + + +@xhiveframework.whitelist() +def get_tags(doctype, txt): + tag = xhiveframework.get_list("Tag", filters=[["name", "like", f"%{txt}%"]]) + tags = [t.name for t in tag] + + return sorted(filter(lambda t: t and txt.casefold() in t.casefold(), list(set(tags)))) + + +class DocTags: + """Tags for a particular doctype""" + + def __init__(self, dt): + self.dt = dt + + def get_tag_fields(self): + """returns tag_fields property""" + return xhiveframework.db.get_value("DocType", self.dt, "tag_fields") + + def get_tags(self, dn): + """returns tag for a particular item""" + return (xhiveframework.db.get_value(self.dt, dn, "_user_tags", ignore=1) or "").strip() + + def add(self, dn, tag): + """add a new user tag""" + tl = self.get_tags(dn).split(",") + if tag not in tl: + tl.append(tag) + if not xhiveframework.db.exists("Tag", tag): + xhiveframework.get_doc({"doctype": "Tag", "name": tag}).insert(ignore_permissions=True) + self.update(dn, tl) + + def remove(self, dn, tag): + """remove a user tag""" + tl = self.get_tags(dn).split(",") + self.update(dn, filter(lambda x: x.lower() != tag.lower(), tl)) + + def remove_all(self, dn): + """remove all user tags (call before delete)""" + self.update(dn, []) + + def update(self, dn, tl): + """updates the _user_tag column in the table""" + + if not tl: + tags = "" + else: + tl = unique(filter(lambda x: x, tl)) + tags = "," + ",".join(tl) + try: + xhiveframework.db.sql( + "update `tab{}` set _user_tags={} where name={}".format(self.dt, "%s", "%s"), (tags, dn) + ) + doc = xhiveframework.get_doc(self.dt, dn) + update_tags(doc, tags) + except Exception as e: + if xhiveframework.db.is_column_missing(e): + if not tags: + # no tags, nothing to do + return + + self.setup() + self.update(dn, tl) + else: + raise + + def setup(self): + """adds the _user_tags column if not exists""" + from xhiveframework.database.schema import add_column + + add_column(self.dt, "_user_tags", "Data") + + +def delete_tags_for_document(doc): + """ + Delete the Tag Link entry of a document that has + been deleted + :param doc: Deleted document + """ + if not xhiveframework.db.table_exists("Tag Link"): + return + + xhiveframework.db.delete("Tag Link", {"document_type": doc.doctype, "document_name": doc.name}) + + +def update_tags(doc, tags): + """Adds tags for documents + + :param doc: Document to be added to global tags + """ + doc.check_permission("write") + new_tags = {tag.strip() for tag in tags.split(",") if tag} + existing_tags = [ + tag.tag + for tag in xhiveframework.get_list( + "Tag Link", filters={"document_type": doc.doctype, "document_name": doc.name}, fields=["tag"] + ) + ] + + added_tags = set(new_tags) - set(existing_tags) + for tag in added_tags: + xhiveframework.get_doc( + { + "doctype": "Tag Link", + "document_type": doc.doctype, + "document_name": doc.name, + "title": doc.get_title() or "", + "tag": tag, + } + ).insert(ignore_permissions=True) + + deleted_tags = list(set(existing_tags) - set(new_tags)) + for tag in deleted_tags: + xhiveframework.db.delete("Tag Link", {"document_type": doc.doctype, "document_name": doc.name, "tag": tag}) + + +@xhiveframework.whitelist() +def get_documents_for_tag(tag): + """ + Search for given text in Tag Link + :param tag: tag to be searched + """ + # remove hastag `#` from tag + tag = tag[1:] + + result = xhiveframework.get_list( + "Tag Link", filters={"tag": tag}, fields=["document_type", "document_name", "title", "tag"] + ) + + return [ + { + "doctype": res.document_type, + "name": res.document_name, + "content": res.title, + } + for res in result + ] + + +@xhiveframework.whitelist() +def get_tags_list_for_awesomebar(): + return xhiveframework.get_list("Tag", pluck="name", order_by=None) diff --git a/xhiveframework/desk/doctype/tag/test_tag.py b/xhiveframework/desk/doctype/tag/test_tag.py new file mode 100644 index 0000000..86f0513 --- /dev/null +++ b/xhiveframework/desk/doctype/tag/test_tag.py @@ -0,0 +1,34 @@ +import xhiveframework +from xhiveframework.desk.doctype.tag.tag import add_tag +from xhiveframework.desk.reportview import get_stats +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestTag(XhiveFrameworkTestCase): + def setUp(self) -> None: + xhiveframework.db.delete("Tag") + xhiveframework.db.sql("UPDATE `tabDocType` set _user_tags=''") + + def test_tag_count_query(self): + self.assertDictEqual( + get_stats('["_user_tags"]', "DocType"), + {"_user_tags": [["No Tags", xhiveframework.db.count("DocType")]]}, + ) + add_tag("Standard", "DocType", "User") + add_tag("Standard", "DocType", "ToDo") + + # count with no filter + self.assertDictEqual( + get_stats('["_user_tags"]', "DocType"), + {"_user_tags": [["Standard", 2], ["No Tags", xhiveframework.db.count("DocType") - 2]]}, + ) + + # count with child table field filter + self.assertDictEqual( + get_stats( + '["_user_tags"]', + "DocType", + filters='[["DocField", "fieldname", "like", "%last_name%"], ["DocType", "name", "like", "%use%"]]', + ), + {"_user_tags": [["Standard", 1], ["No Tags", 0]]}, + ) diff --git a/xhiveframework/desk/doctype/tag_link/__init__.py b/xhiveframework/desk/doctype/tag_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/tag_link/tag_link.js b/xhiveframework/desk/doctype/tag_link/tag_link.js new file mode 100644 index 0000000..1d99b43 --- /dev/null +++ b/xhiveframework/desk/doctype/tag_link/tag_link.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Tag Link", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/desk/doctype/tag_link/tag_link.json b/xhiveframework/desk/doctype/tag_link/tag_link.json new file mode 100644 index 0000000..9142279 --- /dev/null +++ b/xhiveframework/desk/doctype/tag_link/tag_link.json @@ -0,0 +1,83 @@ +{ + "actions": [], + "creation": "2019-09-24 13:25:36.435685", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "document_name", + "tag", + "title" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Document Title", + "read_only": 1 + }, + { + "fieldname": "tag", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Tag", + "options": "Tag", + "read_only": 1 + }, + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "document_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Name", + "options": "document_type", + "read_only": 1 + } + ], + "links": [], + "modified": "2021-09-20 16:53:37.217998", + "modified_by": "Administrator", + "module": "Desk", + "name": "Tag Link", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/tag_link/tag_link.py b/xhiveframework/desk/doctype/tag_link/tag_link.py new file mode 100644 index 0000000..1936f40 --- /dev/null +++ b/xhiveframework/desk/doctype/tag_link/tag_link.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class TagLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document_name: DF.DynamicLink | None + document_type: DF.Link | None + tag: DF.Link | None + title: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/tag_link/test_tag_link.py b/xhiveframework/desk/doctype/tag_link/test_tag_link.py new file mode 100644 index 0000000..8a2b49b --- /dev/null +++ b/xhiveframework/desk/doctype/tag_link/test_tag_link.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestTagLink(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/desk/doctype/todo/README.md b/xhiveframework/desk/doctype/todo/README.md new file mode 100644 index 0000000..b622358 --- /dev/null +++ b/xhiveframework/desk/doctype/todo/README.md @@ -0,0 +1 @@ +To do or assignment. \ No newline at end of file diff --git a/xhiveframework/desk/doctype/todo/__init__.py b/xhiveframework/desk/doctype/todo/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/desk/doctype/todo/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/desk/doctype/todo/test_todo.py b/xhiveframework/desk/doctype/todo/test_todo.py new file mode 100644 index 0000000..0041a00 --- /dev/null +++ b/xhiveframework/desk/doctype/todo/test_todo.py @@ -0,0 +1,153 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.core.doctype.doctype.doctype import clear_permissions_cache +from xhiveframework.model.db_query import DatabaseQuery +from xhiveframework.permissions import add_permission, reset_perms +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["User"] + + +class TestToDo(XhiveFrameworkTestCase): + def test_delete(self): + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test todo", assigned_by="Administrator") + ).insert() + + xhiveframework.db.delete("Deleted Document") + todo.delete() + + deleted = xhiveframework.get_doc( + "Deleted Document", dict(deleted_doctype=todo.doctype, deleted_name=todo.name) + ) + self.assertEqual(todo.as_json(), deleted.data) + + def test_fetch(self): + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test todo", assigned_by="Administrator") + ).insert() + self.assertEqual( + todo.assigned_by_full_name, xhiveframework.db.get_value("User", todo.assigned_by, "full_name") + ) + + def test_fetch_setup(self): + xhiveframework.db.delete("ToDo") + + todo_meta = xhiveframework.get_doc("DocType", "ToDo") + todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[0].fetch_from = "" + todo_meta.save() + + xhiveframework.clear_cache(doctype="ToDo") + + todo = xhiveframework.get_doc( + dict(doctype="ToDo", description="test todo", assigned_by="Administrator") + ).insert() + self.assertFalse(todo.assigned_by_full_name) + + todo_meta = xhiveframework.get_doc("DocType", "ToDo") + todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[ + 0 + ].fetch_from = "assigned_by.full_name" + todo_meta.save() + + todo.reload() + + self.assertEqual( + todo.assigned_by_full_name, xhiveframework.db.get_value("User", todo.assigned_by, "full_name") + ) + + def test_todo_list_access(self): + create_new_todo("Test1", "testperm@example.com") + + xhiveframework.set_user("test4@example.com") + create_new_todo("Test2", "test4@example.com") + test_user_data = DatabaseQuery("ToDo").execute() + + xhiveframework.set_user("testperm@example.com") + system_manager_data = DatabaseQuery("ToDo").execute() + + self.assertNotEqual(test_user_data, system_manager_data) + + xhiveframework.set_user("Administrator") + xhiveframework.db.rollback() + + def test_doc_read_access(self): + # owner and assigned_by is testperm + todo1 = create_new_todo("Test1", "testperm@example.com") + test_user = xhiveframework.get_doc("User", "test4@example.com") + + # owner is testperm, but assigned_by is test4 + todo2 = create_new_todo("Test2", "test4@example.com") + + xhiveframework.set_user("test4@example.com") + # owner and assigned_by is test4 + todo3 = create_new_todo("Test3", "test4@example.com") + + # user without any role to read or write todo document + self.assertFalse(todo1.has_permission("read")) + self.assertFalse(todo1.has_permission("write")) + + # user without any role but he/she is assigned_by of that todo document + self.assertTrue(todo2.has_permission("read")) + self.assertTrue(todo2.has_permission("write")) + + # user is the owner and assigned_by of the todo document + self.assertTrue(todo3.has_permission("read")) + self.assertTrue(todo3.has_permission("write")) + + xhiveframework.set_user("Administrator") + + test_user.add_roles("Blogger") + add_permission("ToDo", "Blogger") + + xhiveframework.set_user("test4@example.com") + + # user with only read access to todo document, not an owner or assigned_by + self.assertTrue(todo1.has_permission("read")) + self.assertFalse(todo1.has_permission("write")) + + xhiveframework.set_user("Administrator") + test_user.remove_roles("Blogger") + reset_perms("ToDo") + clear_permissions_cache("ToDo") + xhiveframework.db.rollback() + + def test_fetch_if_empty(self): + xhiveframework.db.delete("ToDo") + + # Allow user changes + todo_meta = xhiveframework.get_doc("DocType", "ToDo") + field = todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[0] + field.fetch_from = "assigned_by.full_name" + field.fetch_if_empty = 1 + todo_meta.save() + + xhiveframework.clear_cache(doctype="ToDo") + + todo = xhiveframework.get_doc( + dict( + doctype="ToDo", + description="test todo", + assigned_by="Administrator", + assigned_by_full_name="Admin", + ) + ).insert() + + self.assertEqual(todo.assigned_by_full_name, "Admin") + + # Overwrite user changes + todo.meta.get("fields", dict(fieldname="assigned_by_full_name"))[0].fetch_if_empty = 0 + todo.meta.save() + + todo.reload() + todo.save() + + self.assertEqual( + todo.assigned_by_full_name, xhiveframework.db.get_value("User", todo.assigned_by, "full_name") + ) + + +def create_new_todo(description, assigned_by): + todo = {"doctype": "ToDo", "description": description, "assigned_by": assigned_by} + return xhiveframework.get_doc(todo).insert() diff --git a/xhiveframework/desk/doctype/todo/todo.js b/xhiveframework/desk/doctype/todo/todo.js new file mode 100644 index 0000000..c77c7aa --- /dev/null +++ b/xhiveframework/desk/doctype/todo/todo.js @@ -0,0 +1,55 @@ +// bind events + +xhiveframework.ui.form.on("ToDo", { + onload: function (frm) { + frm.set_query("reference_type", function (txt) { + return { + filters: { + issingle: 0, + }, + }; + }); + }, + refresh: function (frm) { + if (frm.doc.reference_type && frm.doc.reference_name) { + frm.add_custom_button(__(frm.doc.reference_name), function () { + xhiveframework.set_route("Form", frm.doc.reference_type, frm.doc.reference_name); + }); + } + + if (!frm.doc.__islocal) { + if (frm.doc.status !== "Closed") { + frm.add_custom_button( + __("Close"), + function () { + frm.set_value("status", "Closed"); + frm.save(null, function () { + // back to list + xhiveframework.set_route("List", "ToDo"); + }); + }, + "fa fa-check", + "btn-success" + ); + } else { + frm.add_custom_button( + __("Reopen"), + function () { + frm.set_value("status", "Open"); + frm.save(); + }, + null, + "btn-default" + ); + } + frm.add_custom_button( + __("New"), + function () { + xhiveframework.new_doc("ToDo"); + }, + null, + "btn-default" + ); + } + }, +}); diff --git a/xhiveframework/desk/doctype/todo/todo.json b/xhiveframework/desk/doctype/todo/todo.json new file mode 100644 index 0000000..c3b534d --- /dev/null +++ b/xhiveframework/desk/doctype/todo/todo.json @@ -0,0 +1,202 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2012-07-03 13:30:35", + "doctype": "DocType", + "document_type": "Setup", + "email_append_to": 1, + "engine": "InnoDB", + "field_order": [ + "description_and_status", + "status", + "priority", + "column_break_2", + "color", + "date", + "allocated_to", + "description_section", + "description", + "section_break_6", + "reference_type", + "reference_name", + "column_break_10", + "role", + "assigned_by", + "assigned_by_full_name", + "sender", + "assignment_rule" + ], + "fields": [ + { + "fieldname": "description_and_status", + "fieldtype": "Section Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nClosed\nCancelled" + }, + { + "default": "Medium", + "fieldname": "priority", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Priority", + "oldfieldname": "priority", + "oldfieldtype": "Data", + "options": "High\nMedium\nLow" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "color", + "fieldtype": "Color", + "label": "Color" + }, + { + "allow_in_quick_entry": 1, + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Due Date", + "oldfieldname": "date", + "oldfieldtype": "Date" + }, + { + "fieldname": "description_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_global_search": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "print_width": "300px", + "reqd": 1, + "width": "300px" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "reference_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Type", + "oldfieldname": "reference_type", + "oldfieldtype": "Data", + "options": "DocType" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "oldfieldname": "reference_name", + "oldfieldtype": "Data", + "options": "reference_type" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "role", + "fieldtype": "Link", + "label": "Role", + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role" + }, + { + "fieldname": "assigned_by", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Assigned By", + "options": "User" + }, + { + "fetch_from": "assigned_by.full_name", + "fieldname": "assigned_by_full_name", + "fieldtype": "Read Only", + "label": "Assigned By Full Name" + }, + { + "fieldname": "sender", + "fieldtype": "Data", + "hidden": 1, + "label": "Sender", + "options": "Email" + }, + { + "fieldname": "assignment_rule", + "fieldtype": "Link", + "label": "Assignment Rule", + "options": "Assignment Rule", + "read_only": 1 + }, + { + "fieldname": "allocated_to", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Allocated To", + "options": "User" + } + ], + "icon": "fa fa-check", + "idx": 2, + "links": [], + "modified": "2023-10-05 07:44:38.476400", + "modified_by": "Administrator", + "module": "Desk", + "name": "ToDo", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "description, reference_type, reference_name", + "sender_field": "sender", + "sort_field": "modified", + "sort_order": "DESC", + "subject_field": "description", + "title_field": "description", + "track_changes": 1, + "track_seen": 1 +} diff --git a/xhiveframework/desk/doctype/todo/todo.py b/xhiveframework/desk/doctype/todo/todo.py new file mode 100644 index 0000000..ced6462 --- /dev/null +++ b/xhiveframework/desk/doctype/todo/todo.py @@ -0,0 +1,174 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.permissions import AUTOMATIC_ROLES +from xhiveframework.utils import get_fullname, parse_addr + +exclude_from_linked_with = True + + +class ToDo(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + allocated_to: DF.Link | None + assigned_by: DF.Link | None + assigned_by_full_name: DF.ReadOnly | None + assignment_rule: DF.Link | None + color: DF.Color | None + date: DF.Date | None + description: DF.TextEditor + priority: DF.Literal["High", "Medium", "Low"] + reference_name: DF.DynamicLink | None + reference_type: DF.Link | None + role: DF.Link | None + sender: DF.Data | None + status: DF.Literal["Open", "Closed", "Cancelled"] + # end: auto-generated types + DocType = "ToDo" + + def validate(self): + self._assignment = None + if self.is_new(): + if self.assigned_by == self.allocated_to: + assignment_message = xhiveframework._("{0} self assigned this task: {1}").format( + get_fullname(self.assigned_by), self.description + ) + else: + assignment_message = xhiveframework._("{0} assigned {1}: {2}").format( + get_fullname(self.assigned_by), get_fullname(self.allocated_to), self.description + ) + + self._assignment = {"text": assignment_message, "comment_type": "Assigned"} + + else: + # NOTE the previous value is only available in validate method + if self.get_db_value("status") != self.status: + if self.allocated_to == xhiveframework.session.user: + removal_message = xhiveframework._("{0} removed their assignment.").format( + get_fullname(xhiveframework.session.user) + ) + else: + removal_message = xhiveframework._("Assignment of {0} removed by {1}").format( + get_fullname(self.allocated_to), get_fullname(xhiveframework.session.user) + ) + + self._assignment = {"text": removal_message, "comment_type": "Assignment Completed"} + + def on_update(self): + if self._assignment: + self.add_assign_comment(**self._assignment) + + self.update_in_reference() + + def on_trash(self): + self.delete_communication_links() + self.update_in_reference() + + def add_assign_comment(self, text, comment_type): + if not (self.reference_type and self.reference_name): + return + + xhiveframework.get_doc(self.reference_type, self.reference_name).add_comment(comment_type, text) + + def delete_communication_links(self): + # unlink todo from linked comments + return xhiveframework.db.delete("Communication Link", {"link_doctype": self.doctype, "link_name": self.name}) + + def update_in_reference(self): + if not (self.reference_type and self.reference_name): + return + + try: + assignments = xhiveframework.get_all( + "ToDo", + filters={ + "reference_type": self.reference_type, + "reference_name": self.reference_name, + "status": ("not in", ("Cancelled", "Closed")), + "allocated_to": ("is", "set"), + }, + pluck="allocated_to", + ) + assignments.reverse() + + if xhiveframework.get_meta(self.reference_type).issingle: + xhiveframework.db.set_single_value( + self.reference_type, + "_assign", + json.dumps(assignments), + update_modified=False, + ) + else: + xhiveframework.db.set_value( + self.reference_type, + self.reference_name, + "_assign", + json.dumps(assignments), + update_modified=False, + ) + + except Exception as e: + if xhiveframework.db.is_table_missing(e) and xhiveframework.flags.in_install: + # no table + return + + elif xhiveframework.db.is_column_missing(e): + from xhiveframework.database.schema import add_column + + add_column(self.reference_type, "_assign", "Text") + self.update_in_reference() + + else: + raise + + @classmethod + def get_owners(cls, filters=None): + """Returns list of owners after applying filters on todo's.""" + rows = xhiveframework.get_all(cls.DocType, filters=filters or {}, fields=["allocated_to"]) + return [parse_addr(row.allocated_to)[1] for row in rows if row.allocated_to] + + +# NOTE: todo is viewable if a user is an owner, or set as assigned_to value, or has any role that is allowed to access ToDo doctype. +def on_doctype_update(): + xhiveframework.db.add_index("ToDo", ["reference_type", "reference_name"]) + + +def get_permission_query_conditions(user): + if not user: + user = xhiveframework.session.user + + todo_roles = xhiveframework.permissions.get_doctype_roles("ToDo") + todo_roles = set(todo_roles) - set(AUTOMATIC_ROLES) + + if any(check in todo_roles for check in xhiveframework.get_roles(user)): + return None + else: + return """(`tabToDo`.allocated_to = {user} or `tabToDo`.assigned_by = {user})""".format( + user=xhiveframework.db.escape(user) + ) + + +def has_permission(doc, ptype="read", user=None): + user = user or xhiveframework.session.user + todo_roles = xhiveframework.permissions.get_doctype_roles("ToDo", ptype) + todo_roles = set(todo_roles) - set(AUTOMATIC_ROLES) + + if any(check in todo_roles for check in xhiveframework.get_roles(user)): + return True + else: + return doc.allocated_to == user or doc.assigned_by == user + + +@xhiveframework.whitelist() +def new_todo(description): + xhiveframework.get_doc({"doctype": "ToDo", "description": description}).insert() diff --git a/xhiveframework/desk/doctype/todo/todo_calendar.js b/xhiveframework/desk/doctype/todo/todo_calendar.js new file mode 100644 index 0000000..4248b16 --- /dev/null +++ b/xhiveframework/desk/doctype/todo/todo_calendar.js @@ -0,0 +1,29 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +xhiveframework.views.calendar["ToDo"] = { + field_map: { + start: "date", + end: "date", + id: "name", + title: "description", + allDay: "allDay", + progress: "progress", + }, + gantt: true, + filters: [ + { + fieldtype: "Link", + fieldname: "reference_type", + options: "Task", + label: __("Task"), + }, + { + fieldtype: "Dynamic Link", + fieldname: "reference_name", + options: "reference_type", + label: __("Task"), + }, + ], + get_events_method: "xhiveframework.desk.calendar.get_events", +}; diff --git a/xhiveframework/desk/doctype/todo/todo_list.js b/xhiveframework/desk/doctype/todo/todo_list.js new file mode 100644 index 0000000..8b1387a --- /dev/null +++ b/xhiveframework/desk/doctype/todo/todo_list.js @@ -0,0 +1,29 @@ +xhiveframework.listview_settings["ToDo"] = { + hide_name_column: true, + add_fields: ["reference_type", "reference_name"], + + onload: function (me) { + if (!Object.keys(xhiveframework.route_options).length) { + xhiveframework.route_options = { + allocated_to: xhiveframework.session.user, + status: "Open", + }; + } + me.page.set_title(__("To Do")); + }, + + button: { + show: function (doc) { + return doc.reference_name; + }, + get_label: function () { + return __("Open", null, "Access"); + }, + get_description: function (doc) { + return __("Open {0}", [`${__(doc.reference_type)}: ${doc.reference_name}`]); + }, + action: function (doc) { + xhiveframework.set_route("Form", doc.reference_type, doc.reference_name); + }, + }, +}; diff --git a/xhiveframework/desk/doctype/workspace/__init__.py b/xhiveframework/desk/doctype/workspace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace/test_workspace.py b/xhiveframework/desk/doctype/workspace/test_workspace.py new file mode 100644 index 0000000..fab7270 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace/test_workspace.py @@ -0,0 +1,98 @@ +# Copyright (c) 2020, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestWorkspace(XhiveFrameworkTestCase): + def setUp(self): + create_module("Test Module") + + def tearDown(self): + xhiveframework.db.delete("Workspace", {"module": "Test Module"}) + xhiveframework.db.delete("DocType", {"module": "Test Module"}) + xhiveframework.delete_doc("Module Def", "Test Module") + + # TODO: FIX ME - flaky test!!! + # def test_workspace_with_cards_specific_to_a_country(self): + # workspace = create_workspace() + # insert_card(workspace, "Card Label 1", "DocType 1", "DocType 2", "France") + # insert_card(workspace, "Card Label 2", "DocType A", "DocType B") + + # workspace.insert(ignore_if_duplicate = True) + + # cards = workspace.get_link_groups() + + # if xhiveframework.get_system_settings('country') == "France": + # self.assertEqual(len(cards), 2) + # else: + # self.assertEqual(len(cards), 1) + + +def create_module(module_name): + module = xhiveframework.get_doc({"doctype": "Module Def", "module_name": module_name, "app_name": "xhiveframework"}) + module.insert(ignore_if_duplicate=True) + + return module + + +def create_workspace(**args): + workspace = xhiveframework.new_doc("Workspace") + args = xhiveframework._dict(args) + + workspace.name = args.name or "Test Workspace" + workspace.label = args.label or "Test Workspace" + workspace.category = args.category or "Modules" + workspace.is_standard = args.is_standard or 1 + workspace.module = "Test Module" + + return workspace + + +def insert_card(workspace, card_label, doctype1, doctype2, country=None): + workspace.append("links", {"type": "Card Break", "label": card_label, "only_for": country}) + + create_doctype(doctype1, "Test Module") + workspace.append( + "links", + { + "type": "Link", + "label": doctype1, + "only_for": country, + "link_type": "DocType", + "link_to": doctype1, + }, + ) + + create_doctype(doctype2, "Test Module") + workspace.append( + "links", + { + "type": "Link", + "label": doctype2, + "only_for": country, + "link_type": "DocType", + "link_to": doctype2, + }, + ) + + +def create_doctype(doctype_name, module): + xhiveframework.get_doc( + { + "doctype": "DocType", + "name": doctype_name, + "module": module, + "custom": 1, + "autoname": "field:title", + "fields": [ + {"label": "Title", "fieldname": "title", "reqd": 1, "fieldtype": "Data"}, + {"label": "Description", "fieldname": "description", "fieldtype": "Small Text"}, + {"label": "Date", "fieldname": "date", "fieldtype": "Date"}, + {"label": "Duration", "fieldname": "duration", "fieldtype": "Duration"}, + {"label": "Number", "fieldname": "number", "fieldtype": "Int"}, + {"label": "Number", "fieldname": "another_number", "fieldtype": "Int"}, + ], + "permissions": [{"role": "System Manager"}], + } + ).insert(ignore_if_duplicate=True) diff --git a/xhiveframework/desk/doctype/workspace/workspace.js b/xhiveframework/desk/doctype/workspace/workspace.js new file mode 100644 index 0000000..24818f2 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace/workspace.js @@ -0,0 +1,59 @@ +// Copyright (c) 2020, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Workspace", { + setup: function () { + xhiveframework.meta.get_field("Workspace Link", "only_for").no_default = true; + }, + + refresh: function (frm) { + frm.enable_save(); + + let url = `/app/${ + frm.doc.public + ? xhiveframework.router.slug(frm.doc.title) + : "private/" + xhiveframework.router.slug(frm.doc.title) + }`; + frm.sidebar + .add_user_action(__("Go to Workspace")) + .attr("href", url) + .attr("target", "_blank"); + + frm.layout.message.empty(); + let message = __( + "This document allows you to edit limited fields. For all kinds of workspace customization, use the Edit button located on the workspace page" + ); + + if ( + frm.doc.for_user || + (frm.doc.public && + !frm.has_perm("write") && + !xhiveframework.user.has_role("Workspace Manager")) + ) { + frm.trigger("disable_form"); + + if (frm.doc.public) { + message = __("Only Workspace Manager can edit public workspaces"); + } else { + message = __( + "We do not allow editing of this document. Simply click the Edit button on the workspace page to make your workspace editable and customize it as you wish" + ); + } + } + + if (xhiveframework.boot.developer_mode) { + frm.set_df_property("module", "read_only", 0); + } + + frm.layout.show_message(message); + }, + + disable_form: function (frm) { + frm.fields + .filter((field) => field.has_input) + .forEach((field) => { + frm.set_df_property(field.df.fieldname, "read_only", "1"); + }); + frm.disable_save(); + }, +}); diff --git a/xhiveframework/desk/doctype/workspace/workspace.json b/xhiveframework/desk/doctype/workspace/workspace.json new file mode 100644 index 0000000..e4f58b5 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace/workspace.json @@ -0,0 +1,246 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:label", + "beta": 1, + "creation": "2020-01-23 13:45:59.470592", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "label", + "title", + "sequence_id", + "for_user", + "parent_page", + "module", + "column_break_3", + "icon", + "indicator_color", + "restrict_to_domain", + "hide_custom", + "public", + "is_hidden", + "content", + "number_cards_tab", + "number_cards", + "tab_break_2", + "charts", + "tab_break_15", + "shortcuts", + "tab_break_18", + "links", + "quick_lists_tab", + "quick_lists", + "custom_blocks_tab", + "custom_blocks", + "roles_tab", + "roles" + ], + "fields": [ + { + "fieldname": "label", + "fieldtype": "Data", + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "charts", + "fieldname": "tab_break_2", + "fieldtype": "Tab Break", + "label": "Dashboards" + }, + { + "fieldname": "charts", + "fieldtype": "Table", + "label": "Charts", + "options": "Workspace Chart" + }, + { + "fieldname": "shortcuts", + "fieldtype": "Table", + "label": "Shortcuts", + "options": "Workspace Shortcut" + }, + { + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict to Domain", + "options": "Domain", + "search_index": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Module", + "options": "Module Def", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "shortcuts", + "fieldname": "tab_break_15", + "fieldtype": "Tab Break", + "label": "Shortcuts" + }, + { + "collapsible": 1, + "collapsible_depends_on": "links", + "fieldname": "tab_break_18", + "fieldtype": "Tab Break", + "label": "Link Cards" + }, + { + "fieldname": "for_user", + "fieldtype": "Data", + "label": "For User", + "read_only": 1 + }, + { + "default": "0", + "description": "Checking this will hide custom doctypes and reports cards in Links section", + "fieldname": "hide_custom", + "fieldtype": "Check", + "label": "Hide Custom DocTypes and Reports" + }, + { + "fieldname": "icon", + "fieldtype": "Icon", + "label": "Icon", + "read_only": 1 + }, + { + "fieldname": "links", + "fieldtype": "Table", + "label": "Links", + "options": "Workspace Link" + }, + { + "default": "0", + "fieldname": "public", + "fieldtype": "Check", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Public", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "parent_page", + "fieldtype": "Data", + "label": "Parent Page", + "read_only": 1 + }, + { + "default": "[]", + "fieldname": "content", + "fieldtype": "Long Text", + "hidden": 1, + "label": "Content" + }, + { + "fieldname": "sequence_id", + "fieldtype": "Float", + "label": "Sequence Id", + "read_only": 1 + }, + { + "fieldname": "roles", + "fieldtype": "Table", + "label": "Roles", + "options": "Has Role" + }, + { + "fieldname": "roles_tab", + "fieldtype": "Tab Break", + "label": "Roles" + }, + { + "fieldname": "quick_lists_tab", + "fieldtype": "Tab Break", + "label": "Quick Lists" + }, + { + "fieldname": "quick_lists", + "fieldtype": "Table", + "label": "Quick Lists", + "options": "Workspace Quick List" + }, + { + "default": "0", + "fieldname": "is_hidden", + "fieldtype": "Check", + "label": "Is Hidden" + }, + { + "fieldname": "number_cards_tab", + "fieldtype": "Tab Break", + "label": "Number Cards" + }, + { + "fieldname": "number_cards", + "fieldtype": "Table", + "label": "Number Cards", + "options": "Workspace Number Card" + }, + { + "fieldname": "custom_blocks_tab", + "fieldtype": "Tab Break", + "label": "Custom Blocks" + }, + { + "fieldname": "custom_blocks", + "fieldtype": "Table", + "label": "Custom Blocks", + "options": "Workspace Custom Block" + }, + { + "depends_on": "doc.icon", + "fieldname": "indicator_color", + "fieldtype": "Select", + "label": "Indicator Color", + "options": "green\ncyan\nblue\norange\nyellow\ngray\ngrey\nred\npink\ndarkgrey\npurple\nlight-blue" + } + ], + "in_create": 1, + "links": [], + "modified": "2023-08-25 15:04:03.419848", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Workspace Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace/workspace.py b/xhiveframework/desk/doctype/workspace/workspace.py new file mode 100644 index 0000000..6bc95d9 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace/workspace.py @@ -0,0 +1,475 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from collections import defaultdict +from json import loads + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.desktop import save_new_widget +from xhiveframework.desk.utils import validate_route_conflict +from xhiveframework.model.document import Document +from xhiveframework.model.rename_doc import rename_doc +from xhiveframework.modules.export_file import delete_folder, export_to_files +from xhiveframework.utils import strip_html + + +class Workspace(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.core.doctype.has_role.has_role import HasRole + from xhiveframework.desk.doctype.workspace_chart.workspace_chart import WorkspaceChart + from xhiveframework.desk.doctype.workspace_custom_block.workspace_custom_block import ( + WorkspaceCustomBlock, + ) + from xhiveframework.desk.doctype.workspace_link.workspace_link import WorkspaceLink + from xhiveframework.desk.doctype.workspace_number_card.workspace_number_card import WorkspaceNumberCard + from xhiveframework.desk.doctype.workspace_quick_list.workspace_quick_list import WorkspaceQuickList + from xhiveframework.desk.doctype.workspace_shortcut.workspace_shortcut import WorkspaceShortcut + from xhiveframework.types import DF + + charts: DF.Table[WorkspaceChart] + content: DF.LongText | None + custom_blocks: DF.Table[WorkspaceCustomBlock] + for_user: DF.Data | None + hide_custom: DF.Check + indicator_color: DF.Literal[ + "green", + "cyan", + "blue", + "orange", + "yellow", + "gray", + "grey", + "red", + "pink", + "darkgrey", + "purple", + "light-blue", + ] + is_hidden: DF.Check + label: DF.Data + links: DF.Table[WorkspaceLink] + module: DF.Link | None + number_cards: DF.Table[WorkspaceNumberCard] + parent_page: DF.Data | None + public: DF.Check + quick_lists: DF.Table[WorkspaceQuickList] + restrict_to_domain: DF.Link | None + roles: DF.Table[HasRole] + sequence_id: DF.Float + shortcuts: DF.Table[WorkspaceShortcut] + title: DF.Data + + # end: auto-generated types + def validate(self): + self.title = strip_html(self.title) + + if self.public and not is_workspace_manager() and not disable_saving_as_public(): + xhiveframework.throw(_("You need to be Workspace Manager to edit this document")) + if self.has_value_changed("title"): + validate_route_conflict(self.doctype, self.title) + else: + validate_route_conflict(self.doctype, self.name) + + try: + if not isinstance(loads(self.content), list): + raise + except Exception: + xhiveframework.throw(_("Content data shoud be a list")) + + def clear_cache(self): + super().clear_cache() + if self.for_user: + xhiveframework.cache.hdel("bootinfo", self.for_user) + else: + xhiveframework.cache.delete_key("bootinfo") + + def on_update(self): + if disable_saving_as_public(): + return + + if xhiveframework.conf.developer_mode and self.public: + if self.module: + export_to_files(record_list=[["Workspace", self.name]], record_module=self.module) + + if self.has_value_changed("title") or self.has_value_changed("module"): + previous = self.get_doc_before_save() + if previous and previous.get("module") and previous.get("title"): + delete_folder(previous.get("module"), "Workspace", previous.get("title")) + + def before_export(self, doc): + if doc.title != doc.label and doc.label == doc.name: + self.name = doc.name = doc.label = doc.title + + def after_delete(self): + if disable_saving_as_public(): + return + + if self.module and xhiveframework.conf.developer_mode: + delete_folder(self.module, "Workspace", self.title) + + @staticmethod + def get_module_wise_workspaces(): + workspaces = xhiveframework.get_all( + "Workspace", + fields=["name", "module"], + filters={"for_user": "", "public": 1}, + order_by="creation", + ) + + module_workspaces = defaultdict(list) + + for workspace in workspaces: + if not workspace.module: + continue + module_workspaces[workspace.module].append(workspace.name) + + return module_workspaces + + def get_link_groups(self): + cards = [] + current_card = xhiveframework._dict( + { + "label": "Link", + "type": "Card Break", + "icon": None, + "hidden": False, + } + ) + + card_links = [] + + for link in self.links: + link = link.as_dict() + if link.type == "Card Break": + if card_links and ( + not current_card.get("only_for") + or current_card.get("only_for") == xhiveframework.get_system_settings("country") + ): + current_card["links"] = card_links + cards.append(current_card) + + current_card = link + card_links = [] + elif not link.get("only_for") or link.get("only_for") == xhiveframework.get_system_settings("country"): + card_links.append(link) + + current_card["links"] = card_links + cards.append(current_card) + + return cards + + def build_links_table_from_card(self, config): + for idx, card in enumerate(config): + links = loads(card.get("links")) + + # remove duplicate before adding + for idx, link in enumerate(self.links): + if link.get("label") == card.get("label") and link.get("type") == "Card Break": + # count and set number of links for the card if link_count is 0 + if link.link_count == 0: + for count, card_link in enumerate(self.links[idx + 1 :]): + if card_link.get("type") == "Card Break": + break + link.link_count = count + 1 + + del self.links[idx : idx + link.link_count + 1] + + self.append( + "links", + { + "label": card.get("label"), + "type": "Card Break", + "icon": card.get("icon"), + "description": card.get("description"), + "hidden": card.get("hidden") or False, + "link_count": card.get("link_count"), + "idx": 1 if not self.links else self.links[-1].idx + 1, + }, + ) + + for link in links: + self.append( + "links", + { + "label": link.get("label"), + "type": "Link", + "link_type": link.get("link_type"), + "link_to": link.get("link_to"), + "onboard": link.get("onboard"), + "only_for": link.get("only_for"), + "dependencies": link.get("dependencies"), + "is_query_report": link.get("is_query_report"), + "idx": self.links[-1].idx + 1, + }, + ) + + +def disable_saving_as_public(): + return ( + xhiveframework.flags.in_install + or xhiveframework.flags.in_uninstall + or xhiveframework.flags.in_patch + or xhiveframework.flags.in_test + or xhiveframework.flags.in_fixtures + or xhiveframework.flags.in_migrate + ) + + +def get_link_type(key): + key = key.lower() + + link_type_map = {"doctype": "DocType", "page": "Page", "report": "Report"} + + if key in link_type_map: + return link_type_map[key] + + return "DocType" + + +def get_report_type(report): + report_type = xhiveframework.get_value("Report", report, "report_type") + return report_type in ["Query Report", "Script Report", "Custom Report"] + + +@xhiveframework.whitelist() +def new_page(new_page): + if not loads(new_page): + return + + page = loads(new_page) + + if page.get("public") and not is_workspace_manager(): + return + elif ( + not page.get("public") and page.get("for_user") != xhiveframework.session.user and not is_workspace_manager() + ): + xhiveframework.throw(_("Cannot create private workspace of other users"), xhiveframework.PermissionError) + + doc = xhiveframework.new_doc("Workspace") + doc.title = page.get("title") + doc.icon = page.get("icon") + doc.indicator_color = page.get("indicator_color") + doc.content = page.get("content") + doc.parent_page = page.get("parent_page") + doc.label = page.get("label") + doc.for_user = page.get("for_user") + doc.public = page.get("public") + doc.sequence_id = last_sequence_id(doc) + 1 + doc.save(ignore_permissions=True) + + return doc + + +@xhiveframework.whitelist() +def save_page(title, public, new_widgets, blocks): + public = xhiveframework.parse_json(public) + + filters = {"public": public, "label": title} + + if not public: + filters = {"for_user": xhiveframework.session.user, "label": title + "-" + xhiveframework.session.user} + pages = xhiveframework.get_all("Workspace", filters=filters) + if pages: + doc = xhiveframework.get_doc("Workspace", pages[0]) + else: + xhiveframework.throw(_("Workspace not found"), xhiveframework.DoesNotExistError) + + doc.content = blocks + doc.save(ignore_permissions=True) + + save_new_widget(doc, title, blocks, new_widgets) + + return {"name": title, "public": public, "label": doc.label} + + +@xhiveframework.whitelist() +def update_page(name, title, icon, indicator_color, parent, public): + public = xhiveframework.parse_json(public) + doc = xhiveframework.get_doc("Workspace", name) + + if not doc.get("public") and doc.get("for_user") != xhiveframework.session.user and not is_workspace_manager(): + xhiveframework.throw( + _("Need Workspace Manager role to edit private workspace of other users"), + xhiveframework.PermissionError, + ) + + if doc: + child_docs = xhiveframework.get_all("Workspace", filters={"parent_page": doc.title, "public": doc.public}) + doc.title = title + doc.icon = icon + doc.indicator_color = indicator_color + doc.parent_page = parent + if doc.public != public: + doc.sequence_id = xhiveframework.db.count("Workspace", {"public": public}, cache=True) + doc.public = public + doc.for_user = "" if public else doc.for_user or xhiveframework.session.user + doc.label = new_name = f"{title}-{doc.for_user}" if doc.for_user else title + doc.save(ignore_permissions=True) + + if name != new_name: + rename_doc("Workspace", name, new_name, force=True, ignore_permissions=True) + + # update new name and public in child pages + if child_docs: + for child in child_docs: + child_doc = xhiveframework.get_doc("Workspace", child.name) + child_doc.parent_page = doc.title + if child_doc.public != public: + child_doc.public = public + child_doc.for_user = "" if public else child_doc.for_user or xhiveframework.session.user + child_doc.label = new_child_name = ( + f"{child_doc.title}-{child_doc.for_user}" if child_doc.for_user else child_doc.title + ) + child_doc.save(ignore_permissions=True) + + if child.name != new_child_name: + rename_doc("Workspace", child.name, new_child_name, force=True, ignore_permissions=True) + + return {"name": title, "public": public, "label": new_name} + + +def hide_unhide_page(page_name: str, is_hidden: bool): + page = xhiveframework.get_doc("Workspace", page_name) + + if page.get("public") and not is_workspace_manager(): + xhiveframework.throw( + _("Need Workspace Manager role to hide/unhide public workspaces"), xhiveframework.PermissionError + ) + + if not page.get("public") and page.get("for_user") != xhiveframework.session.user and not is_workspace_manager(): + xhiveframework.throw(_("Cannot update private workspace of other users"), xhiveframework.PermissionError) + + page.is_hidden = int(is_hidden) + page.save(ignore_permissions=True) + return True + + +@xhiveframework.whitelist() +def hide_page(page_name: str): + return hide_unhide_page(page_name, 1) + + +@xhiveframework.whitelist() +def unhide_page(page_name: str): + return hide_unhide_page(page_name, 0) + + +@xhiveframework.whitelist() +def duplicate_page(page_name, new_page): + if not loads(new_page): + return + + new_page = loads(new_page) + + if new_page.get("is_public") and not is_workspace_manager(): + return + + old_doc = xhiveframework.get_doc("Workspace", page_name) + doc = xhiveframework.copy_doc(old_doc) + doc.title = new_page.get("title") + doc.icon = new_page.get("icon") + doc.indicator_color = new_page.get("indicator_color") + doc.parent_page = new_page.get("parent") or "" + doc.public = new_page.get("is_public") + doc.for_user = "" + doc.label = doc.title + doc.module = "" + if not doc.public: + doc.for_user = doc.for_user or xhiveframework.session.user + doc.label = f"{doc.title}-{doc.for_user}" + doc.name = doc.label + if old_doc.public == doc.public: + doc.sequence_id += 0.1 + else: + doc.sequence_id = last_sequence_id(doc) + 1 + doc.insert(ignore_permissions=True) + + return doc + + +@xhiveframework.whitelist() +def delete_page(page): + if not loads(page): + return + + page = loads(page) + + if page.get("public") and not is_workspace_manager(): + xhiveframework.throw( + _("Cannot delete public workspace without Workspace Manager role"), + xhiveframework.PermissionError, + ) + elif not page.get("public") and not is_workspace_manager(): + workspace_owner = xhiveframework.get_value("Workspace", page.get("name"), "for_user") + if workspace_owner != xhiveframework.session.user: + xhiveframework.throw( + _("Cannot delete private workspace of other users"), + xhiveframework.PermissionError, + ) + + if xhiveframework.db.exists("Workspace", page.get("name")): + xhiveframework.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True) + + return {"name": page.get("name"), "public": page.get("public"), "title": page.get("title")} + + +@xhiveframework.whitelist() +def sort_pages(sb_public_items, sb_private_items): + if not loads(sb_public_items) and not loads(sb_private_items): + return + + sb_public_items = loads(sb_public_items) + sb_private_items = loads(sb_private_items) + + workspace_public_pages = get_page_list(["name", "title"], {"public": 1}) + workspace_private_pages = get_page_list(["name", "title"], {"for_user": xhiveframework.session.user}) + + if sb_private_items: + return sort_page(workspace_private_pages, sb_private_items) + + if sb_public_items and is_workspace_manager(): + return sort_page(workspace_public_pages, sb_public_items) + + return False + + +def sort_page(workspace_pages, pages): + for seq, d in enumerate(pages): + for page in workspace_pages: + if page.title == d.get("title"): + doc = xhiveframework.get_doc("Workspace", page.name) + doc.sequence_id = seq + 1 + doc.parent_page = d.get("parent_page") or "" + doc.flags.ignore_links = True + doc.save(ignore_permissions=True) + break + + return True + + +def last_sequence_id(doc): + doc_exists = xhiveframework.db.exists({"doctype": "Workspace", "public": doc.public, "for_user": doc.for_user}) + + if not doc_exists: + return 0 + + return xhiveframework.get_all( + "Workspace", + fields=["sequence_id"], + filters={"public": doc.public, "for_user": doc.for_user}, + order_by="sequence_id desc", + )[0].sequence_id + + +def get_page_list(fields, filters): + return xhiveframework.get_all("Workspace", fields=fields, filters=filters, order_by="sequence_id asc") + + +def is_workspace_manager(): + return "Workspace Manager" in xhiveframework.get_roles() diff --git a/xhiveframework/desk/doctype/workspace_chart/__init__.py b/xhiveframework/desk/doctype/workspace_chart/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_chart/workspace_chart.json b/xhiveframework/desk/doctype/workspace_chart/workspace_chart.json new file mode 100644 index 0000000..0d80049 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_chart/workspace_chart.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2020-01-23 13:44:03.882158", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "chart_name", + "label" + ], + "fields": [ + { + "fieldname": "chart_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Chart Name", + "options": "Dashboard Chart", + "reqd": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label" + } + ], + "istable": 1, + "links": [], + "modified": "2021-01-12 13:13:25.781925", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Chart", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace_chart/workspace_chart.py b/xhiveframework/desk/doctype/workspace_chart/workspace_chart.py new file mode 100644 index 0000000..7a01999 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_chart/workspace_chart.py @@ -0,0 +1,23 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceChart(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + chart_name: DF.Link + label: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/workspace_custom_block/__init__.py b/xhiveframework/desk/doctype/workspace_custom_block/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.json b/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.json new file mode 100644 index 0000000..090719d --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-05-17 14:49:19.454932", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "custom_block_name", + "label" + ], + "fields": [ + { + "fieldname": "custom_block_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Custom Block Name", + "options": "Custom HTML Block" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-05-17 14:50:45.575609", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Custom Block", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.py b/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.py new file mode 100644 index 0000000..91e1f9e --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_custom_block/workspace_custom_block.py @@ -0,0 +1,23 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceCustomBlock(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + custom_block_name: DF.Link | None + label: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/workspace_link/__init__.py b/xhiveframework/desk/doctype/workspace_link/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_link/workspace_link.json b/xhiveframework/desk/doctype/workspace_link/workspace_link.json new file mode 100644 index 0000000..5f0a082 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_link/workspace_link.json @@ -0,0 +1,135 @@ +{ + "actions": [], + "creation": "2020-11-16 15:30:45.784417", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "type", + "label", + "icon", + "description", + "hidden", + "link_details_section", + "link_type", + "link_to", + "column_break_7", + "dependencies", + "only_for", + "onboard", + "is_query_report", + "link_count" + ], + "fields": [ + { + "default": "Link", + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "Link\nCard Break", + "reqd": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "depends_on": "eval:doc.type == \"Card Break\"", + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Card Break\"", + "fieldname": "hidden", + "fieldtype": "Check", + "label": "Hidden" + }, + { + "depends_on": "eval:doc.type == \"Link\"", + "fieldname": "link_details_section", + "fieldtype": "Section Break", + "label": "Link Details" + }, + { + "fieldname": "link_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Link Type", + "mandatory_depends_on": "eval:doc.type==\"Link\"", + "options": "DocType\nPage\nReport", + "read_only_depends_on": "eval:doc.type!=\"Link\"" + }, + { + "fieldname": "link_to", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Link To", + "mandatory_depends_on": "eval:doc.type==\"Link\"", + "options": "link_type", + "read_only_depends_on": "eval:doc.type!=\"Link\"" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "dependencies", + "fieldtype": "Data", + "label": "Dependencies" + }, + { + "fieldname": "only_for", + "fieldtype": "Link", + "label": "Only for", + "options": "Country" + }, + { + "default": "0", + "fieldname": "onboard", + "fieldtype": "Check", + "label": "Onboard" + }, + { + "default": "0", + "depends_on": "eval:doc.link_type == \"Report\"", + "fieldname": "is_query_report", + "fieldtype": "Check", + "label": "Is Query Report" + }, + { + "depends_on": "eval:doc.type == \"Card Break\"", + "fieldname": "link_count", + "fieldtype": "Int", + "hidden": 1, + "label": "Link Count" + }, + { + "depends_on": "eval:doc.type == \"Card Break\"", + "fieldname": "description", + "fieldtype": "HTML Editor", + "ignore_xss_filter": 1, + "label": "Description", + "max_height": "7rem" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-01-23 17:39:16.833318", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace_link/workspace_link.py b/xhiveframework/desk/doctype/workspace_link/workspace_link.py new file mode 100644 index 0000000..c4880bc --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_link/workspace_link.py @@ -0,0 +1,34 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceLink(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + dependencies: DF.Data | None + description: DF.HTMLEditor | None + hidden: DF.Check + icon: DF.Data | None + is_query_report: DF.Check + label: DF.Data + link_count: DF.Int + link_to: DF.DynamicLink | None + link_type: DF.Literal["DocType", "Page", "Report"] + onboard: DF.Check + only_for: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + type: DF.Literal["Link", "Card Break"] + # end: auto-generated types + + pass diff --git a/xhiveframework/desk/doctype/workspace_number_card/__init__.py b/xhiveframework/desk/doctype/workspace_number_card/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.json b/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.json new file mode 100644 index 0000000..f9e3865 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-02-15 01:16:26.216201", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "number_card_name", + "label" + ], + "fields": [ + { + "fieldname": "number_card_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Number Card Name", + "options": "Number Card", + "reqd": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-02-15 01:16:26.216201", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Number Card", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.py b/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.py new file mode 100644 index 0000000..0bfccf5 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_number_card/workspace_number_card.py @@ -0,0 +1,23 @@ +# Copyright (c) 2023, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceNumberCard(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + label: DF.Data | None + number_card_name: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/workspace_quick_list/__init__.py b/xhiveframework/desk/doctype/workspace_quick_list/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.json b/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.json new file mode 100644 index 0000000..1542ebe --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "creation": "2022-05-12 12:58:41.824496", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "column_break_1", + "label", + "section_break_4", + "quick_list_filter" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "column_break_1", + "fieldtype": "Column Break" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "quick_list_filter", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Quick List Filter", + "options": "JSON" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-05-12 13:48:40.617623", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Quick List", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.py b/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.py new file mode 100644 index 0000000..5ac6f1e --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_quick_list/workspace_quick_list.py @@ -0,0 +1,24 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceQuickList(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document_type: DF.Link + label: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + quick_list_filter: DF.Code | None + # end: auto-generated types + pass diff --git a/xhiveframework/desk/doctype/workspace_shortcut/__init__.py b/xhiveframework/desk/doctype/workspace_shortcut/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.json b/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.json new file mode 100644 index 0000000..9adbbb1 --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.json @@ -0,0 +1,129 @@ +{ + "actions": [], + "creation": "2020-01-23 13:44:59.248426", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "type", + "link_to", + "url", + "doc_view", + "kanban_board", + "column_break_4", + "label", + "icon", + "restrict_to_domain", + "section_break_5", + "stats_filter", + "column_break_3", + "color", + "format" + ], + "fields": [ + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "DocType\nReport\nPage\nDashboard\nURL", + "reqd": 1 + }, + { + "depends_on": "eval:doc.type != \"URL\"", + "fieldname": "link_to", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Link To", + "options": "type" + }, + { + "depends_on": "eval:doc.type == \"DocType\"", + "description": "Which view of the associated DocType should this shortcut take you to?", + "fieldname": "doc_view", + "fieldtype": "Select", + "in_list_view": 1, + "label": "DocType View", + "options": "\nList\nReport Builder\nDashboard\nTree\nNew\nCalendar\nKanban" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "reqd": 1 + }, + { + "depends_on": "eval:xhiveframework.boot.developer_mode", + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon" + }, + { + "depends_on": "eval:xhiveframework.boot.developer_mode", + "fieldname": "restrict_to_domain", + "fieldtype": "Link", + "label": "Restrict to Domain", + "options": "Domain" + }, + { + "depends_on": "eval:doc.type == \"DocType\" && xhiveframework.boot.developer_mode", + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Count Filter" + }, + { + "fieldname": "stats_filter", + "fieldtype": "Code", + "label": "Count Filter", + "options": "JSON" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "color", + "fieldtype": "Color", + "label": "Color" + }, + { + "description": "For example: {} Open", + "fieldname": "format", + "fieldtype": "Data", + "label": "Format" + }, + { + "depends_on": "eval:doc.type == \"URL\"", + "fieldname": "url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "URL" + }, + { + "depends_on": "eval:doc.doc_view == \"Kanban\"", + "fieldname": "kanban_board", + "fieldtype": "Link", + "label": "Kanban Board", + "options": "Kanban Board" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-11-27 14:13:38.489737", + "modified_by": "Administrator", + "module": "Desk", + "name": "Workspace Shortcut", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.py b/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.py new file mode 100644 index 0000000..147363c --- /dev/null +++ b/xhiveframework/desk/doctype/workspace_shortcut/workspace_shortcut.py @@ -0,0 +1,32 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WorkspaceShortcut(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + color: DF.Color | None + doc_view: DF.Literal["", "List", "Report Builder", "Dashboard", "Tree", "New", "Calendar", "Kanban"] + format: DF.Data | None + icon: DF.Data | None + kanban_board: DF.Link | None + label: DF.Data + link_to: DF.DynamicLink | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + restrict_to_domain: DF.Link | None + stats_filter: DF.Code | None + type: DF.Literal["DocType", "Report", "Page", "Dashboard", "URL"] + url: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/desk/form/__init__.py b/xhiveframework/desk/form/__init__.py new file mode 100644 index 0000000..e317598 --- /dev/null +++ b/xhiveframework/desk/form/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE diff --git a/xhiveframework/desk/form/assign_to.py b/xhiveframework/desk/form/assign_to.py new file mode 100644 index 0000000..876d91e --- /dev/null +++ b/xhiveframework/desk/form/assign_to.py @@ -0,0 +1,286 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +"""assign/unassign to ToDo""" + +import json + +import xhiveframework +import xhiveframework.share +import xhiveframework.utils +from xhiveframework import _ +from xhiveframework.desk.doctype.notification_log.notification_log import ( + enqueue_create_notification, + get_title, + get_title_html, +) +from xhiveframework.desk.form.document_follow import follow_document + + +class DuplicateToDoError(xhiveframework.ValidationError): + pass + + +def get(args=None): + """get assigned to""" + if not args: + args = xhiveframework.local.form_dict + + return xhiveframework.get_all( + "ToDo", + fields=["allocated_to as owner", "name"], + filters={ + "reference_type": args.get("doctype"), + "reference_name": args.get("name"), + "status": ("not in", ("Cancelled", "Closed")), + }, + limit=5, + ) + + +@xhiveframework.whitelist() +def add(args=None, *, ignore_permissions=False): + """add in someone's to do list + args = { + "assign_to": [], + "doctype": , + "name": , + "description": , + "assignment_rule": + } + + """ + if not args: + args = xhiveframework.local.form_dict + + users_with_duplicate_todo = [] + shared_with_users = [] + + for assign_to in xhiveframework.parse_json(args.get("assign_to")): + filters = { + "reference_type": args["doctype"], + "reference_name": args["name"], + "status": "Open", + "allocated_to": assign_to, + } + if not ignore_permissions: + xhiveframework.get_doc(args["doctype"], args["name"]).check_permission() + + if xhiveframework.get_all("ToDo", filters=filters): + users_with_duplicate_todo.append(assign_to) + else: + from xhiveframework.utils import nowdate + + if not args.get("description"): + args["description"] = _("Assignment for {0} {1}").format(args["doctype"], args["name"]) + + d = xhiveframework.get_doc( + { + "doctype": "ToDo", + "allocated_to": assign_to, + "reference_type": args["doctype"], + "reference_name": args["name"], + "description": args.get("description"), + "priority": args.get("priority", "Medium"), + "status": "Open", + "date": args.get("date", nowdate()), + "assigned_by": args.get("assigned_by", xhiveframework.session.user), + "assignment_rule": args.get("assignment_rule"), + } + ).insert(ignore_permissions=True) + + # set assigned_to if field exists + if xhiveframework.get_meta(args["doctype"]).get_field("assigned_to"): + xhiveframework.db.set_value(args["doctype"], args["name"], "assigned_to", assign_to) + + doc = xhiveframework.get_doc(args["doctype"], args["name"]) + + # if assignee does not have permissions, share or inform + if not xhiveframework.has_permission(doc=doc, user=assign_to): + if xhiveframework.get_system_settings("disable_document_sharing"): + msg = _("User {0} is not permitted to access this document.").format( + xhiveframework.bold(assign_to) + ) + msg += "
    " + _( + "As document sharing is disabled, please give them the required permissions before assigning." + ) + xhiveframework.throw(msg, title=_("Missing Permission")) + else: + xhiveframework.share.add(doc.doctype, doc.name, assign_to) + shared_with_users.append(assign_to) + + # make this document followed by assigned user + if xhiveframework.get_cached_value("User", assign_to, "follow_assigned_documents"): + follow_document(args["doctype"], args["name"], assign_to) + + # notify + notify_assignment( + d.assigned_by, + d.allocated_to, + d.reference_type, + d.reference_name, + action="ASSIGN", + description=args.get("description"), + ) + + if shared_with_users: + user_list = format_message_for_assign_to(shared_with_users) + xhiveframework.msgprint( + _("Shared with the following Users with Read access:{0}").format(user_list, alert=True) + ) + + if users_with_duplicate_todo: + user_list = format_message_for_assign_to(users_with_duplicate_todo) + xhiveframework.msgprint(_("Already in the following Users ToDo list:{0}").format(user_list, alert=True)) + + return get(args) + + +@xhiveframework.whitelist() +def add_multiple(args=None): + if not args: + args = xhiveframework.local.form_dict + + docname_list = json.loads(args["name"]) + + for docname in docname_list: + args.update({"name": docname}) + add(args) + + +def close_all_assignments(doctype, name, ignore_permissions=False): + assignments = xhiveframework.get_all( + "ToDo", + fields=["allocated_to", "name"], + filters=dict(reference_type=doctype, reference_name=name, status=("!=", "Cancelled")), + ) + if not assignments: + return False + + for assign_to in assignments: + set_status( + doctype, + name, + todo=assign_to.name, + assign_to=assign_to.allocated_to, + status="Closed", + ignore_permissions=ignore_permissions, + ) + + return True + + +@xhiveframework.whitelist() +def remove(doctype, name, assign_to, ignore_permissions=False): + return set_status(doctype, name, "", assign_to, status="Cancelled", ignore_permissions=ignore_permissions) + + +@xhiveframework.whitelist() +def close(doctype: str, name: str, assign_to: str, ignore_permissions=False): + if assign_to != xhiveframework.session.user: + xhiveframework.throw(_("Only the assignee can complete this to-do.")) + + return set_status(doctype, name, "", assign_to, status="Closed", ignore_permissions=ignore_permissions) + + +def set_status(doctype, name, todo=None, assign_to=None, status="Cancelled", ignore_permissions=False): + """remove from todo""" + + if not ignore_permissions: + xhiveframework.get_doc(doctype, name).check_permission() + try: + if not todo: + todo = xhiveframework.db.get_value( + "ToDo", + { + "reference_type": doctype, + "reference_name": name, + "allocated_to": assign_to, + "status": ("!=", status), + }, + ) + if todo: + todo = xhiveframework.get_doc("ToDo", todo) + todo.status = status + todo.save(ignore_permissions=True) + + notify_assignment(todo.assigned_by, todo.allocated_to, todo.reference_type, todo.reference_name) + except xhiveframework.DoesNotExistError: + pass + + # clear assigned_to if field exists + if xhiveframework.get_meta(doctype).get_field("assigned_to") and status in ("Cancelled", "Closed"): + xhiveframework.db.set_value(doctype, name, "assigned_to", None) + + return get({"doctype": doctype, "name": name}) + + +def clear(doctype, name, ignore_permissions=False): + """ + Clears assignments, return False if not assigned. + """ + assignments = xhiveframework.get_all( + "ToDo", + fields=["allocated_to", "name"], + filters=dict(reference_type=doctype, reference_name=name), + ) + if not assignments: + return False + + for assign_to in assignments: + set_status( + doctype, + name, + todo=assign_to.name, + assign_to=assign_to.allocated_to, + status="Cancelled", + ignore_permissions=ignore_permissions, + ) + + return True + + +def notify_assignment(assigned_by, allocated_to, doc_type, doc_name, action="CLOSE", description=None): + """ + Notify assignee that there is a change in assignment + """ + if not (assigned_by and allocated_to and doc_type and doc_name): + return + + assigned_user = xhiveframework.db.get_value("User", allocated_to, ["language", "enabled"], as_dict=True) + + # return if self assigned or user disabled + if assigned_by == allocated_to or not assigned_user.enabled: + return + + # Search for email address in description -- i.e. assignee + user_name = xhiveframework.get_cached_value("User", xhiveframework.session.user, "full_name") + title = get_title(doc_type, doc_name) + description_html = f"
    {description}
    " if description else None + + if action == "CLOSE": + subject = _("Your assignment on {0} {1} has been removed by {2}", lang=assigned_user.language).format( + xhiveframework.bold(_(doc_type)), get_title_html(title), xhiveframework.bold(user_name) + ) + else: + user_name = xhiveframework.bold(user_name) + document_type = xhiveframework.bold(_(doc_type, lang=assigned_user.language)) + title = get_title_html(title) + subject = _("{0} assigned a new task {1} {2} to you", lang=assigned_user.language).format( + user_name, document_type, title + ) + + notification_doc = { + "type": "Assignment", + "document_type": doc_type, + "subject": subject, + "document_name": doc_name, + "from_user": xhiveframework.session.user, + "email_content": description_html, + } + + enqueue_create_notification(allocated_to, notification_doc) + + +def format_message_for_assign_to(users): + return "

    " + "
    ".join(users) diff --git a/xhiveframework/desk/form/document_follow.py b/xhiveframework/desk/form/document_follow.py new file mode 100644 index 0000000..8521928 --- /dev/null +++ b/xhiveframework/desk/form/document_follow.py @@ -0,0 +1,342 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +import xhiveframework.utils +from xhiveframework import _ +from xhiveframework.model import log_types +from xhiveframework.query_builder import DocType +from xhiveframework.utils import get_url_to_form + + +@xhiveframework.whitelist() +def update_follow(doctype, doc_name, following): + if following: + return follow_document(doctype, doc_name, xhiveframework.session.user) + else: + return unfollow_document(doctype, doc_name, xhiveframework.session.user) + + +@xhiveframework.whitelist() +def follow_document(doctype, doc_name, user): + """ + param: + Doctype name + doc name + user email + + condition: + avoided for some doctype + follow only if track changes are set to 1 + """ + if ( + doctype + in ( + "Communication", + "ToDo", + "Email Unsubscribe", + "File", + "Comment", + "Email Account", + "Email Domain", + ) + or doctype in log_types + ): + return + + if (not xhiveframework.get_meta(doctype).track_changes) or user == "Administrator": + return + + if not xhiveframework.db.get_value("User", user, "document_follow_notify", ignore=True, cache=True): + return + + if not is_document_followed(doctype, doc_name, user): + doc = xhiveframework.new_doc("Document Follow") + doc.update({"ref_doctype": doctype, "ref_docname": doc_name, "user": user}) + doc.save() + return doc + + +@xhiveframework.whitelist() +def unfollow_document(doctype, doc_name, user): + doc = xhiveframework.get_all( + "Document Follow", + filters={"ref_doctype": doctype, "ref_docname": doc_name, "user": user}, + fields=["name"], + limit=1, + ) + if doc: + xhiveframework.delete_doc("Document Follow", doc[0].name) + return 1 + return 0 + + +def get_message(doc_name, doctype, frequency, user): + activity_list = get_version(doctype, doc_name, frequency, user) + get_comments( + doctype, doc_name, frequency, user + ) + return sorted(activity_list, key=lambda k: k["time"], reverse=True) + + +def send_email_alert(receiver, docinfo, timeline): + if receiver: + xhiveframework.sendmail( + subject=_("Document Follow Notification"), + recipients=[receiver], + template="document_follow", + args={ + "docinfo": docinfo, + "timeline": timeline, + }, + ) + + +def send_document_follow_mails(frequency): + """ + param: + frequency for sanding mails + + task: + set receiver according to frequency + group document list according to user + get changes, activity, comments on doctype + call method to send mail + """ + + user_list = get_user_list(frequency) + + for user in user_list: + message, valid_document_follows = get_message_for_user(frequency, user) + if message: + send_email_alert(user, valid_document_follows, message) + # send an email if we have already spent resources creating the message + # nosemgrep + xhiveframework.db.commit() + + +def get_user_list(frequency): + DocumentFollow = DocType("Document Follow") + User = DocType("User") + return ( + xhiveframework.qb.from_(DocumentFollow) + .join(User) + .on(DocumentFollow.user == User.name) + .where(User.document_follow_notify == 1) + .where(User.document_follow_frequency == frequency) + .select(DocumentFollow.user) + .groupby(DocumentFollow.user) + ).run(pluck="user") + + +def get_message_for_user(frequency, user): + message = [] + latest_document_follows = get_document_followed_by_user(user) + valid_document_follows = [] + + for document_follow in latest_document_follows: + content = get_message(document_follow.ref_docname, document_follow.ref_doctype, frequency, user) + if content: + message = message + content + valid_document_follows.append( + { + "reference_docname": document_follow.ref_docname, + "reference_doctype": document_follow.ref_doctype, + "reference_url": get_url_to_form( + document_follow.ref_doctype, document_follow.ref_docname + ), + } + ) + return message, valid_document_follows + + +def get_document_followed_by_user(user): + DocumentFollow = DocType("Document Follow") + # at max 20 documents are sent for each user + return ( + xhiveframework.qb.from_(DocumentFollow) + .where(DocumentFollow.user == user) + .select(DocumentFollow.ref_doctype, DocumentFollow.ref_docname) + .orderby(DocumentFollow.modified) + .limit(20) + ).run(as_dict=True) + + +def get_version(doctype, doc_name, frequency, user): + timeline = [] + version = xhiveframework.get_all( + "Version", + filters=[ + ["ref_doctype", "=", doctype], + ["docname", "=", doc_name], + *_get_filters(frequency, user), + ], + fields=["data", "modified", "modified_by"], + ) + if version: + for v in version: + change = xhiveframework.parse_json(v.data) + time = xhiveframework.utils.format_datetime(v.modified, "hh:mm a") + timeline_items = [] + if change.changed: + timeline_items = get_field_changed(change.changed, time, doctype, doc_name, v) + if change.row_changed: + timeline_items = get_row_changed(change.row_changed, time, doctype, doc_name, v) + if change.added: + timeline_items = get_added_row(change.added, time, doctype, doc_name, v) + + timeline = timeline + timeline_items + + return timeline + + +def get_comments(doctype, doc_name, frequency, user): + from xhiveframework.core.utils import html2text + + timeline = [] + comments = xhiveframework.get_all( + "Comment", + filters=[ + ["reference_doctype", "=", doctype], + ["reference_name", "=", doc_name], + *_get_filters(frequency, user), + ], + fields=["content", "modified", "modified_by", "comment_type"], + ) + for comment in comments: + if comment.comment_type == "Like": + by = f""" By : {comment.modified_by}""" + elif comment.comment_type == "Comment": + by = f"""Commented by : {comment.modified_by}""" + else: + by = "" + + time = xhiveframework.utils.format_datetime(comment.modified, "hh:mm a") + timeline.append( + { + "time": comment.modified, + "data": {"time": time, "comment": html2text(str(comment.content)), "by": by}, + "doctype": doctype, + "doc_name": doc_name, + "type": "comment", + } + ) + return timeline + + +def is_document_followed(doctype, doc_name, user): + return xhiveframework.db.exists( + "Document Follow", {"ref_doctype": doctype, "ref_docname": doc_name, "user": user} + ) + + +@xhiveframework.whitelist() +def get_follow_users(doctype, doc_name): + return xhiveframework.get_all( + "Document Follow", filters={"ref_doctype": doctype, "ref_docname": doc_name}, fields=["user"] + ) + + +def get_row_changed(row_changed, time, doctype, doc_name, v): + from xhiveframework.core.utils import html2text + + items = [] + for d in row_changed: + d[2] = d[2] if d[2] else " " + d[0] = d[0] if d[0] else " " + d[3][0][1] = d[3][0][1] if d[3][0][1] else " " + items.append( + { + "time": v.modified, + "data": { + "time": time, + "table_field": d[0], + "row": str(d[1]), + "field": d[3][0][0], + "from": html2text(str(d[3][0][1])), + "to": html2text(str(d[3][0][2])), + }, + "doctype": doctype, + "doc_name": doc_name, + "type": "row changed", + "by": v.modified_by, + } + ) + return items + + +def get_added_row(added, time, doctype, doc_name, v): + return [ + { + "time": v.modified, + "data": {"to": d[0], "time": time}, + "doctype": doctype, + "doc_name": doc_name, + "type": "row added", + "by": v.modified_by, + } + for d in added + ] + + +def get_field_changed(changed, time, doctype, doc_name, v): + from xhiveframework.core.utils import html2text + + items = [] + for d in changed: + d[1] = d[1] if d[1] else " " + d[2] = d[2] if d[2] else " " + d[0] = d[0] if d[0] else " " + items.append( + { + "time": v.modified, + "data": { + "time": time, + "field": d[0], + "from": html2text(str(d[1])), + "to": html2text(str(d[2])), + }, + "doctype": doctype, + "doc_name": doc_name, + "type": "field changed", + "by": v.modified_by, + } + ) + return items + + +def send_hourly_updates(): + send_document_follow_mails("Hourly") + + +def send_daily_updates(): + send_document_follow_mails("Daily") + + +def send_weekly_updates(): + send_document_follow_mails("Weekly") + + +def _get_filters(frequency, user): + filters = [ + ["modified_by", "!=", user], + ] + + if frequency == "Weekly": + filters += [ + ["modified", ">", xhiveframework.utils.add_days(xhiveframework.utils.nowdate(), -7)], + ["modified", "<", xhiveframework.utils.nowdate()], + ] + + elif frequency == "Daily": + filters += [ + ["modified", ">", xhiveframework.utils.add_days(xhiveframework.utils.nowdate(), -1)], + ["modified", "<", xhiveframework.utils.nowdate()], + ] + + elif frequency == "Hourly": + filters += [ + ["modified", ">", xhiveframework.utils.add_to_date(xhiveframework.utils.now_datetime(), hours=-1)], + ["modified", "<", xhiveframework.utils.now_datetime()], + ] + + return filters diff --git a/xhiveframework/desk/form/linked_with.py b/xhiveframework/desk/form/linked_with.py new file mode 100644 index 0000000..cb22575 --- /dev/null +++ b/xhiveframework/desk/form/linked_with.py @@ -0,0 +1,671 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import itertools +import json +from collections import defaultdict + +import xhiveframework +import xhiveframework.desk.form.load +import xhiveframework.desk.form.meta +from xhiveframework import _ +from xhiveframework.model.meta import is_single +from xhiveframework.modules import load_doctype_module + + +@xhiveframework.whitelist() +def get_submitted_linked_docs(doctype: str, name: str) -> list[tuple]: + """Get all the nested submitted documents those are present in referencing tables (dependent tables). + + :param doctype: Document type + :param name: Name of the document + + Usecase: + * User should be able to cancel the linked documents along with the one user trying to cancel. + + Case1: If document sd1-n1 (document name n1 from sumittable doctype sd1) is linked to sd2-n2 and sd2-n2 is linked to sd3-n3, + Getting submittable linked docs of `sd1-n1`should give both sd2-n2 and sd3-n3. + Case2: If document sd1-n1 (document name n1 from sumittable doctype sd1) is linked to d2-n2 and d2-n2 is linked to sd3-n3, + Getting submittable linked docs of `sd1-n1`should give None. (because d2-n2 is not a submittable doctype) + Case3: If document sd1-n1 (document name n1 from submittable doctype sd1) is linked to d2-n2 & sd2-n2. d2-n2 is linked to sd3-n3. + Getting submittable linked docs of `sd1-n1`should give sd2-n2. + + Logic: + ----- + 1. We can find linked documents only if we know how the doctypes are related. + 2. As we need only submittable documents, we can limit doctype relations search to submittable doctypes by + finding the relationships(Foreign key references) across submittable doctypes. + 3. Searching for links is going to be a tree like structure where at every level, + you will be finding documents using parent document and parent document links. + """ + xhiveframework.has_permission(doctype, doc=name) + tree = SubmittableDocumentTree(doctype, name) + visited_documents = tree.get_all_children() + docs = [] + + for dt, names in visited_documents.items(): + docs.extend([{"doctype": dt, "name": name, "docstatus": 1} for name in names]) + + return {"docs": docs, "count": len(docs)} + + +class SubmittableDocumentTree: + def __init__(self, doctype: str, name: str): + """Construct a tree for the submitable linked documents. + + * Node has properties like doctype and docnames. Represented as Node(doctype, docnames). + * Nodes are linked by doctype relationships like table, link and dynamic links. + * Node is referenced(linked) by many other documents and those are the child nodes. + + NOTE: child document is a property of child node (not same as XhiveFramework child docs of a table field). + """ + self.root_doctype = doctype + self.root_docname = name + + # Documents those are yet to be visited for linked documents. + self.to_be_visited_documents = {doctype: [name]} + self.visited_documents = defaultdict(list) + + self._submittable_doctypes = None # All submittable doctypes in the system + self._references_across_doctypes = None # doctype wise links/references + + def get_all_children(self): + """Get all nodes of a tree except the root node (all the nested submitted + documents those are present in referencing tables dependent tables). + """ + while self.to_be_visited_documents: + next_level_children = defaultdict(list) + for parent_dt in list(self.to_be_visited_documents): + parent_docs = self.to_be_visited_documents.get(parent_dt) + if not parent_docs: + del self.to_be_visited_documents[parent_dt] + continue + + child_docs = self.get_next_level_children(parent_dt, parent_docs) + self.visited_documents[parent_dt].extend(parent_docs) + for linked_dt, linked_names in child_docs.items(): + not_visited_child_docs = set(linked_names) - set( + self.visited_documents.get(linked_dt, []) + ) + next_level_children[linked_dt].extend(not_visited_child_docs) + + self.to_be_visited_documents = next_level_children + + # Remove root node from visited documents + if self.root_docname in self.visited_documents.get(self.root_doctype, []): + self.visited_documents[self.root_doctype].remove(self.root_docname) + + return self.visited_documents + + def get_next_level_children(self, parent_dt, parent_names): + """Get immediate children of a Node(parent_dt, parent_names)""" + referencing_fields = self.get_doctype_references(parent_dt) + + child_docs = defaultdict(list) + for field in referencing_fields: + if field["fieldname"] == "amended_from": + # perf: amended_from links are always linked to cancelled documents. + continue + + links = ( + get_referencing_documents( + parent_dt, + parent_names.copy(), + field, + get_parent_if_child_table_doc=True, + parent_filters=[("docstatus", "=", 1)], + allowed_parents=self.get_link_sources(), + ) + or {} + ) + for dt, names in links.items(): + child_docs[dt].extend(names) + return child_docs + + def get_doctype_references(self, doctype): + """Get references for a given document.""" + if self._references_across_doctypes is None: + get_links_to = self.get_document_sources() + limit_link_doctypes = self.get_link_sources() + self._references_across_doctypes = get_references_across_doctypes( + get_links_to, limit_link_doctypes + ) + return self._references_across_doctypes.get(doctype, []) + + def get_document_sources(self): + """Returns list of doctypes from where we access submittable documents.""" + return list(set([*self.get_link_sources(), self.root_doctype])) + + def get_link_sources(self): + """limit doctype links to these doctypes.""" + return list(set(self.get_submittable_doctypes()) - set(get_exempted_doctypes() or [])) + + def get_submittable_doctypes(self) -> list[str]: + """Returns list of submittable doctypes.""" + if not self._submittable_doctypes: + self._submittable_doctypes = xhiveframework.get_all( + "DocType", {"is_submittable": 1}, pluck="name", order_by=None + ) + return self._submittable_doctypes + + +def get_child_tables_of_doctypes(doctypes: list[str] | None = None): + """Returns child tables by doctype.""" + filters = [["fieldtype", "=", "Table"]] + filters_for_docfield = filters + filters_for_customfield = filters + + if doctypes: + filters_for_docfield = [*filters, ["parent", "in", tuple(doctypes)]] + filters_for_customfield = [*filters, ["dt", "in", tuple(doctypes)]] + + links = xhiveframework.get_all( + "DocField", + fields=["parent", "fieldname", "options as child_table"], + filters=filters_for_docfield, + as_list=1, + order_by=None, + ) + + links += xhiveframework.get_all( + "Custom Field", + fields=["dt as parent", "fieldname", "options as child_table"], + filters=filters_for_customfield, + as_list=1, + order_by=None, + ) + + child_tables_by_doctype = defaultdict(list) + for doctype, fieldname, child_table in links: + child_tables_by_doctype[doctype].append( + {"doctype": doctype, "fieldname": fieldname, "child_table": child_table} + ) + return child_tables_by_doctype + + +def get_references_across_doctypes( + to_doctypes: list[str] | None = None, limit_link_doctypes: list[str] | None = None +) -> list: + """Find doctype wise foreign key references. + + :param to_doctypes: Get links of these doctypes. + :param limit_link_doctypes: limit links to these doctypes. + + * Include child table, link and dynamic link references. + """ + if limit_link_doctypes: + child_tables_by_doctype = get_child_tables_of_doctypes(limit_link_doctypes) + all_child_tables = [ + each["child_table"] for each in itertools.chain(*child_tables_by_doctype.values()) + ] + limit_link_doctypes = limit_link_doctypes + all_child_tables + else: + child_tables_by_doctype = get_child_tables_of_doctypes() + all_child_tables = [ + each["child_table"] for each in itertools.chain(*child_tables_by_doctype.values()) + ] + + references_by_link_fields = get_references_across_doctypes_by_link_field(to_doctypes, limit_link_doctypes) + references_by_dlink_fields = get_references_across_doctypes_by_dynamic_link_field( + to_doctypes, limit_link_doctypes + ) + + references = references_by_link_fields.copy() + for k, v in references_by_dlink_fields.items(): + references.setdefault(k, []).extend(v) + + for links in references.values(): + for link in links: + link["is_child"] = link["doctype"] in all_child_tables + return references + + +def get_references_across_doctypes_by_link_field( + to_doctypes: list[str] | None = None, limit_link_doctypes: list[str] | None = None +): + """Find doctype wise foreign key references based on link fields. + + :param to_doctypes: Get links to these doctypes. + :param limit_link_doctypes: limit links to these doctypes. + """ + filters = [["fieldtype", "=", "Link"]] + + if to_doctypes: + filters += [["options", "in", tuple(to_doctypes)]] + + filters_for_docfield = filters[:] + filters_for_customfield = filters[:] + + if limit_link_doctypes: + filters_for_docfield += [["parent", "in", tuple(limit_link_doctypes)]] + filters_for_customfield += [["dt", "in", tuple(limit_link_doctypes)]] + + links = xhiveframework.get_all( + "DocField", + fields=["parent", "fieldname", "options as linked_to"], + filters=filters_for_docfield, + as_list=1, + ) + + links += xhiveframework.get_all( + "Custom Field", + fields=["dt as parent", "fieldname", "options as linked_to"], + filters=filters_for_customfield, + as_list=1, + ) + + links_by_doctype = defaultdict(list) + for doctype, fieldname, linked_to in links: + links_by_doctype[linked_to].append({"doctype": doctype, "fieldname": fieldname}) + return links_by_doctype + + +def get_references_across_doctypes_by_dynamic_link_field( + to_doctypes: list[str] | None = None, limit_link_doctypes: list[str] | None = None +): + """Find doctype wise foreign key references based on dynamic link fields. + + :param to_doctypes: Get links to these doctypes. + :param limit_link_doctypes: limit links to these doctypes. + """ + + filters = [["fieldtype", "=", "Dynamic Link"]] + + filters_for_docfield = filters[:] + filters_for_customfield = filters[:] + + if limit_link_doctypes: + filters_for_docfield += [["parent", "in", tuple(limit_link_doctypes)]] + filters_for_customfield += [["dt", "in", tuple(limit_link_doctypes)]] + + # find dynamic links of parents + links = xhiveframework.get_all( + "DocField", + fields=["parent as doctype", "fieldname", "options as doctype_fieldname"], + filters=filters_for_docfield, + as_list=1, + order_by=None, + ) + + links += xhiveframework.get_all( + "Custom Field", + fields=["dt as doctype", "fieldname", "options as doctype_fieldname"], + filters=filters_for_customfield, + as_list=1, + order_by=None, + ) + + links_by_doctype = defaultdict(list) + for doctype, fieldname, doctype_fieldname in links: + try: + filters = [[doctype_fieldname, "in", to_doctypes]] if to_doctypes else [] + for linked_to in xhiveframework.get_all(doctype, pluck=doctype_fieldname, filters=filters, distinct=1): + if linked_to: + links_by_doctype[linked_to].append( + {"doctype": doctype, "fieldname": fieldname, "doctype_fieldname": doctype_fieldname} + ) + except xhiveframework.db.ProgrammingError: + # TODO: FIXME + continue + return links_by_doctype + + +def get_referencing_documents( + reference_doctype: str, + reference_names: list[str], + link_info: dict, + get_parent_if_child_table_doc: bool = True, + parent_filters: list[list] | None = None, + child_filters=None, + allowed_parents=None, +): + """Get linked documents based on link_info. + + :param reference_doctype: reference doctype to find links + :param reference_names: reference document names to find links for + :param link_info: linking details to get the linked documents + Ex: {'doctype': 'Purchase Invoice Advance', 'fieldname': 'reference_name', + 'doctype_fieldname': 'reference_type', 'is_child': True} + :param get_parent_if_child_table_doc: Get parent record incase linked document is a child table record. + :param parent_filters: filters to apply on if not a child table. + :param child_filters: apply filters if it is a child table. + :param allowed_parents: list of parents allowed in case of get_parent_if_child_table_doc + is enabled. + """ + from_table = link_info["doctype"] + filters = [[link_info["fieldname"], "in", tuple(reference_names)]] + if link_info.get("doctype_fieldname"): + filters.append([link_info["doctype_fieldname"], "=", reference_doctype]) + + if not link_info.get("is_child"): + filters.extend(parent_filters or []) + return {from_table: xhiveframework.get_all(from_table, filters, pluck="name", order_by=None)} + + filters.extend(child_filters or []) + res = xhiveframework.get_all(from_table, filters=filters, fields=["name", "parenttype", "parent"], order_by=None) + documents = defaultdict(list) + + for parent, rows in itertools.groupby(res, key=lambda row: row["parenttype"]): + if allowed_parents and parent not in allowed_parents: + continue + filters = (parent_filters or []) + [["name", "in", tuple(row.parent for row in rows)]] + documents[parent].extend(xhiveframework.get_all(parent, filters=filters, pluck="name", order_by=None) or []) + return documents + + +@xhiveframework.whitelist() +def cancel_all_linked_docs(docs, ignore_doctypes_on_cancel_all=None): + """ + Cancel all linked doctype, optionally ignore doctypes specified in a list. + + Arguments: + docs (json str) - It contains list of dictionaries of a linked documents. + ignore_doctypes_on_cancel_all (list) - List of doctypes to ignore while cancelling. + """ + if ignore_doctypes_on_cancel_all is None: + ignore_doctypes_on_cancel_all = [] + + docs = json.loads(docs) + if isinstance(ignore_doctypes_on_cancel_all, str): + ignore_doctypes_on_cancel_all = json.loads(ignore_doctypes_on_cancel_all) + for i, doc in enumerate(docs, 1): + if validate_linked_doc(doc, ignore_doctypes_on_cancel_all): + linked_doc = xhiveframework.get_doc(doc.get("doctype"), doc.get("name")) + linked_doc.cancel() + xhiveframework.publish_progress(percent=i / len(docs) * 100, title=_("Cancelling documents")) + + +def validate_linked_doc(docinfo, ignore_doctypes_on_cancel_all=None): + """ + Validate a document to be submitted and non-exempted from auto-cancel. + + Arguments: + docinfo (dict): The document to check for submitted and non-exempt from auto-cancel + ignore_doctypes_on_cancel_all (list) - List of doctypes to ignore while cancelling. + + Returns: + bool: True if linked document passes all validations, else False + """ + # ignore doctype to cancel + if docinfo.get("doctype") in (ignore_doctypes_on_cancel_all or []): + return False + + # skip non-submittable doctypes since they don't need to be cancelled + if not xhiveframework.get_meta(docinfo.get("doctype")).is_submittable: + return False + + # skip draft or cancelled documents + if docinfo.get("docstatus") != 1: + return False + + # skip other doctypes since they don't need to be cancelled + auto_cancel_exempt_doctypes = get_exempted_doctypes() + if docinfo.get("doctype") in auto_cancel_exempt_doctypes: + return False + + return True + + +def get_exempted_doctypes(): + """Get list of doctypes exempted from being auto-cancelled""" + return list(xhiveframework.get_hooks("auto_cancel_exempted_doctypes")) + + +def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> dict[str, list]: + if isinstance(linkinfo, str): + # additional fields are added in linkinfo + linkinfo = json.loads(linkinfo) + + results = {} + + if not linkinfo: + return results + + for dt, link in linkinfo.items(): + filters = [] + link["doctype"] = dt + try: + link_meta_bundle = xhiveframework.desk.form.load.get_meta_bundle(dt) + except Exception as e: + if isinstance(e, xhiveframework.DoesNotExistError): + xhiveframework.clear_last_message() + continue + linkmeta = link_meta_bundle[0] + + if not linkmeta.get("issingle"): + fields = [ + d.fieldname + for d in linkmeta.get( + "fields", + { + "in_list_view": 1, + "fieldtype": ["not in", ("Image", "HTML", "Button", *xhiveframework.model.table_fields)], + }, + ) + ] + ["name", "modified", "docstatus"] + + if link.get("add_fields"): + fields += link["add_fields"] + + fields = [f"`tab{dt}`.`{sf.strip()}`" for sf in fields if sf and "`tab" not in sf] + + try: + if link.get("filters"): + ret = xhiveframework.get_all( + doctype=dt, fields=fields, filters=link.get("filters"), order_by=None + ) + + elif link.get("get_parent"): + ret = None + + # check for child table + if not xhiveframework.get_meta(doctype).istable: + continue + + me = xhiveframework.db.get_value( + doctype, name, ["parenttype", "parent"], as_dict=True, order_by=None + ) + if me and me.parenttype == dt: + ret = xhiveframework.get_all( + doctype=dt, fields=fields, filters=[[dt, "name", "=", me.parent]], order_by=None + ) + + elif link.get("child_doctype"): + or_filters = [ + [link.get("child_doctype"), link_fieldnames, "=", name] + for link_fieldnames in link.get("fieldname") + ] + + # dynamic link + if link.get("doctype_fieldname"): + filters.append( + [link.get("child_doctype"), link.get("doctype_fieldname"), "=", doctype] + ) + + ret = xhiveframework.get_all( + doctype=dt, + fields=fields, + filters=filters, + or_filters=or_filters, + distinct=True, + order_by=None, + ) + + else: + link_fieldnames = link.get("fieldname") + if link_fieldnames: + if isinstance(link_fieldnames, str): + link_fieldnames = [link_fieldnames] + or_filters = [[dt, fieldname, "=", name] for fieldname in link_fieldnames] + # dynamic link + if link.get("doctype_fieldname"): + filters.append([dt, link.get("doctype_fieldname"), "=", doctype]) + ret = xhiveframework.get_all( + doctype=dt, fields=fields, filters=filters, or_filters=or_filters, order_by=None + ) + + else: + ret = None + + except xhiveframework.PermissionError: + xhiveframework.clear_last_message() + + continue + + if ret: + results[dt] = ret + + return results + + +@xhiveframework.whitelist() +def get(doctype, docname): + xhiveframework.has_permission(doctype, doc=docname) + linked_doctypes = get_linked_doctypes(doctype=doctype) + return get_linked_docs(doctype=doctype, name=docname, linkinfo=linked_doctypes) + + +@xhiveframework.whitelist() +def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): + """add list of doctypes this doctype is 'linked' with. + + Example, for Customer: + + {"Address": {"fieldname": "customer"}..} + """ + if without_ignore_user_permissions_enabled: + return xhiveframework.cache.hget( + "linked_doctypes_without_ignore_user_permissions_enabled", + doctype, + lambda: _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled), + ) + else: + return xhiveframework.cache.hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype)) + + +def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): + ret = {} + # find fields where this doctype is linked + ret.update(get_linked_fields(doctype, without_ignore_user_permissions_enabled)) + ret.update(get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled)) + + filters = [["fieldtype", "in", xhiveframework.model.table_fields], ["options", "=", doctype]] + if without_ignore_user_permissions_enabled: + filters.append(["ignore_user_permissions", "!=", 1]) + # find links of parents + links = xhiveframework.get_all("DocField", fields=["parent as dt"], filters=filters) + links += xhiveframework.get_all("Custom Field", fields=["dt"], filters=filters) + + for (dt,) in links: + if dt in ret: + continue + ret[dt] = {"get_parent": True} + + for dt in list(ret): + try: + doctype_module = load_doctype_module(dt) + except (ImportError, KeyError): + # in case of Custom DocType + # or in case of module rename eg. (Schools -> Education) + continue + + if getattr(doctype_module, "exclude_from_linked_with", False): + del ret[dt] + + return ret + + +def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False): + filters = [["fieldtype", "=", "Link"], ["options", "=", doctype]] + if without_ignore_user_permissions_enabled: + filters.append(["ignore_user_permissions", "!=", 1]) + + # find links of parents + links = xhiveframework.get_all("DocField", fields=["parent", "fieldname"], filters=filters, as_list=1) + links += xhiveframework.get_all("Custom Field", fields=["dt as parent", "fieldname"], filters=filters, as_list=1) + + ret = {} + + if not links: + return ret + + links_dict = defaultdict(list) + for doctype, fieldname in links: + links_dict[doctype].append(fieldname) + + for doctype_name in links_dict: + ret[doctype_name] = {"fieldname": links_dict.get(doctype_name)} + table_doctypes = xhiveframework.get_all( + "DocType", filters=[["istable", "=", "1"], ["name", "in", tuple(links_dict)]] + ) + child_filters = [ + ["fieldtype", "in", xhiveframework.model.table_fields], + ["options", "in", tuple(doctype.name for doctype in table_doctypes)], + ] + if without_ignore_user_permissions_enabled: + child_filters.append(["ignore_user_permissions", "!=", 1]) + + # find out if linked in a child table + for parent, options in xhiveframework.get_all( + "DocField", fields=["parent", "options"], filters=child_filters, as_list=1 + ): + ret[parent] = {"child_doctype": options, "fieldname": links_dict[options]} + if options in ret: + del ret[options] + + virtual_doctypes = xhiveframework.get_all("DocType", {"is_virtual": 1}, pluck="name") + for dt in virtual_doctypes: + ret.pop(dt, None) + + return ret + + +def get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled=False): + ret = {} + + filters = [["fieldtype", "=", "Dynamic Link"]] + if without_ignore_user_permissions_enabled: + filters.append(["ignore_user_permissions", "!=", 1]) + + # find dynamic links of parents + links = xhiveframework.get_all( + "DocField", + fields=["parent as doctype", "fieldname", "options as doctype_fieldname"], + filters=filters, + ) + links += xhiveframework.get_all( + "Custom Field", + fields=["dt as doctype", "fieldname", "options as doctype_fieldname"], + filters=filters, + ) + + for df in links: + if is_single(df.doctype): + continue + + meta = xhiveframework.get_meta(df.doctype) + if meta.is_virtual: + continue + + is_child = meta.istable + possible_link = xhiveframework.get_all( + df.doctype, + filters={df.doctype_fieldname: doctype}, + fields=["parenttype"] if is_child else None, + distinct=True, + ) + + if not possible_link: + continue + + if is_child: + for d in possible_link: + ret[d.parenttype] = { + "child_doctype": df.doctype, + "fieldname": [df.fieldname], + "doctype_fieldname": df.doctype_fieldname, + } + else: + ret[df.doctype] = {"fieldname": [df.fieldname], "doctype_fieldname": df.doctype_fieldname} + + return ret diff --git a/xhiveframework/desk/form/load.py b/xhiveframework/desk/form/load.py new file mode 100644 index 0000000..80665d0 --- /dev/null +++ b/xhiveframework/desk/form/load.py @@ -0,0 +1,489 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import typing +from urllib.parse import quote + +import xhiveframework +import xhiveframework.defaults +import xhiveframework.desk.form.meta +import xhiveframework.utils +from xhiveframework import _, _dict +from xhiveframework.desk.form.document_follow import is_document_followed +from xhiveframework.model.utils.user_settings import get_user_settings +from xhiveframework.permissions import get_doc_permissions +from xhiveframework.utils.data import cstr + +if typing.TYPE_CHECKING: + from xhiveframework.model.document import Document + + +@xhiveframework.whitelist() +def getdoc(doctype, name, user=None): + """ + Loads a doclist for a given document. This method is called directly from the client. + Requries "doctype", "name" as form variables. + Will also call the "onload" method on the document. + """ + + if not (doctype and name): + raise Exception("doctype and name required!") + + try: + doc = xhiveframework.get_doc(doctype, name) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_last_message() + return [] + + if not doc.has_permission("read"): + xhiveframework.flags.error_message = _("Insufficient Permission for {0}").format( + xhiveframework.bold(doctype + " " + name) + ) + raise xhiveframework.PermissionError(("read", doctype, name)) + + run_onload(doc) + doc.apply_fieldlevel_read_permissions() + + # add file list + doc.add_viewed() + get_docinfo(doc) + + doc.add_seen() + set_link_titles(doc) + if xhiveframework.response.docs is None: + xhiveframework.local.response = _dict({"docs": []}) + xhiveframework.response.docs.append(doc) + + +@xhiveframework.whitelist() +def getdoctype(doctype, with_parent=False, cached_timestamp=None): + """load doctype""" + + docs = [] + parent_dt = None + + # with parent (called from report builder) + if with_parent and (parent_dt := xhiveframework.model.meta.get_parent_dt(doctype)): + docs = get_meta_bundle(parent_dt) + xhiveframework.response["parent_dt"] = parent_dt + + if not docs: + docs = get_meta_bundle(doctype) + + xhiveframework.response["user_settings"] = get_user_settings(parent_dt or doctype) + + if cached_timestamp and docs[0].modified == cached_timestamp: + return "use_cache" + + xhiveframework.response.docs.extend(docs) + + +def get_meta_bundle(doctype): + bundle = [xhiveframework.desk.form.meta.get_meta(doctype)] + bundle.extend( + xhiveframework.desk.form.meta.get_meta(df.options) + for df in bundle[0].fields + if df.fieldtype in xhiveframework.model.table_fields + ) + return bundle + + +@xhiveframework.whitelist() +def get_docinfo(doc=None, doctype=None, name=None): + from xhiveframework.share import _get_users as get_docshares + + if not doc: + doc = xhiveframework.get_doc(doctype, name) + if not doc.has_permission("read"): + raise xhiveframework.PermissionError + + all_communications = _get_communications(doc.doctype, doc.name, limit=21) + automated_messages = [ + msg for msg in all_communications if msg["communication_type"] == "Automated Message" + ] + communications_except_auto_messages = [ + msg for msg in all_communications if msg["communication_type"] != "Automated Message" + ] + + docinfo = xhiveframework._dict(user_info={}) + + add_comments(doc, docinfo) + + docinfo.update( + { + "doctype": doc.doctype, + "name": doc.name, + "attachments": get_attachments(doc.doctype, doc.name), + "communications": communications_except_auto_messages, + "automated_messages": automated_messages, + "versions": get_versions(doc), + "assignments": get_assignments(doc.doctype, doc.name), + "permissions": get_doc_permissions(doc), + "shared": get_docshares(doc), + "views": get_view_logs(doc), + "energy_point_logs": get_point_logs(doc.doctype, doc.name), + "additional_timeline_content": get_additional_timeline_content(doc.doctype, doc.name), + "milestones": get_milestones(doc.doctype, doc.name), + "is_document_followed": is_document_followed(doc.doctype, doc.name, xhiveframework.session.user), + "tags": get_tags(doc.doctype, doc.name), + "document_email": get_document_email(doc.doctype, doc.name), + } + ) + + update_user_info(docinfo) + + xhiveframework.response["docinfo"] = docinfo + + +def add_comments(doc, docinfo): + # divide comments into separate lists + docinfo.comments = [] + docinfo.shared = [] + docinfo.assignment_logs = [] + docinfo.attachment_logs = [] + docinfo.info_logs = [] + docinfo.like_logs = [] + docinfo.workflow_logs = [] + + comments = xhiveframework.get_all( + "Comment", + fields=["name", "creation", "content", "owner", "comment_type"], + filters={"reference_doctype": doc.doctype, "reference_name": doc.name}, + ) + + for c in comments: + match c.comment_type: + case "Comment": + c.content = xhiveframework.utils.markdown(c.content) + docinfo.comments.append(c) + case "Shared" | "Unshared": + docinfo.shared.append(c) + case "Assignment Completed" | "Assigned": + docinfo.assignment_logs.append(c) + case "Attachment" | "Attachment Removed": + docinfo.attachment_logs.append(c) + case "Info" | "Edit" | "Label": + docinfo.info_logs.append(c) + case "Like": + docinfo.like_logs.append(c) + case "Workflow": + docinfo.workflow_logs.append(c) + + return comments + + +def get_milestones(doctype, name): + return xhiveframework.get_all( + "Milestone", + fields=["creation", "owner", "track_field", "value"], + filters=dict(reference_type=doctype, reference_name=name), + ) + + +def get_attachments(dt, dn): + return xhiveframework.get_all( + "File", + fields=["name", "file_name", "file_url", "is_private"], + filters={"attached_to_name": dn, "attached_to_doctype": dt}, + ) + + +def get_versions(doc: "Document") -> list[dict]: + if not doc.meta.track_changes: + return [] + return xhiveframework.get_all( + "Version", + filters=dict(ref_doctype=doc.doctype, docname=doc.name), + fields=["name", "owner", "creation", "data"], + limit=10, + order_by="creation desc", + ) + + +@xhiveframework.whitelist() +def get_communications(doctype, name, start=0, limit=20): + from xhiveframework.utils import cint + + doc = xhiveframework.get_doc(doctype, name) + if not doc.has_permission("read"): + raise xhiveframework.PermissionError + + return _get_communications(doctype, name, cint(start), cint(limit)) + + +def get_comments(doctype: str, name: str, comment_type: str | list[str] = "Comment") -> list[xhiveframework._dict]: + if isinstance(comment_type, list): + comment_types = comment_type + + elif comment_type == "share": + comment_types = ["Shared", "Unshared"] + + elif comment_type == "assignment": + comment_types = ["Assignment Completed", "Assigned"] + + elif comment_type == "attachment": + comment_types = ["Attachment", "Attachment Removed"] + + else: + comment_types = [comment_type] + + comments = xhiveframework.get_all( + "Comment", + fields=["name", "creation", "content", "owner", "comment_type"], + filters={ + "reference_doctype": doctype, + "reference_name": name, + "comment_type": ["in", comment_types], + }, + ) + + # convert to markdown (legacy ?) + for c in comments: + if c.comment_type == "Comment": + c.content = xhiveframework.utils.markdown(c.content) + + return comments + + +def get_point_logs(doctype, docname): + return xhiveframework.get_all( + "Energy Point Log", + filters={"reference_doctype": doctype, "reference_name": docname, "type": ["!=", "Review"]}, + fields=["*"], + ) + + +def _get_communications(doctype, name, start=0, limit=20): + communications = get_communication_data(doctype, name, start, limit) + for c in communications: + if c.communication_type in ("Communication", "Automated Message"): + c.attachments = json.dumps( + xhiveframework.get_all( + "File", + fields=["file_url", "is_private"], + filters={"attached_to_doctype": "Communication", "attached_to_name": c.name}, + ) + ) + + return communications + + +def get_communication_data( + doctype, name, start=0, limit=20, after=None, fields=None, group_by=None, as_dict=True +): + """Returns list of communications for a given document""" + if not fields: + fields = """ + C.name, C.communication_type, C.communication_medium, + C.comment_type, C.communication_date, C.content, + C.sender, C.sender_full_name, C.cc, C.bcc, + C.creation AS creation, C.subject, C.delivery_status, + C._liked_by, C.reference_doctype, C.reference_name, + C.read_by_recipient, C.rating, C.recipients + """ + + conditions = "" + if after: + # find after a particular date + conditions += f""" + AND C.communication_date > {after} + """ + + if doctype == "User": + conditions += """ + AND NOT (C.reference_doctype='User' AND C.communication_type='Communication') + """ + + # communications linked to reference_doctype + part1 = f""" + SELECT {fields} + FROM `tabCommunication` as C + WHERE C.communication_type IN ('Communication', 'Feedback', 'Automated Message') + AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s) + {conditions} + """ + + # communications linked in Timeline Links + part2 = f""" + SELECT {fields} + FROM `tabCommunication` as C + INNER JOIN `tabCommunication Link` ON C.name=`tabCommunication Link`.parent + WHERE C.communication_type IN ('Communication', 'Feedback', 'Automated Message') + AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s + {conditions} + """ + + return xhiveframework.db.sql( + """ + SELECT * + FROM (({part1}) UNION ({part2})) AS combined + {group_by} + ORDER BY communication_date DESC + LIMIT %(limit)s + OFFSET %(start)s + """.format(part1=part1, part2=part2, group_by=(group_by or "")), + dict( + doctype=doctype, + name=name, + start=xhiveframework.utils.cint(start), + limit=limit, + ), + as_dict=as_dict, + ) + + +def get_assignments(dt, dn): + return xhiveframework.get_all( + "ToDo", + fields=["name", "allocated_to as owner", "description", "status"], + filters={ + "reference_type": dt, + "reference_name": dn, + "status": ("not in", ("Cancelled", "Closed")), + "allocated_to": ("is", "set"), + }, + ) + + +def run_onload(doc): + doc.set("__onload", xhiveframework._dict()) + doc.run_method("onload") + + +def get_view_logs(doc: "Document") -> list[dict]: + """get and return the latest view logs if available""" + if not doc.meta.track_views: + return [] + + return xhiveframework.get_all( + "View Log", + filters={ + "reference_doctype": doc.doctype, + "reference_name": doc.name, + }, + fields=["name", "creation", "owner"], + order_by="creation desc", + ) + + +def get_tags(doctype: str, name: str) -> str: + tags = xhiveframework.get_all( + "Tag Link", + filters={"document_type": doctype, "document_name": name}, + fields=["tag"], + pluck="tag", + ) + + return ",".join(tags) + + +def get_document_email(doctype, name): + email = get_automatic_email_link() + if not email: + return None + + email = email.split("@") + return f"{email[0]}+{quote(doctype)}={quote(cstr(name))}@{email[1]}" + + +def get_automatic_email_link(): + return xhiveframework.db.get_value( + "Email Account", {"enable_incoming": 1, "enable_automatic_linking": 1}, "email_id" + ) + + +def get_additional_timeline_content(doctype, docname): + contents = [] + hooks = xhiveframework.get_hooks().get("additional_timeline_content", {}) + methods_for_all_doctype = hooks.get("*", []) + methods_for_current_doctype = hooks.get(doctype, []) + + for method in methods_for_all_doctype + methods_for_current_doctype: + contents.extend(xhiveframework.get_attr(method)(doctype, docname) or []) + + return contents + + +def set_link_titles(doc): + link_titles = {} + link_titles.update(get_title_values_for_link_and_dynamic_link_fields(doc)) + link_titles.update(get_title_values_for_table_and_multiselect_fields(doc)) + + send_link_titles(link_titles) + + +def get_title_values_for_link_and_dynamic_link_fields(doc, link_fields=None): + link_titles = {} + + if not link_fields: + meta = xhiveframework.get_meta(doc.doctype) + link_fields = meta.get_link_fields() + meta.get_dynamic_link_fields() + + for field in link_fields: + if not doc.get(field.fieldname): + continue + + doctype = field.options if field.fieldtype == "Link" else doc.get(field.options) + + meta = xhiveframework.get_meta(doctype) + if not meta or not (meta.title_field and meta.show_title_field_in_link): + continue + + link_title = xhiveframework.db.get_value( + doctype, doc.get(field.fieldname), meta.title_field, cache=True, order_by=None + ) + link_titles.update({doctype + "::" + doc.get(field.fieldname): link_title}) + + return link_titles + + +def get_title_values_for_table_and_multiselect_fields(doc, table_fields=None): + link_titles = {} + + if not table_fields: + meta = xhiveframework.get_meta(doc.doctype) + table_fields = meta.get_table_fields() + + for field in table_fields: + if not doc.get(field.fieldname): + continue + + for value in doc.get(field.fieldname): + link_titles.update(get_title_values_for_link_and_dynamic_link_fields(value)) + + return link_titles + + +def send_link_titles(link_titles): + """Append link titles dict in `xhiveframework.local.response`.""" + if "_link_titles" not in xhiveframework.local.response: + xhiveframework.local.response["_link_titles"] = {} + + xhiveframework.local.response["_link_titles"].update(link_titles) + + +def update_user_info(docinfo): + users = set() + + users.update(d.sender for d in docinfo.communications) + users.update(d.user for d in docinfo.shared) + users.update(d.owner for d in docinfo.assignments) + users.update(d.owner for d in docinfo.views) + users.update(d.owner for d in docinfo.workflow_logs) + users.update(d.owner for d in docinfo.like_logs) + users.update(d.owner for d in docinfo.info_logs) + users.update(d.owner for d in docinfo.attachment_logs) + users.update(d.owner for d in docinfo.assignment_logs) + users.update(d.owner for d in docinfo.comments) + + xhiveframework.utils.add_user_info(users, docinfo.user_info) + + +@xhiveframework.whitelist() +def get_user_info_for_viewers(users): + user_info = {} + for user in json.loads(users): + xhiveframework.utils.add_user_info(user, user_info) + + return user_info diff --git a/xhiveframework/desk/form/meta.py b/xhiveframework/desk/form/meta.py new file mode 100644 index 0000000..2fcbb43 --- /dev/null +++ b/xhiveframework/desk/form/meta.py @@ -0,0 +1,314 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import io +import os + +import xhiveframework +from xhiveframework import _ +from xhiveframework.build import scrub_html_template +from xhiveframework.model.meta import Meta +from xhiveframework.model.utils import render_include +from xhiveframework.modules import get_module_path, load_doctype_module, scrub +from xhiveframework.utils import get_bench_path, get_html_format +from xhiveframework.utils.data import get_link_to_form + +ASSET_KEYS = ( + "__js", + "__css", + "__list_js", + "__calendar_js", + "__map_js", + "__linked_with", + "__messages", + "__print_formats", + "__workflow_docs", + "__form_grid_templates", + "__listview_template", + "__tree_js", + "__dashboard", + "__kanban_column_fields", + "__templates", + "__custom_js", + "__custom_list_js", +) + + +def get_meta(doctype, cached=True) -> "FormMeta": + # don't cache for developer mode as js files, templates may be edited + cached = cached and not xhiveframework.conf.developer_mode + if cached: + meta = xhiveframework.cache.hget("doctype_form_meta", doctype) + if not meta: + # Cache miss - explicitly get meta from DB to avoid + meta = FormMeta(doctype, cached=False) + xhiveframework.cache.hset("doctype_form_meta", doctype, meta) + else: + meta = FormMeta(doctype) + + if xhiveframework.local.lang != "en": + meta.set_translations(xhiveframework.local.lang) + + return meta + + +class FormMeta(Meta): + def __init__(self, doctype, *, cached=True): + self.__dict__.update(xhiveframework.get_meta(doctype, cached=cached).__dict__) + self.load_assets() + + def load_assets(self): + if self.get("__assets_loaded", False): + return + + self.add_search_fields() + self.add_linked_document_type() + + if not self.istable: + self.add_code() + self.add_custom_script() + self.load_print_formats() + self.load_workflows() + self.load_templates() + self.load_dashboard() + self.load_kanban_meta() + + self.set("__assets_loaded", True) + + def as_dict(self, no_nulls=False): + d = super().as_dict(no_nulls=no_nulls) + + for k in ASSET_KEYS: + d[k] = self.get(k) + + # d['fields'] = d.get('fields', []) + + for i, df in enumerate(d.get("fields") or []): + for k in ("search_fields", "is_custom_field", "linked_document_type"): + df[k] = self.get("fields")[i].get(k) + + return d + + def add_code(self): + if self.custom: + return + + path = os.path.join(get_module_path(self.module), "doctype", scrub(self.name)) + + def _get_path(fname): + return os.path.join(path, scrub(fname)) + + system_country = xhiveframework.get_system_settings("country") + + self._add_code(_get_path(self.name + ".js"), "__js") + if system_country: + self._add_code(_get_path(os.path.join("regional", system_country + ".js")), "__js") + + self._add_code(_get_path(self.name + ".css"), "__css") + self._add_code(_get_path(self.name + "_list.js"), "__list_js") + if system_country: + self._add_code(_get_path(os.path.join("regional", system_country + "_list.js")), "__list_js") + + self._add_code(_get_path(self.name + "_calendar.js"), "__calendar_js") + self._add_code(_get_path(self.name + "_tree.js"), "__tree_js") + + listview_template = _get_path(self.name + "_list.html") + if os.path.exists(listview_template): + self.set("__listview_template", get_html_format(listview_template)) + + self.add_code_via_hook("doctype_js", "__js") + self.add_code_via_hook("doctype_list_js", "__list_js") + self.add_code_via_hook("doctype_tree_js", "__tree_js") + self.add_code_via_hook("doctype_calendar_js", "__calendar_js") + self.add_html_templates(path) + + def _add_code(self, path, fieldname): + js = get_js(path) + if js: + bench_path = get_bench_path() + "/" + asset_path = path.replace(bench_path, "") + comment = f"\n\n/* Adding {asset_path} */\n\n" + sourceURL = f"\n\n//# sourceURL={scrub(self.name) + fieldname}" + self.set(fieldname, (self.get(fieldname) or "") + comment + js + sourceURL) + + def add_html_templates(self, path): + if self.custom: + return + templates = dict() + for fname in os.listdir(path): + if fname.endswith(".html"): + with open(os.path.join(path, fname), encoding="utf-8") as f: + templates[fname.split(".", 1)[0]] = scrub_html_template(f.read()) + + self.set("__templates", templates or None) + + def add_code_via_hook(self, hook, fieldname): + for path in get_code_files_via_hooks(hook, self.name): + self._add_code(path, fieldname) + + def add_custom_script(self): + """embed all require files""" + # custom script + client_scripts = ( + xhiveframework.get_all( + "Client Script", + filters={"dt": self.name, "enabled": 1}, + fields=["name", "script", "view"], + order_by="creation asc", + ) + or "" + ) + + list_script = "" + form_script = "" + for script in client_scripts: + if not script.script: + continue + + if script.view == "List": + list_script += f""" +// {script.name} +{script.script} + +""" + + elif script.view == "Form": + form_script += f""" +// {script.name} +{script.script} + +""" + + file = scrub(self.name) + form_script += f"\n\n//# sourceURL={file}__custom_js" + list_script += f"\n\n//# sourceURL={file}__custom_list_js" + + self.set("__custom_js", form_script) + self.set("__custom_list_js", list_script) + + def add_search_fields(self): + """add search fields found in the doctypes indicated by link fields' options""" + for df in self.get("fields", {"fieldtype": "Link", "options": ["!=", "[Select]"]}): + if df.options: + try: + search_fields = xhiveframework.get_meta(df.options).search_fields + except xhiveframework.DoesNotExistError: + self._show_missing_doctype_msg(df) + + if search_fields: + search_fields = search_fields.split(",") + df.search_fields = [sf.strip() for sf in search_fields] + + def _show_missing_doctype_msg(self, df): + # A link field is referring to non-existing doctype, this usually happens when + # customizations are removed or some custom app is removed but hasn't cleaned + # up after itself. + xhiveframework.clear_last_message() + + msg = _("Field {0} is referring to non-existing doctype {1}.").format( + xhiveframework.bold(df.fieldname), xhiveframework.bold(df.options) + ) + + if df.get("is_custom_field"): + custom_field_link = get_link_to_form("Custom Field", df.name) + msg += " " + _("Please delete the field from {0} or add the required doctype.").format( + custom_field_link + ) + + xhiveframework.throw(msg, title=_("Missing DocType")) + + def add_linked_document_type(self): + for df in self.get("fields", {"fieldtype": "Link"}): + if df.options: + try: + df.linked_document_type = xhiveframework.get_meta(df.options).document_type + except xhiveframework.DoesNotExistError: + self._show_missing_doctype_msg(df) + + def load_print_formats(self): + print_formats = xhiveframework.db.sql( + """select * FROM `tabPrint Format` + WHERE doc_type=%s AND docstatus<2 and disabled=0""", + (self.name,), + as_dict=1, + update={"doctype": "Print Format"}, + ) + + self.set("__print_formats", print_formats) + + def load_workflows(self): + # get active workflow + workflow_name = self.get_workflow() + workflow_docs = [] + + if workflow_name and xhiveframework.db.exists("Workflow", workflow_name): + workflow = xhiveframework.get_doc("Workflow", workflow_name) + workflow_docs.append(workflow) + + workflow_docs.extend(xhiveframework.get_doc("Workflow State", d.state) for d in workflow.get("states")) + self.set("__workflow_docs", workflow_docs) + + def load_templates(self): + if not self.custom: + module = load_doctype_module(self.name) + app = module.__name__.split(".", 1)[0] + templates = {} + if hasattr(module, "form_grid_templates"): + for key, path in module.form_grid_templates.items(): + templates[key] = get_html_format(xhiveframework.get_app_path(app, path)) + + self.set("__form_grid_templates", templates) + + def set_translations(self, lang): + from xhiveframework.translate import extract_messages_from_code, make_dict_from_messages + + self.set("__messages", xhiveframework.get_lang_dict("doctype", self.name)) + + # set translations for grid templates + if self.get("__form_grid_templates"): + for content in self.get("__form_grid_templates").values(): + messages = extract_messages_from_code(content) + messages = make_dict_from_messages(messages) + self.get("__messages").update(messages) + + def load_dashboard(self): + self.set("__dashboard", self.get_dashboard_data()) + + def load_kanban_meta(self): + self.load_kanban_column_fields() + + def load_kanban_column_fields(self): + try: + values = xhiveframework.get_list( + "Kanban Board", fields=["field_name"], filters={"reference_doctype": self.name} + ) + + fields = [x["field_name"] for x in values] + fields = list(set(fields)) + self.set("__kanban_column_fields", fields) + except xhiveframework.PermissionError: + # no access to kanban board + pass + + +def get_code_files_via_hooks(hook, name): + code_files = [] + for app_name in xhiveframework.get_installed_apps(): + code_hook = xhiveframework.get_hooks(hook, default={}, app_name=app_name) + if not code_hook: + continue + + files = code_hook.get(name, []) + if not isinstance(files, list): + files = [files] + + for file in files: + path = xhiveframework.get_app_path(app_name, *file.strip("/").split("/")) + code_files.append(path) + + return code_files + + +def get_js(path): + js = xhiveframework.read_file(path) + if js: + return render_include(js) diff --git a/xhiveframework/desk/form/save.py b/xhiveframework/desk/form/save.py new file mode 100644 index 0000000..d29d45e --- /dev/null +++ b/xhiveframework/desk/form/save.py @@ -0,0 +1,85 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.core.doctype.submission_queue.submission_queue import queue_submission +from xhiveframework.desk.form.load import run_onload +from xhiveframework.model.docstatus import DocStatus +from xhiveframework.monitor import add_data_to_monitor +from xhiveframework.utils.scheduler import is_scheduler_inactive +from xhiveframework.utils.telemetry import capture_doc + + +@xhiveframework.whitelist() +def savedocs(doc, action): + """save / submit / update doclist""" + doc = xhiveframework.get_doc(json.loads(doc)) + capture_doc(doc, action) + if doc.get("__islocal") and doc.name.startswith("new-" + doc.doctype.lower().replace(" ", "-")): + # required to relink missing attachments if they exist. + doc.__temporary_name = doc.name + set_local_name(doc) + + # action + doc.docstatus = { + "Save": DocStatus.draft(), + "Submit": DocStatus.submitted(), + "Update": DocStatus.submitted(), + "Cancel": DocStatus.cancelled(), + }[action] + + if doc.docstatus.is_submitted(): + if action == "Submit" and doc.meta.queue_in_background and not is_scheduler_inactive(): + queue_submission(doc, action) + return + doc.submit() + else: + doc.save() + + # update recent documents + run_onload(doc) + send_updated_docs(doc) + + add_data_to_monitor(doctype=doc.doctype, action=action) + xhiveframework.msgprint(xhiveframework._("Saved"), indicator="green", alert=True) + + +@xhiveframework.whitelist() +def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_state=None): + """cancel a doclist""" + doc = xhiveframework.get_doc(doctype, name) + capture_doc(doc, "Cancel") + + if workflow_state_fieldname and workflow_state: + doc.set(workflow_state_fieldname, workflow_state) + doc.cancel() + send_updated_docs(doc) + xhiveframework.msgprint(xhiveframework._("Cancelled"), indicator="red", alert=True) + + +def send_updated_docs(doc): + from .load import get_docinfo + + get_docinfo(doc) + + d = doc.as_dict() + if hasattr(doc, "localname"): + d["localname"] = doc.localname + + xhiveframework.response.docs.append(d) + + +def set_local_name(doc): + def _set_local_name(d): + if doc.get("__islocal") or d.get("__islocal"): + d.localname = d.name + d.name = None + + _set_local_name(doc) + for child in doc.get_all_children(): + _set_local_name(child) + + if doc.get("__newname"): + doc.name = doc.get("__newname") diff --git a/xhiveframework/desk/form/test_form.py b/xhiveframework/desk/form/test_form.py new file mode 100644 index 0000000..e59197f --- /dev/null +++ b/xhiveframework/desk/form/test_form.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.desk.form.linked_with import get_linked_docs, get_linked_doctypes +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestForm(XhiveFrameworkTestCase): + def test_linked_with(self): + results = get_linked_docs("Role", "System Manager", linkinfo=get_linked_doctypes("Role")) + self.assertTrue("User" in results) + self.assertTrue("DocType" in results) + + +if __name__ == "__main__": + import unittest + + xhiveframework.connect() + unittest.main() diff --git a/xhiveframework/desk/form/utils.py b/xhiveframework/desk/form/utils.py new file mode 100644 index 0000000..ee48665 --- /dev/null +++ b/xhiveframework/desk/form/utils.py @@ -0,0 +1,108 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +from typing import TYPE_CHECKING + +import xhiveframework +import xhiveframework.desk.form.load +import xhiveframework.desk.form.meta +from xhiveframework import _ +from xhiveframework.core.doctype.file.utils import extract_images_from_html +from xhiveframework.desk.form.document_follow import follow_document + +if TYPE_CHECKING: + from xhiveframework.core.doctype.comment.comment import Comment + + +@xhiveframework.whitelist(methods=["DELETE", "POST"]) +def remove_attach(): + """remove attachment""" + fid = xhiveframework.form_dict.get("fid") + xhiveframework.delete_doc("File", fid) + + +@xhiveframework.whitelist(methods=["POST", "PUT"]) +def add_comment( + reference_doctype: str, reference_name: str, content: str, comment_email: str, comment_by: str +) -> "Comment": + """Allow logged user with permission to read document to add a comment""" + reference_doc = xhiveframework.get_doc(reference_doctype, reference_name) + reference_doc.check_permission() + + comment = xhiveframework.new_doc("Comment") + comment.update( + { + "comment_type": "Comment", + "reference_doctype": reference_doctype, + "reference_name": reference_name, + "comment_email": comment_email, + "comment_by": comment_by, + "content": extract_images_from_html(reference_doc, content, is_private=True), + } + ) + comment.insert(ignore_permissions=True) + + if xhiveframework.get_cached_value("User", xhiveframework.session.user, "follow_commented_documents"): + follow_document(comment.reference_doctype, comment.reference_name, xhiveframework.session.user) + + return comment + + +@xhiveframework.whitelist() +def update_comment(name, content): + """allow only owner to update comment""" + doc = xhiveframework.get_doc("Comment", name) + + if xhiveframework.session.user not in ["Administrator", doc.owner]: + xhiveframework.throw(_("Comment can only be edited by the owner"), xhiveframework.PermissionError) + + if doc.reference_doctype and doc.reference_name: + reference_doc = xhiveframework.get_doc(doc.reference_doctype, doc.reference_name) + reference_doc.check_permission() + + doc.content = extract_images_from_html(reference_doc, content, is_private=True) + else: + doc.content = content + + doc.save(ignore_permissions=True) + + +@xhiveframework.whitelist() +def get_next(doctype, value, prev, filters=None, sort_order="desc", sort_field="modified"): + prev = int(prev) + if not filters: + filters = [] + if isinstance(filters, str): + filters = json.loads(filters) + + # # condition based on sort order + condition = ">" if sort_order.lower() == "asc" else "<" + + # switch the condition + if prev: + sort_order = "asc" if sort_order.lower() == "desc" else "desc" + condition = "<" if condition == ">" else ">" + + # # add condition for next or prev item + filters.append([doctype, sort_field, condition, xhiveframework.get_value(doctype, value, sort_field)]) + + res = xhiveframework.get_list( + doctype, + fields=["name"], + filters=filters, + order_by=f"`tab{doctype}`.{sort_field}" + " " + sort_order, + limit_start=0, + limit_page_length=1, + as_list=True, + ) + + if not res: + xhiveframework.msgprint(_("No further records")) + return None + else: + return res[0][0] + + +def get_pdf_link(doctype, docname, print_format="Standard", no_letterhead=0): + return f"/api/method/xhiveframework.utils.print_format.download_pdf?doctype={doctype}&name={docname}&format={print_format}&no_letterhead={no_letterhead}" diff --git a/xhiveframework/desk/gantt.py b/xhiveframework/desk/gantt.py new file mode 100644 index 0000000..f368ce1 --- /dev/null +++ b/xhiveframework/desk/gantt.py @@ -0,0 +1,17 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework + + +@xhiveframework.whitelist() +def update_task(args, field_map): + """Updates Doc (called via gantt) based on passed `field_map`""" + args = xhiveframework._dict(json.loads(args)) + field_map = xhiveframework._dict(json.loads(field_map)) + d = xhiveframework.get_doc(args.doctype, args.name) + d.set(field_map.start, args.start) + d.set(field_map.end, args.end) + d.save() diff --git a/xhiveframework/desk/leaderboard.py b/xhiveframework/desk/leaderboard.py new file mode 100644 index 0000000..5c29e4e --- /dev/null +++ b/xhiveframework/desk/leaderboard.py @@ -0,0 +1,50 @@ +import xhiveframework +from xhiveframework.utils import get_fullname + + +def get_leaderboards(): + return { + "User": { + "fields": ["points"], + "method": "xhiveframework.desk.leaderboard.get_energy_point_leaderboard", + "company_disabled": 1, + "icon": "users", + } + } + + +@xhiveframework.whitelist() +def get_energy_point_leaderboard(date_range, company=None, field=None, limit=None): + users = xhiveframework.get_list( + "User", + filters={ + "name": ["not in", ["Administrator", "Guest"]], + "enabled": 1, + "user_type": ["!=", "Website User"], + }, + pluck="name", + ) + + filters = [["type", "!=", "Review"], ["user", "in", users]] + if date_range: + date_range = xhiveframework.parse_json(date_range) + filters.append(["creation", "between", [date_range[0], date_range[1]]]) + energy_point_users = xhiveframework.get_all( + "Energy Point Log", + fields=["user as name", "sum(points) as value"], + filters=filters, + group_by="user", + order_by="value desc", + ) + + energy_point_users_list = list(map(lambda x: x["name"], energy_point_users)) + for user in users: + if user not in energy_point_users_list: + energy_point_users.append({"name": user, "value": 0}) + + for user in energy_point_users: + user_id = user["name"] + user["name"] = get_fullname(user["name"]) + user["formatted_name"] = f'{get_fullname(user_id)}' + + return energy_point_users diff --git a/xhiveframework/desk/like.py b/xhiveframework/desk/like.py new file mode 100644 index 0000000..262b690 --- /dev/null +++ b/xhiveframework/desk/like.py @@ -0,0 +1,91 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +"""Allow adding of likes to documents""" + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.database.schema import add_column +from xhiveframework.desk.form.document_follow import follow_document +from xhiveframework.utils import get_link_to_form + + +@xhiveframework.whitelist() +def toggle_like(doctype, name, add=False): + """Adds / removes the current user in the `__liked_by` property of the given document. + If column does not exist, will add it in the database. + + The `_liked_by` property is always set from this function and is ignored if set via + Document API + + :param doctype: DocType of the document to like + :param name: Name of the document to like + :param add: `Yes` if like is to be added. If not `Yes` the like will be removed.""" + + _toggle_like(doctype, name, add) + + +def _toggle_like(doctype, name, add, user=None): + """Same as toggle_like but hides param `user` from API""" + + if not user: + user = xhiveframework.session.user + + try: + liked_by = xhiveframework.db.get_value(doctype, name, "_liked_by") + + if liked_by: + liked_by = json.loads(liked_by) + else: + liked_by = [] + + if add == "Yes": + if user not in liked_by: + liked_by.append(user) + add_comment(doctype, name) + if xhiveframework.get_cached_value("User", user, "follow_liked_documents"): + follow_document(doctype, name, user) + else: + if user in liked_by: + liked_by.remove(user) + remove_like(doctype, name) + + if xhiveframework.get_meta(doctype).issingle: + xhiveframework.db.set_single_value(doctype, "_liked_by", json.dumps(liked_by), update_modified=False) + else: + xhiveframework.db.set_value(doctype, name, "_liked_by", json.dumps(liked_by), update_modified=False) + + except xhiveframework.db.ProgrammingError as e: + if xhiveframework.db.is_column_missing(e): + add_column(doctype, "_liked_by", "Text") + _toggle_like(doctype, name, add, user) + else: + raise + + +def remove_like(doctype, name): + """Remove previous Like""" + # remove Comment + xhiveframework.delete_doc( + "Comment", + [ + c.name + for c in xhiveframework.get_all( + "Comment", + filters={ + "comment_type": "Like", + "reference_doctype": doctype, + "reference_name": name, + "owner": xhiveframework.session.user, + }, + ) + ], + ignore_permissions=True, + ) + + +def add_comment(doctype, name): + doc = xhiveframework.get_doc(doctype, name) + doc.add_comment("Like", _("Liked")) diff --git a/xhiveframework/desk/link_preview.py b/xhiveframework/desk/link_preview.py new file mode 100644 index 0000000..e891810 --- /dev/null +++ b/xhiveframework/desk/link_preview.py @@ -0,0 +1,52 @@ +import xhiveframework +from xhiveframework.model import no_value_fields, table_fields + + +@xhiveframework.whitelist() +def get_preview_data(doctype, docname): + preview_fields = [] + meta = xhiveframework.get_meta(doctype) + if not meta.show_preview_popup: + return + + preview_fields = [ + field.fieldname + for field in meta.fields + if field.in_preview and field.fieldtype not in no_value_fields and field.fieldtype not in table_fields + ] + + # no preview fields defined, build list from mandatory fields + if not preview_fields: + preview_fields = [ + field.fieldname for field in meta.fields if field.reqd and field.fieldtype not in table_fields + ] + + title_field = meta.get_title_field() + image_field = meta.image_field + + preview_fields.append(title_field) + preview_fields.append(image_field) + preview_fields.append("name") + + preview_data = xhiveframework.get_list(doctype, filters={"name": docname}, fields=preview_fields, limit=1) + + if not preview_data: + return + + preview_data = preview_data[0] + + formatted_preview_data = { + "preview_image": preview_data.get(image_field), + "preview_title": preview_data.get(title_field), + "name": preview_data.get("name"), + } + + for key, val in preview_data.items(): + if val and meta.has_field(key) and key not in [image_field, title_field, "name"]: + formatted_preview_data[meta.get_field(key).label] = xhiveframework.format( + val, + meta.get_field(key).fieldtype, + translated=True, + ) + + return formatted_preview_data diff --git a/xhiveframework/desk/listview.py b/xhiveframework/desk/listview.py new file mode 100644 index 0000000..211ceef --- /dev/null +++ b/xhiveframework/desk/listview.py @@ -0,0 +1,73 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model import is_default_field +from xhiveframework.query_builder import Order +from xhiveframework.query_builder.functions import Count +from xhiveframework.query_builder.terms import SubQuery +from xhiveframework.query_builder.utils import DocType + + +@xhiveframework.whitelist() +def get_list_settings(doctype): + try: + return xhiveframework.get_cached_doc("List View Settings", doctype) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_messages() + + +@xhiveframework.whitelist() +def set_list_settings(doctype, values): + try: + doc = xhiveframework.get_doc("List View Settings", doctype) + except xhiveframework.DoesNotExistError: + doc = xhiveframework.new_doc("List View Settings") + doc.name = doctype + xhiveframework.clear_messages() + doc.update(xhiveframework.parse_json(values)) + doc.save() + + +@xhiveframework.whitelist() +def get_group_by_count(doctype: str, current_filters: str, field: str) -> list[dict]: + current_filters = xhiveframework.parse_json(current_filters) + + if field == "assigned_to": + ToDo = DocType("ToDo") + User = DocType("User") + count = Count("*").as_("count") + filtered_records = xhiveframework.qb.get_query( + doctype, + filters=current_filters, + fields=["name"], + validate_filters=True, + ) + + return ( + xhiveframework.qb.from_(ToDo) + .from_(User) + .select(ToDo.allocated_to.as_("name"), count) + .where( + (ToDo.status != "Cancelled") + & (ToDo.allocated_to == User.name) + & (User.user_type == "System User") + & (ToDo.reference_name.isin(SubQuery(filtered_records))) + ) + .groupby(ToDo.allocated_to) + .orderby(count, order=Order.desc) + .limit(50) + .run(as_dict=True) + ) + + if not xhiveframework.get_meta(doctype).has_field(field) and not is_default_field(field): + raise ValueError("Field does not belong to doctype") + + return xhiveframework.get_list( + doctype, + filters=current_filters, + group_by=f"`tab{doctype}`.{field}", + fields=["count(*) as count", f"`{field}` as name"], + order_by="count desc", + limit=50, + ) diff --git a/xhiveframework/desk/notifications.py b/xhiveframework/desk/notifications.py new file mode 100644 index 0000000..459c118 --- /dev/null +++ b/xhiveframework/desk/notifications.py @@ -0,0 +1,406 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +from bs4 import BeautifulSoup + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.doctype.notification_log.notification_log import ( + enqueue_create_notification, + get_title, + get_title_html, +) +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + get_subscribed_documents, +) +from xhiveframework.utils import get_fullname + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_notifications(): + out = { + "open_count_doctype": {}, + "targets": {}, + } + if xhiveframework.flags.in_install or not xhiveframework.db.get_single_value("System Settings", "setup_complete"): + return out + + config = get_notification_config() + + if not config: + return out + + groups = list(config.get("for_doctype")) + list(config.get("for_module")) + + notification_count = {} + notification_percent = {} + + for name in groups: + count = xhiveframework.cache.hget("notification_count:" + name, xhiveframework.session.user) + if count is not None: + notification_count[name] = count + + out["open_count_doctype"] = get_notifications_for_doctypes(config, notification_count) + out["targets"] = get_notifications_for_targets(config, notification_percent) + + return out + + +def get_notifications_for_doctypes(config, notification_count): + """Notifications for DocTypes""" + can_read = xhiveframework.get_user().get_can_read() + open_count_doctype = {} + + for d in config.for_doctype: + if d in can_read: + condition = config.for_doctype[d] + + if d in notification_count: + open_count_doctype[d] = notification_count[d] + else: + try: + if isinstance(condition, dict): + result = xhiveframework.get_list( + d, fields=["count(*) as count"], filters=condition, ignore_ifnull=True + )[0].count + else: + result = xhiveframework.get_attr(condition)() + + except xhiveframework.PermissionError: + xhiveframework.clear_messages() + pass + # xhiveframework.msgprint("Permission Error in notifications for {0}".format(d)) + + except Exception as e: + # OperationalError: (1412, 'Table definition has changed, please retry transaction') + # InternalError: (1684, 'Table definition is being modified by concurrent DDL statement') + if e.args and e.args[0] not in (1412, 1684): + raise + + else: + open_count_doctype[d] = result + xhiveframework.cache.hset("notification_count:" + d, xhiveframework.session.user, result) + + return open_count_doctype + + +def get_notifications_for_targets(config, notification_percent): + """Notifications for doc targets""" + can_read = xhiveframework.get_user().get_can_read() + doc_target_percents = {} + + # doc_target_percents = { + # "Company": { + # "Acme": 87, + # "RobotsRUs": 50, + # }, {}... + # } + + for doctype in config.targets: + if doctype in can_read: + if doctype in notification_percent: + doc_target_percents[doctype] = notification_percent[doctype] + else: + doc_target_percents[doctype] = {} + d = config.targets[doctype] + condition = d["filters"] + target_field = d["target_field"] + value_field = d["value_field"] + try: + if isinstance(condition, dict): + doc_list = xhiveframework.get_list( + doctype, + fields=["name", target_field, value_field], + filters=condition, + limit_page_length=100, + ignore_ifnull=True, + ) + + except xhiveframework.PermissionError: + xhiveframework.clear_messages() + pass + except Exception as e: + if e.args[0] not in (1412, 1684): + raise + + else: + for doc in doc_list: + value = doc[value_field] + target = doc[target_field] + doc_target_percents[doctype][doc.name] = ( + (value / target * 100) if value < target else 100 + ) + + return doc_target_percents + + +def clear_notifications(user=None): + if xhiveframework.flags.in_install: + return + config = get_notification_config() + + if not config: + return + + for_doctype = list(config.get("for_doctype")) if config.get("for_doctype") else [] + for_module = list(config.get("for_module")) if config.get("for_module") else [] + groups = for_doctype + for_module + + for name in groups: + if user: + xhiveframework.cache.hdel("notification_count:" + name, user) + else: + xhiveframework.cache.delete_key("notification_count:" + name) + + +def clear_notification_config(user): + xhiveframework.cache.hdel("notification_config", user) + + +def delete_notification_count_for(doctype): + xhiveframework.cache.delete_key("notification_count:" + doctype) + + +def clear_doctype_notifications(doc, method=None, *args, **kwargs): + config = get_notification_config() + if not config: + return + if isinstance(doc, str): + doctype = doc # assuming doctype name was passed directly + else: + doctype = doc.doctype + + if doctype in config.for_doctype: + delete_notification_count_for(doctype) + return + + +@xhiveframework.whitelist() +def get_notification_info(): + config = get_notification_config() + out = get_notifications() + can_read = xhiveframework.get_user().get_can_read() + conditions = {} + module_doctypes = {} + doctype_info = dict(xhiveframework.db.sql("""select name, module from tabDocType""")) + + for d in list(set(can_read + list(config.for_doctype))): + if d in config.for_doctype: + conditions[d] = config.for_doctype[d] + + if d in doctype_info: + module_doctypes.setdefault(doctype_info[d], []).append(d) + + out.update( + { + "conditions": conditions, + "module_doctypes": module_doctypes, + } + ) + + return out + + +def get_notification_config(): + user = xhiveframework.session.user or "Guest" + + def _get(): + subscribed_documents = get_subscribed_documents() + config = xhiveframework._dict() + hooks = xhiveframework.get_hooks() + if hooks: + for notification_config in hooks.notification_config: + nc = xhiveframework.get_attr(notification_config)() + for key in ("for_doctype", "for_module", "for_other", "targets"): + config.setdefault(key, {}) + if key == "for_doctype": + if len(subscribed_documents) > 0: + key_config = nc.get(key, {}) + subscribed_docs_config = xhiveframework._dict() + for document in subscribed_documents: + if key_config.get(document): + subscribed_docs_config[document] = key_config.get(document) + config[key].update(subscribed_docs_config) + else: + config[key].update(nc.get(key, {})) + else: + config[key].update(nc.get(key, {})) + return config + + return xhiveframework.cache.hget("notification_config", user, _get) + + +def get_filters_for(doctype): + """get open filters for doctype""" + config = get_notification_config() + doctype_config = config.get("for_doctype").get(doctype, {}) + return None if isinstance(doctype_config, str) else doctype_config + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_open_count(doctype: str, name: str, items=None): + """Get count for internal and external links for given transactions + + :param doctype: Reference DocType + :param name: Reference Name + :param items: Optional list of transactions (json/dict)""" + + if xhiveframework.flags.in_migrate or xhiveframework.flags.in_install: + return {"count": []} + + doc = xhiveframework.get_doc(doctype, name) + doc.check_permission() + meta = doc.meta + links = meta.get_dashboard_data() + + # compile all items in a list + if items is None: + items = [] + for group in links.transactions: + items.extend(group.get("items")) + + if not isinstance(items, list): + items = json.loads(items) + + out = { + "external_links_found": [], + "internal_links_found": [], + } + + for d in items: + internal_link_for_doctype = links.get("internal_links", {}).get(d) or links.get( + "internal_and_external_links", {} + ).get(d) + if internal_link_for_doctype: + internal_links_data_for_d = get_internal_links(doc, internal_link_for_doctype, d) + if internal_links_data_for_d["count"]: + out["internal_links_found"].append(internal_links_data_for_d) + else: + try: + external_links_data_for_d = get_external_links(d, name, links) + out["external_links_found"].append(external_links_data_for_d) + except Exception: + out["external_links_found"].append({"doctype": d, "open_count": 0, "count": 0}) + else: + external_links_data_for_d = get_external_links(d, name, links) + out["external_links_found"].append(external_links_data_for_d) + + out = { + "count": out, + } + + if not meta.custom: + module = xhiveframework.get_meta_module(doctype) + if hasattr(module, "get_timeline_data"): + out["timeline_data"] = module.get_timeline_data(doctype, name) + + return out + + +def get_internal_links(doc, link, link_doctype): + names = [] + data = {"doctype": link_doctype} + + if isinstance(link, str): + # get internal links in parent document + value = doc.get(link) + if value and value not in names: + names.append(value) + elif isinstance(link, list): + # get internal links in child documents + table_fieldname, link_fieldname = link + for row in doc.get(table_fieldname) or []: + value = row.get(link_fieldname) + if value and value not in names: + names.append(value) + + data["open_count"] = 0 + data["count"] = len(names) + data["names"] = names + + return data + + +def get_external_links(doctype, name, links): + filters = get_filters_for(doctype) + fieldname = links.get("non_standard_fieldnames", {}).get(doctype, links.get("fieldname")) + data = {"doctype": doctype} + + if filters: + # get the fieldname for the current document + # we only need open documents related to the current document + filters[fieldname] = name + total = len( + xhiveframework.get_all( + doctype, fields="name", filters=filters, limit=100, distinct=True, ignore_ifnull=True + ) + ) + data["open_count"] = total + else: + data["open_count"] = 0 + + total = len( + xhiveframework.get_all( + doctype, fields="name", filters={fieldname: name}, limit=100, distinct=True, ignore_ifnull=True + ) + ) + data["count"] = total + + return data + + +def notify_mentions(ref_doctype, ref_name, content): + if ref_doctype and ref_name and content: + mentions = extract_mentions(content) + + if not mentions: + return + + sender_fullname = get_fullname(xhiveframework.session.user) + title = get_title(ref_doctype, ref_name) + + recipients = [ + xhiveframework.db.get_value( + "User", + {"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1}, + "email", + ) + for name in mentions + ] + + notification_message = _("""{0} mentioned you in a comment in {1} {2}""").format( + xhiveframework.bold(sender_fullname), xhiveframework.bold(ref_doctype), get_title_html(title) + ) + + notification_doc = { + "type": "Mention", + "document_type": ref_doctype, + "document_name": ref_name, + "subject": notification_message, + "from_user": xhiveframework.session.user, + "email_content": content, + } + + enqueue_create_notification(recipients, notification_doc) + + +def extract_mentions(txt): + """Find all instances of @mentions in the html.""" + soup = BeautifulSoup(txt, "html.parser") + emails = [] + for mention in soup.find_all(class_="mention"): + if mention.get("data-is-group") == "true": + try: + user_group = xhiveframework.get_cached_doc("User Group", mention["data-id"]) + emails += [d.user for d in user_group.user_group_members] + except xhiveframework.DoesNotExistError: + pass + continue + email = mention["data-id"] + emails.append(email) + + return emails diff --git a/xhiveframework/desk/page/__init__.py b/xhiveframework/desk/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/page/backups/__init__.py b/xhiveframework/desk/page/backups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/page/backups/backups.css b/xhiveframework/desk/page/backups/backups.css new file mode 100644 index 0000000..32ccb88 --- /dev/null +++ b/xhiveframework/desk/page/backups/backups.css @@ -0,0 +1,14 @@ +.download-backups { + font-size: var(--text-base); +} + +.download-backup-card { + display: block; + text-decoration: none; + margin-bottom: var(--margin-lg); +} + +.download-backup-card:hover { + box-shadow: var(--shadow-md); + text-decoration: none; +} diff --git a/xhiveframework/desk/page/backups/backups.html b/xhiveframework/desk/page/backups/backups.html new file mode 100644 index 0000000..57ad061 --- /dev/null +++ b/xhiveframework/desk/page/backups/backups.html @@ -0,0 +1,27 @@ + + diff --git a/xhiveframework/desk/page/backups/backups.js b/xhiveframework/desk/page/backups/backups.js new file mode 100644 index 0000000..ac63662 --- /dev/null +++ b/xhiveframework/desk/page/backups/backups.js @@ -0,0 +1,45 @@ +xhiveframework.pages["backups"].on_page_load = function (wrapper) { + var page = xhiveframework.ui.make_app_page({ + parent: wrapper, + title: __("Download Backups"), + single_column: true, + }); + + page.add_inner_button(__("Set Number of Backups"), function () { + xhiveframework.set_route("Form", "System Settings"); + }); + + page.add_inner_button(__("Download Files Backup"), function () { + xhiveframework.call({ + method: "xhiveframework.desk.page.backups.backups.schedule_files_backup", + args: { user_email: xhiveframework.session.user_email }, + }); + }); + + page.add_inner_button(__("Get Backup Encryption Key"), function () { + if (xhiveframework.user.has_role("System Manager")) { + xhiveframework.verify_password(function () { + xhiveframework.call({ + method: "xhiveframework.utils.backups.get_backup_encryption_key", + callback: function (r) { + xhiveframework.msgprint({ + title: __("Backup Encryption Key"), + message: __(r.message), + indicator: "blue", + }); + }, + }); + }); + } else { + xhiveframework.msgprint({ + title: __("Error"), + message: __("System Manager privileges required."), + indicator: "red", + }); + } + }); + + xhiveframework.breadcrumbs.add("Setup"); + + $(xhiveframework.render_template("backups")).appendTo(page.body.addClass("no-border")); +}; diff --git a/xhiveframework/desk/page/backups/backups.json b/xhiveframework/desk/page/backups/backups.json new file mode 100644 index 0000000..dd6e8d9 --- /dev/null +++ b/xhiveframework/desk/page/backups/backups.json @@ -0,0 +1,21 @@ +{ + "content": null, + "creation": "2015-09-24 01:26:06.225378", + "docstatus": 0, + "doctype": "Page", + "modified": "2015-09-24 01:26:06.225378", + "modified_by": "Administrator", + "module": "Desk", + "name": "backups", + "owner": "Administrator", + "page_name": "backups", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "title": "Download Backups" +} \ No newline at end of file diff --git a/xhiveframework/desk/page/backups/backups.py b/xhiveframework/desk/page/backups/backups.py new file mode 100644 index 0000000..f3254ff --- /dev/null +++ b/xhiveframework/desk/page/backups/backups.py @@ -0,0 +1,120 @@ +import datetime +import os + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils import cint, get_site_path, get_url +from xhiveframework.utils.data import convert_utc_to_system_timezone + + +def get_context(context): + def get_time(path): + dt = os.path.getmtime(path) + return convert_utc_to_system_timezone(datetime.datetime.utcfromtimestamp(dt)).strftime( + "%a %b %d %H:%M %Y" + ) + + def get_encrytion_status(path): + if "-enc" in path: + return True + + def get_size(path): + size = os.path.getsize(path) + if size > 1048576: + return f"{float(size) / 1048576:.1f}M" + else: + return f"{float(size) / 1024:.1f}K" + + path = get_site_path("private", "backups") + files = [x for x in os.listdir(path) if os.path.isfile(os.path.join(path, x))] + backup_limit = get_scheduled_backup_limit() + + if len(files) > backup_limit: + cleanup_old_backups(path, files, backup_limit) + + files = [ + ( + "/backups/" + _file, + get_time(os.path.join(path, _file)), + get_encrytion_status(os.path.join(path, _file)), + get_size(os.path.join(path, _file)), + ) + for _file in files + if _file.endswith("sql.gz") + ] + files.sort(key=lambda x: x[1], reverse=True) + + return {"files": files[:backup_limit]} + + +def get_scheduled_backup_limit(): + backup_limit = xhiveframework.db.get_singles_value("System Settings", "backup_limit") + return cint(backup_limit) + + +def cleanup_old_backups(site_path, files, limit): + backup_paths = [] + for f in files: + if f.endswith("sql.gz"): + _path = os.path.abspath(os.path.join(site_path, f)) + backup_paths.append(_path) + + backup_paths = sorted(backup_paths, key=os.path.getctime) + files_to_delete = len(backup_paths) - limit + + for idx in range(0, files_to_delete): + f = os.path.basename(backup_paths[idx]) + files.remove(f) + + os.remove(backup_paths[idx]) + + +def delete_downloadable_backups(): + path = get_site_path("private", "backups") + files = [x for x in os.listdir(path) if os.path.isfile(os.path.join(path, x))] + backup_limit = get_scheduled_backup_limit() + + if len(files) > backup_limit: + cleanup_old_backups(path, files, backup_limit) + + +@xhiveframework.whitelist() +def schedule_files_backup(user_email): + from xhiveframework.utils.background_jobs import enqueue, get_jobs + + xhiveframework.only_for("System Manager") + + queued_jobs = get_jobs(site=xhiveframework.local.site, queue="long") + method = "xhiveframework.desk.page.backups.backups.backup_files_and_notify_user" + + if method not in queued_jobs[xhiveframework.local.site]: + enqueue( + "xhiveframework.desk.page.backups.backups.backup_files_and_notify_user", + queue="long", + user_email=user_email, + ) + xhiveframework.msgprint(_("Queued for backup. You will receive an email with the download link")) + else: + xhiveframework.msgprint(_("Backup job is already queued. You will receive an email with the download link")) + + +def backup_files_and_notify_user(user_email=None): + from xhiveframework.utils.backups import backup + + backup_files = backup(with_files=True) + get_downloadable_links(backup_files) + + subject = _("File backup is ready") + xhiveframework.sendmail( + recipients=[user_email], + subject=subject, + template="file_backup_notification", + args=backup_files, + header=[subject, "green"], + ) + + +def get_downloadable_links(backup_files): + for key in ["backup_path_files", "backup_path_private_files"]: + path = backup_files[key] + backup_files[key] = get_url("/".join(path.split("/")[-2:])) diff --git a/xhiveframework/desk/page/leaderboard/__init__.py b/xhiveframework/desk/page/leaderboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/page/leaderboard/leaderboard.css b/xhiveframework/desk/page/leaderboard/leaderboard.css new file mode 100644 index 0000000..d15b4ff --- /dev/null +++ b/xhiveframework/desk/page/leaderboard/leaderboard.css @@ -0,0 +1,85 @@ +.list-filters { + overflow-y: hidden; + padding: 5px +} + +.list-filter-item { + min-width: 150px; + float: left; + margin: 5px; +} + +.list-item_content { + flex: 1; + padding-right: 15px; + align-items: center; +} + +.select-time, .select-doctype, .select-filter, .select-sort { + background: #f0f4f7; +} + +.from-date-field .clearfix{ + display: none; +} + +.from-date-field { + margin-left: 10px; +} + +.select-time:focus, .select-doctype:focus, .select-filter:focus, .select-sort:focus { + background: #f0f4f7; +} + +.header-btn-base { + border: none; + outline: 0; + vertical-align: middle; + overflow: hidden; + text-decoration: none; + color: inherit; + background-color: inherit; + cursor: pointer; + white-space: nowrap; +} + +.header-btn-round { + border-radius: 4px; +} + +.item-title-bold { + font-weight: bold; +} + +.rank { + max-width: 100px; +} + +.leaderboard .result { + border-top: 1px solid var(--border-color); +} + +.leaderboard .list-item { + padding-left: 45px; +} + +.leaderboard .list-item_content { + padding-right: 60px; +} + +.leaderboard-sidebar { + padding-left: 0; + position: fixed; +} + +.leaderboard-list { + padding: var(-padding-sm) 0; + min-height: 70vh; +} + +.leaderboard-empty-state { + align-items: center; + height: 70vh; + justify-content: center; + display: flex; +} diff --git a/xhiveframework/desk/page/leaderboard/leaderboard.js b/xhiveframework/desk/page/leaderboard/leaderboard.js new file mode 100644 index 0000000..9fbf2eb --- /dev/null +++ b/xhiveframework/desk/page/leaderboard/leaderboard.js @@ -0,0 +1,409 @@ +xhiveframework.pages["leaderboard"].on_page_load = (wrapper) => { + xhiveframework.leaderboard = new Leaderboard(wrapper); + + $(wrapper).bind("show", () => { + // Get which leaderboard to show + let doctype = xhiveframework.get_route()[1]; + xhiveframework.leaderboard.show_leaderboard(doctype); + }); +}; + +class Leaderboard { + constructor(parent) { + xhiveframework.ui.make_app_page({ + parent: parent, + title: __("Leaderboard"), + single_column: false, + card_layout: true, + }); + + this.parent = parent; + this.page = this.parent.page; + this.page.sidebar.html( + `
      ` + ); + this.$sidebar_list = this.page.sidebar.find("ul"); + + this.get_leaderboard_config(); + } + + get_leaderboard_config() { + this.doctypes = []; + this.filters = {}; + this.leaderboard_limit = 20; + + xhiveframework + .xcall("xhiveframework.desk.page.leaderboard.leaderboard.get_leaderboard_config") + .then((config) => { + this.leaderboard_config = config; + for (let doctype in this.leaderboard_config) { + this.doctypes.push(doctype); + this.filters[doctype] = this.leaderboard_config[doctype].fields.map( + (field) => { + if (typeof field === "object") { + return field.label || field.fieldname; + } + return field; + } + ); + } + + // For translation. Do not remove this + // __("This Week"), __("This Month"), __("This Quarter"), __("This Year"), + // __("Last Week"), __("Last Month"), __("Last Quarter"), __("Last Year"), + // __("All Time"), __("Select From Date") + this.timespans = [ + "This Week", + "This Month", + "This Quarter", + "This Year", + "Last Week", + "Last Month", + "Last Quarter", + "Last Year", + "All Time", + "Select Date Range", + ]; + + // for saving current selected filters + const _initial_doctype = xhiveframework.get_route()[1] || this.doctypes[0]; + const _initial_timespan = this.timespans[0]; + const _initial_filter = this.filters[_initial_doctype]; + + this.options = { + selected_doctype: _initial_doctype, + selected_filter: _initial_filter, + selected_filter_item: _initial_filter[0], + selected_timespan: _initial_timespan, + }; + + this.message = null; + this.make(); + }); + } + + make() { + this.$container = $(`
      +
      +
      +
      `).appendTo(this.page.main); + + this.$graph_area = this.$container.find(".leaderboard-graph"); + + this.doctypes.map((doctype) => { + const icon = this.leaderboard_config[doctype].icon; + this.get_sidebar_item(doctype, icon).appendTo(this.$sidebar_list); + }); + + this.setup_leaderboard_fields(); + + this.render_selected_doctype(); + + this.render_search_box(); + + // Get which leaderboard to show + let doctype = xhiveframework.get_route()[1]; + this.show_leaderboard(doctype); + } + + setup_leaderboard_fields() { + this.company_select = this.page.add_field({ + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: xhiveframework.defaults.get_default("company"), + reqd: 1, + change: (e) => { + this.make_request(); + }, + }); + + this.timespan_select = this.page.add_select( + __("Timespan"), + this.timespans.map((d) => { + return { label: __(d), value: d }; + }) + ); + this.create_date_range_field(); + + this.type_select = this.page.add_select( + __("Field"), + this.options.selected_filter.map((d) => { + return { label: __(xhiveframework.model.unscrub(d)), value: d }; + }) + ); + + this.timespan_select.on("change", (e) => { + this.options.selected_timespan = e.currentTarget.value; + if (this.options.selected_timespan === "Select Date Range") { + this.date_range_field.show(); + } else { + this.date_range_field.hide(); + } + this.make_request(); + }); + + this.type_select.on("change", (e) => { + this.options.selected_filter_item = e.currentTarget.value; + this.make_request(); + }); + } + + create_date_range_field() { + let timespan_field = $(this.parent).find( + `.xhiveframework-control[data-original-title="${__("Timespan")}"]` + ); + this.date_range_field = $(`
      `) + .insertAfter(timespan_field) + .hide(); + + let date_field = xhiveframework.ui.form.make_control({ + df: { + fieldtype: "DateRange", + fieldname: "selected_date_range", + placeholder: __("Date Range"), + default: [xhiveframework.datetime.month_start(), xhiveframework.datetime.now_date()], + input_class: "input-xs", + reqd: 1, + change: () => { + this.selected_date_range = date_field.get_value(); + if (this.selected_date_range) this.make_request(); + }, + }, + parent: $(this.parent).find(".from-date-field"), + render_input: 1, + }); + } + + render_selected_doctype() { + this.$sidebar_list.on("click", "li", (e) => { + let $li = $(e.currentTarget); + let doctype = $li.find(".doctype-text").attr("doctype-value"); + + this.company_select.set_value( + xhiveframework.defaults.get_default("company") || this.company_select.get_value() + ); + this.options.selected_doctype = doctype; + this.options.selected_filter = this.filters[doctype]; + this.options.selected_filter_item = this.filters[doctype][0]; + + this.type_select.empty().add_options( + this.options.selected_filter.map((d) => { + return { label: __(xhiveframework.model.unscrub(d)), value: d }; + }) + ); + if (this.leaderboard_config[this.options.selected_doctype].company_disabled) { + $(this.parent).find("[data-original-title=Company]").hide(); + } else { + $(this.parent).find("[data-original-title=Company]").show(); + } + + this.$sidebar_list.find("li").removeClass("active selected"); + $li.addClass("active selected"); + + xhiveframework.set_route("leaderboard", this.options.selected_doctype); + this.make_request(); + }); + } + + render_search_box() { + this.$search_box = $(``); + + $(this.parent).find(".page-form").append(this.$search_box); + } + + show_leaderboard(doctype) { + if (this.doctypes.length) { + if (this.doctypes.includes(doctype)) { + this.options.selected_doctype = doctype; + this.$sidebar_list + .find(`[doctype-value = "${this.options.selected_doctype}"]`) + .trigger("click"); + } + + this.$search_box.find(".leaderboard-search-input").val(""); + xhiveframework.set_route("leaderboard", this.options.selected_doctype); + } + } + + make_request() { + xhiveframework.model.with_doctype(this.options.selected_doctype, () => { + this.get_leaderboard(this.get_leaderboard_data); + }); + } + + get_leaderboard(notify) { + let company = this.company_select.get_value(); + if (!company && !this.leaderboard_config[this.options.selected_doctype].company_disabled) { + notify(this, null); + xhiveframework.show_alert(__("Please select Company")); + return; + } + xhiveframework + .call(this.leaderboard_config[this.options.selected_doctype].method, { + date_range: this.get_date_range(), + company: company, + field: this.options.selected_filter_item, + limit: this.leaderboard_limit, + }) + .then((r) => { + let results = r.message || []; + + let graph_items = results.slice(0, 10); + + this.$graph_area.show().empty(); + + const custom_options = { + data: { + datasets: [{ values: graph_items.map((d) => d.value) }], + labels: graph_items.map((d) => d.name), + }, + format_tooltip_x: (d) => d[this.options.selected_filter_item], + height: 140, + }; + xhiveframework.utils.make_chart(".leaderboard-graph", custom_options); + + notify(this, r); + }); + } + + get_leaderboard_data(me, res) { + if (res && res.message.length) { + me.message = null; + me.$container.find(".leaderboard-list").html(me.render_list_view(res.message)); + xhiveframework.utils.setup_search($(me.parent), ".list-item-container", ".list-id"); + } else { + me.$graph_area.hide(); + me.message = __("No Items Found"); + me.$container.find(".leaderboard-list").html(me.render_list_view()); + } + } + + render_list_view(items = []) { + var html = `${this.render_message()} +
      + ${this.render_result(items)} +
      `; + + return $(html); + } + + render_result(items) { + var html = `${this.render_list_header()} + ${this.render_list_result(items)}`; + return html; + } + + render_list_header() { + const _selected_filter = this.options.selected_filter.map((i) => xhiveframework.model.unscrub(i)); + const fields = ["rank", "name", this.options.selected_filter_item]; + const filters = fields + .map((filter) => { + const col = __(xhiveframework.model.unscrub(filter)); + return `
      + + ${col} + +
      `; + }) + .join(""); + + return `
      +
      ${filters}
      +
      `; + } + + render_list_result(items) { + let _html = items + .map((item, index) => { + const $value = $(this.get_item_html(item, index + 1)); + const $item_container = $(`
      `).append($value); + return $item_container[0].outerHTML; + }) + .join(""); + + return `
      +
      + ${_html} +
      +
      `; + } + + render_message() { + const display_class = this.message ? "" : "hide"; + return `
      +
      + Empty State +
      ${this.message}
      +
      +
      `; + } + + get_item_html(item, index) { + const fields = this.leaderboard_config[this.options.selected_doctype].fields; + const value = xhiveframework.format( + item.value, + fields.find((field) => { + let fieldname = field.fieldname || field; + return fieldname === this.options.selected_filter_item; + }) + ); + + const link = `/app/${xhiveframework.router.slug(this.options.selected_doctype)}/${item.name}`; + const name_html = item.formatted_name + ? `${item.formatted_name}` + : ` ${item.name} `; + return `
      +
      + ${index} +
      +
      + ${name_html} +
      +
      + ${value} +
      +
      `; + } + + get_sidebar_item(item, icon) { + let icon_html = icon ? xhiveframework.utils.icon(icon, "md") : ""; + return $(`
    • + ${icon_html} + + ${__(item)} + +
    • `); + } + + get_date_range() { + let timespan = this.options.selected_timespan.toLowerCase(); + let current_date = xhiveframework.datetime.now_date(); + let date_range_map = { + "this week": [xhiveframework.datetime.week_start(), xhiveframework.datetime.week_end()], + "this month": [xhiveframework.datetime.month_start(), xhiveframework.datetime.month_end()], + "this quarter": [xhiveframework.datetime.quarter_start(), xhiveframework.datetime.quarter_end()], + "this year": [xhiveframework.datetime.year_start(), xhiveframework.datetime.year_end()], + "last week": [xhiveframework.datetime.add_days(current_date, -7), current_date], + "last month": [xhiveframework.datetime.add_months(current_date, -1), current_date], + "last quarter": [xhiveframework.datetime.add_months(current_date, -3), current_date], + "last year": [xhiveframework.datetime.add_months(current_date, -12), current_date], + "all time": null, + "select date range": this.selected_date_range || [ + xhiveframework.datetime.month_start(), + current_date, + ], + }; + return date_range_map[timespan]; + } +} diff --git a/xhiveframework/desk/page/leaderboard/leaderboard.json b/xhiveframework/desk/page/leaderboard/leaderboard.json new file mode 100644 index 0000000..0f0b8d8 --- /dev/null +++ b/xhiveframework/desk/page/leaderboard/leaderboard.json @@ -0,0 +1,19 @@ +{ + "content": null, + "creation": "2017-06-06 02:54:24.785360", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2019-09-27 17:44:51.909947", + "modified_by": "Administrator", + "module": "Desk", + "name": "leaderboard", + "owner": "Administrator", + "page_name": "leaderboard", + "roles": [], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Leaderboard" +} \ No newline at end of file diff --git a/xhiveframework/desk/page/leaderboard/leaderboard.py b/xhiveframework/desk/page/leaderboard/leaderboard.py new file mode 100644 index 0000000..71ec4d8 --- /dev/null +++ b/xhiveframework/desk/page/leaderboard/leaderboard.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework + + +@xhiveframework.whitelist() +def get_leaderboard_config(): + leaderboard_config = xhiveframework._dict() + leaderboard_hooks = xhiveframework.get_hooks("leaderboards") + for hook in leaderboard_hooks: + leaderboard_config.update(xhiveframework.get_attr(hook)()) + + return leaderboard_config diff --git a/xhiveframework/desk/page/setup_wizard/__init__.py b/xhiveframework/desk/page/setup_wizard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/page/setup_wizard/install_fixtures.py b/xhiveframework/desk/page/setup_wizard/install_fixtures.py new file mode 100644 index 0000000..2d47213 --- /dev/null +++ b/xhiveframework/desk/page/setup_wizard/install_fixtures.py @@ -0,0 +1,64 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.desk.doctype.global_search_settings.global_search_settings import ( + update_global_search_doctypes, +) +from xhiveframework.utils.dashboard import sync_dashboards + + +def install(): + update_genders() + update_salutations() + update_global_search_doctypes() + setup_email_linking() + sync_dashboards() + add_unsubscribe() + + +def update_genders(): + default_genders = [ + "Male", + "Female", + "Other", + "Transgender", + "Genderqueer", + "Non-Conforming", + "Prefer not to say", + ] + records = [{"doctype": "Gender", "gender": d} for d in default_genders] + for record in records: + xhiveframework.get_doc(record).insert(ignore_permissions=True, ignore_if_duplicate=True) + + +def update_salutations(): + default_salutations = ["Mr", "Ms", "Mx", "Dr", "Mrs", "Madam", "Miss", "Master", "Prof"] + records = [{"doctype": "Salutation", "salutation": d} for d in default_salutations] + for record in records: + doc = xhiveframework.new_doc(record.get("doctype")) + doc.update(record) + doc.insert(ignore_permissions=True, ignore_if_duplicate=True) + + +def setup_email_linking(): + doc = xhiveframework.get_doc( + { + "doctype": "Email Account", + "email_id": "email_linking@example.com", + } + ) + doc.insert(ignore_permissions=True, ignore_if_duplicate=True) + + +def add_unsubscribe(): + email_unsubscribe = [ + {"email": "admin@example.com", "global_unsubscribe": 1}, + {"email": "guest@example.com", "global_unsubscribe": 1}, + ] + + for unsubscribe in email_unsubscribe: + if not xhiveframework.get_all("Email Unsubscribe", filters=unsubscribe): + doc = xhiveframework.new_doc("Email Unsubscribe") + doc.update(unsubscribe) + doc.insert(ignore_permissions=True) diff --git a/xhiveframework/desk/page/setup_wizard/setup_wizard.js b/xhiveframework/desk/page/setup_wizard/setup_wizard.js new file mode 100644 index 0000000..0087724 --- /dev/null +++ b/xhiveframework/desk/page/setup_wizard/setup_wizard.js @@ -0,0 +1,674 @@ +xhiveframework.provide("xhiveframework.setup"); +xhiveframework.provide("xhiveframework.setup.events"); +xhiveframework.provide("xhiveframework.ui"); + +xhiveframework.setup = { + slides: [], + events: {}, + data: {}, + utils: {}, + domains: [], + + on: function (event, fn) { + if (!xhiveframework.setup.events[event]) { + xhiveframework.setup.events[event] = []; + } + xhiveframework.setup.events[event].push(fn); + }, + add_slide: function (slide) { + xhiveframework.setup.slides.push(slide); + }, + + remove_slide: function (slide_name) { + xhiveframework.setup.slides = xhiveframework.setup.slides.filter((slide) => slide.name !== slide_name); + }, + + run_event: function (event) { + $.each(xhiveframework.setup.events[event] || [], function (i, fn) { + fn(); + }); + }, +}; + +xhiveframework.pages["setup-wizard"].on_page_load = function (wrapper) { + if (xhiveframework.boot.setup_complete) { + window.location.href = "/app"; + } + let requires = xhiveframework.boot.setup_wizard_requires || []; + xhiveframework.require(requires, function () { + xhiveframework.call({ + method: "xhiveframework.desk.page.setup_wizard.setup_wizard.load_languages", + freeze: true, + callback: function (r) { + xhiveframework.setup.data.lang = r.message; + + xhiveframework.setup.run_event("before_load"); + var wizard_settings = { + parent: wrapper, + slides: xhiveframework.setup.slides, + slide_class: xhiveframework.setup.SetupWizardSlide, + unidirectional: 1, + done_state: 1, + }; + xhiveframework.wizard = new xhiveframework.setup.SetupWizard(wizard_settings); + xhiveframework.setup.run_event("after_load"); + xhiveframework.wizard.show_slide(cint(xhiveframework.get_route()[1])); + }, + }); + }); +}; + +xhiveframework.pages["setup-wizard"].on_page_show = function () { + xhiveframework.wizard && xhiveframework.wizard.show_slide(cint(xhiveframework.get_route()[1])); +}; + +xhiveframework.setup.on("before_load", function () { + // load slides + xhiveframework.setup.slides_settings.forEach((s) => { + if (!(s.name === "user" && xhiveframework.boot.developer_mode)) { + // if not user slide with developer mode + xhiveframework.setup.add_slide(s); + } + }); +}); + +xhiveframework.setup.SetupWizard = class SetupWizard extends xhiveframework.ui.Slides { + constructor(args = {}) { + super(args); + $.extend(this, args); + + this.page_name = "setup-wizard"; + this.welcomed = true; + xhiveframework.set_route("setup-wizard/0"); + } + + make() { + super.make(); + this.container.addClass("container setup-wizard-slide with-form"); + this.$next_btn.addClass("action"); + this.$complete_btn.addClass("action"); + this.setup_keyboard_nav(); + } + + setup_keyboard_nav() { + $("body").on("keydown", this.handle_enter_press.bind(this)); + } + + disable_keyboard_nav() { + $("body").off("keydown", this.handle_enter_press.bind(this)); + } + + handle_enter_press(e) { + if (e.which === xhiveframework.ui.keyCode.ENTER) { + let $target = $(e.target); + if ($target.hasClass("prev-btn") || $target.hasClass("next-btn")) { + $target.trigger("click"); + } else { + // hitting enter on autocomplete field shouldn't trigger next slide. + if ($target.data().fieldtype == "Autocomplete") return; + + this.container.find(".next-btn").trigger("click"); + e.preventDefault(); + } + } + } + + before_show_slide() { + if (!this.welcomed) { + xhiveframework.set_route(this.page_name); + return false; + } + return true; + } + + show_slide(id) { + if (id === this.slides.length) { + return; + } + super.show_slide(id); + xhiveframework.set_route(this.page_name, cstr(id)); + } + + show_hide_prev_next(id) { + super.show_hide_prev_next(id); + if (id + 1 === this.slides.length) { + this.$next_btn.removeClass("btn-primary").hide(); + this.$complete_btn + .addClass("btn-primary") + .show() + .on("click", () => this.action_on_complete()); + } else { + this.$next_btn.addClass("btn-primary").show(); + this.$complete_btn.removeClass("btn-primary").hide(); + } + } + + refresh_slides() { + // For Translations, etc. + if (this.in_refresh_slides || !this.current_slide.set_values(true)) { + return; + } + this.in_refresh_slides = true; + + this.update_values(); + xhiveframework.setup.slides = []; + xhiveframework.setup.run_event("before_load"); + + xhiveframework.setup.slides = this.get_setup_slides_filtered_by_domain(); + + this.slides = xhiveframework.setup.slides; + xhiveframework.setup.run_event("after_load"); + + // re-render all slide, only remake made slides + $.each(this.slide_dict, (id, slide) => { + if (slide.made) { + this.made_slide_ids.push(id); + } + }); + this.made_slide_ids.push(this.current_id); + this.setup(); + + this.show_slide(this.current_id); + this.refresh(this.current_id); + setTimeout(() => { + this.container.find(".form-control").first().focus(); + }, 200); + this.in_refresh_slides = false; + } + + action_on_complete() { + xhiveframework.telemetry.capture("initated_client_side", "setup"); + if (!this.current_slide.set_values()) return; + this.update_values(); + this.show_working_state(); + this.disable_keyboard_nav(); + this.listen_for_setup_stages(); + + return xhiveframework.call({ + method: "xhiveframework.desk.page.setup_wizard.setup_wizard.setup_complete", + args: { args: this.values }, + callback: (r) => { + if (r.message.status === "ok") { + this.post_setup_success(); + } else if (r.message.status === "registered") { + this.update_setup_message(__("starting the setup...")); + } else if (r.message.fail !== undefined) { + this.abort_setup(r.message.fail); + } + }, + error: () => this.abort_setup(), + }); + } + + post_setup_success() { + this.set_setup_complete_message(__("Setup Complete"), __("Refreshing...")); + if (xhiveframework.setup.welcome_page) { + localStorage.setItem("session_last_route", xhiveframework.setup.welcome_page); + } + setTimeout(function () { + // Reload + window.location.href = "/app"; + }, 2000); + } + + abort_setup(fail_msg) { + this.$working_state.find(".state-icon-container").html(""); + fail_msg = fail_msg + ? fail_msg + : xhiveframework.last_response.setup_wizard_failure_message + ? xhiveframework.last_response.setup_wizard_failure_message + : __("Failed to complete setup"); + + this.update_setup_message("Could not start up: " + fail_msg); + + this.$working_state.find(".title").html("Setup failed"); + + this.$abort_btn.show(); + } + + listen_for_setup_stages() { + xhiveframework.realtime.on("setup_task", (data) => { + // console.log('data', data); + if (data.stage_status) { + // .html('Process '+ data.progress[0] + ' of ' + data.progress[1] + ': ' + data.stage_status); + this.update_setup_message(data.stage_status); + this.set_setup_load_percent(((data.progress[0] + 1) / data.progress[1]) * 100); + } + if (data.fail_msg) { + this.abort_setup(data.fail_msg); + } + if (data.status === "ok") { + this.post_setup_success(); + } + }); + } + + update_setup_message(message) { + this.$working_state.find(".setup-message").html(message); + } + + get_setup_slides_filtered_by_domain() { + let filtered_slides = []; + xhiveframework.setup.slides.forEach(function (slide) { + if (xhiveframework.setup.domains) { + let active_domains = xhiveframework.setup.domains; + if ( + !slide.domains || + slide.domains.filter((d) => active_domains.includes(d)).length > 0 + ) { + filtered_slides.push(slide); + } + } else { + filtered_slides.push(slide); + } + }); + return filtered_slides; + } + + show_working_state() { + this.container.hide(); + xhiveframework.set_route(this.page_name); + + this.$working_state = this.get_message( + __("Setting up your system"), + __("Starting XhiveFramework ...") + ).appendTo(this.parent); + + this.attach_abort_button(); + + this.current_id = this.slides.length; + this.current_slide = null; + } + + attach_abort_button() { + this.$abort_btn = $( + `` + ); + this.$working_state.find(".content").append(this.$abort_btn); + + this.$abort_btn.on("click", () => { + $(this.parent).find(".setup-in-progress").remove(); + this.container.show(); + xhiveframework.set_route(this.page_name, this.slides.length - 1); + }); + + this.$abort_btn.hide(); + } + + get_message(title, message = "") { + const loading_html = `
      +
      +
      +
      +
      `; + + return $(`
      +
      +

      ${title}

      +
      ${loading_html}
      +

      ${message}

      +
      +
      `); + } + + set_setup_complete_message(title, message) { + this.$working_state.find(".title").html(title); + this.$working_state.find(".setup-message").html(message); + } + + set_setup_load_percent(percent) { + this.$working_state.find(".progress-bar").css({ width: percent + "%" }); + } +}; + +xhiveframework.setup.SetupWizardSlide = class SetupWizardSlide extends xhiveframework.ui.Slide { + constructor(slide = null) { + super(slide); + } + + make() { + super.make(); + this.set_init_values(); + this.setup_telemetry_events(); + this.reset_action_button_state(); + } + + set_init_values() { + let me = this; + // set values from xhiveframework.setup.values + if (xhiveframework.wizard.values && this.fields) { + this.fields.forEach(function (f) { + var value = xhiveframework.wizard.values[f.fieldname]; + if (value) { + me.get_field(f.fieldname).set_input(value); + } + }); + } + } + + setup_telemetry_events() { + let me = this; + this.fields.filter(xhiveframework.model.is_value_type).forEach((field) => { + field.fieldname && + me.get_input(field.fieldname)?.on?.("change", function () { + xhiveframework.telemetry.capture(`${field.fieldname}_set`, "setup"); + if ( + field.fieldname == "enable_telemetry" && + !me.get_value("enable_telemetry") + ) { + xhiveframework.telemetry.disable(); + } + }); + }); + } +}; + +// XhiveFramework slides settings +// ====================================================== +xhiveframework.setup.slides_settings = [ + { + // Welcome (language) slide + name: "welcome", + title: __("Welcome"), + + fields: [ + { + fieldname: "language", + label: __("Your Language"), + fieldtype: "Autocomplete", + placeholder: __("Select Language"), + default: "English", + reqd: 1, + }, + { + fieldname: "country", + label: __("Your Country"), + fieldtype: "Autocomplete", + placeholder: __("Select Country"), + reqd: 1, + }, + { + fieldtype: "Section Break", + }, + { + fieldname: "timezone", + label: __("Time Zone"), + placeholder: __("Select Time Zone"), + fieldtype: "Select", + reqd: 1, + }, + { fieldtype: "Column Break" }, + { + fieldname: "currency", + label: __("Currency"), + placeholder: __("Select Currency"), + fieldtype: "Select", + reqd: 1, + }, + { + fieldtype: "Section Break", + }, + { + fieldname: "enable_telemetry", + label: __("Allow sending usage data for improving applications"), + fieldtype: "Check", + default: cint(xhiveframework.telemetry.can_enable()), + depends_on: "eval:xhiveframework.telemetry.can_enable()", + }, + { + fieldname: "allow_recording_first_session", + label: __("Allow recording my first session to improve user experience"), + fieldtype: "Check", + default: 0, + depends_on: "eval:xhiveframework.telemetry.can_enable()", + }, + ], + + onload: function (slide) { + if (xhiveframework.setup.data.regional_data) { + this.setup_fields(slide); + } else { + xhiveframework.setup.utils.load_regional_data(slide, this.setup_fields); + } + if (!slide.get_value("language")) { + let session_language = + xhiveframework.setup.utils.get_language_name_from_code( + xhiveframework.boot.lang || navigator.language + ) || "English"; + let language_field = slide.get_field("language"); + + language_field.set_input(session_language); + if (!xhiveframework.setup._from_load_messages) { + language_field.$input.trigger("change"); + } + delete xhiveframework.setup._from_load_messages; + moment.locale("en"); + } + xhiveframework.setup.utils.bind_region_events(slide); + xhiveframework.setup.utils.bind_language_events(slide); + }, + + setup_fields: function (slide) { + xhiveframework.setup.utils.setup_region_fields(slide); + xhiveframework.setup.utils.setup_language_field(slide); + }, + }, + { + // Profile slide + name: "user", + title: __("Let's set up your account"), + icon: "fa fa-user", + fields: [ + { + fieldname: "full_name", + label: __("Full Name"), + fieldtype: "Data", + reqd: 1, + }, + { + fieldname: "email", + label: __("Email Address") + " (" + __("Will be your login ID") + ")", + fieldtype: "Data", + options: "Email", + }, + { fieldname: "password", label: __("Password"), fieldtype: "Password", length: 512 }, + ], + + onload: function (slide) { + if (xhiveframework.session.user !== "Administrator") { + const { first_name, last_name, email } = xhiveframework.boot.user; + if (first_name || last_name) { + slide.form.fields_dict.full_name.set_input( + [first_name, last_name].join(" ").trim() + ); + } + slide.form.fields_dict.email.set_input(email); + slide.form.fields_dict.email.df.read_only = 1; + slide.form.fields_dict.email.refresh(); + } else { + slide.form.fields_dict.email.df.reqd = 1; + slide.form.fields_dict.email.refresh(); + slide.form.fields_dict.password.df.reqd = 1; + slide.form.fields_dict.password.refresh(); + + xhiveframework.setup.utils.load_user_details(slide, this.setup_fields); + } + }, + + setup_fields: function (slide) { + if (xhiveframework.setup.data.full_name) { + slide.form.fields_dict.full_name.set_input(xhiveframework.setup.data.full_name); + } + if (xhiveframework.setup.data.email) { + let email = xhiveframework.setup.data.email; + slide.form.fields_dict.email.set_input(email); + } + }, + }, +]; + +xhiveframework.setup.utils = { + load_regional_data: function (slide, callback) { + xhiveframework.call({ + method: "xhiveframework.geo.country_info.get_country_timezone_info", + callback: function (data) { + xhiveframework.setup.data.regional_data = data.message; + callback(slide); + }, + }); + }, + + load_user_details: function (slide, callback) { + xhiveframework.call({ + method: "xhiveframework.desk.page.setup_wizard.setup_wizard.load_user_details", + freeze: true, + callback: function (r) { + xhiveframework.setup.data.full_name = r.message.full_name; + xhiveframework.setup.data.email = r.message.email; + callback(slide); + }, + }); + }, + + setup_language_field: function (slide) { + var language_field = slide.get_field("language"); + language_field.df.options = xhiveframework.setup.data.lang.languages; + language_field.set_options(); + }, + + setup_region_fields: function (slide) { + /* + Set a slide's country, timezone and currency fields + */ + let data = xhiveframework.setup.data.regional_data; + let country_field = slide.get_field("country"); + let translated_countries = []; + + Object.keys(data.country_info) + .sort() + .forEach((country) => { + translated_countries.push({ + label: __(country), + value: country, + }); + }); + + country_field.set_data(translated_countries); + + slide + .get_input("currency") + .empty() + .add_options( + xhiveframework.utils.unique($.map(data.country_info, (opts) => opts.currency).sort()) + ); + + slide.get_input("timezone").empty().add_options(data.all_timezones); + + slide.get_field("currency").set_input(xhiveframework.wizard.values.currency); + slide.get_field("timezone").set_input(xhiveframework.wizard.values.timezone); + + // set values if present + let country = + xhiveframework.wizard.values.country || + data.default_country || + guess_country(xhiveframework.setup.data.regional_data.country_info); + + if (country) { + country_field.set_input(country); + $(country_field.input).change(); + } + }, + + bind_language_events: function (slide) { + slide + .get_input("language") + .unbind("change") + .on("change", function () { + clearTimeout(slide.language_call_timeout); + slide.language_call_timeout = setTimeout(() => { + let lang = $(this).val() || "English"; + xhiveframework._messages = {}; + xhiveframework.call({ + method: "xhiveframework.desk.page.setup_wizard.setup_wizard.load_messages", + freeze: true, + args: { + language: lang, + }, + callback: function () { + xhiveframework.setup._from_load_messages = true; + xhiveframework.wizard.refresh_slides(); + }, + }); + }, 500); + }); + }, + + get_language_name_from_code: function (language_code) { + return xhiveframework.setup.data.lang.codes_to_names[language_code] || "English"; + }, + + bind_region_events: function (slide) { + /* + Bind a slide's country, timezone and currency fields + */ + slide.get_input("country").on("change", function () { + let country = slide.get_input("country").val(); + let $timezone = slide.get_input("timezone"); + let data = xhiveframework.setup.data.regional_data; + + $timezone.empty(); + + if (!country) return; + // add country specific timezones first + const timezone_list = data.country_info[country].timezones || []; + $timezone.add_options(timezone_list.sort()); + slide.get_field("currency").set_input(data.country_info[country].currency); + slide.get_field("currency").$input.trigger("change"); + + // add all timezones at the end, so that user has the option to change it to any timezone + $timezone.add_options(data.all_timezones); + slide.get_field("timezone").set_input($timezone.val()); + + // temporarily set date format + xhiveframework.boot.sysdefaults.date_format = + data.country_info[country].date_format || "dd-mm-yyyy"; + }); + + slide.get_input("currency").on("change", function () { + let currency = slide.get_input("currency").val(); + if (!currency) return; + xhiveframework.model.with_doc("Currency", currency, function () { + xhiveframework.provide("locals.:Currency." + currency); + let currency_doc = xhiveframework.model.get_doc("Currency", currency); + let number_format = currency_doc.number_format; + if (number_format === "#.###") { + number_format = "#.###,##"; + } else if (number_format === "#,###") { + number_format = "#,###.##"; + } + + xhiveframework.boot.sysdefaults.number_format = number_format; + locals[":Currency"][currency] = $.extend({}, currency_doc); + }); + }); + }, +}; + +// https://github.com/eggert/tz/blob/main/backward add more if required. +const TZ_BACKWARD_COMPATBILITY_MAP = { + "Asia/Calcutta": "Asia/Kolkata", +}; + +function guess_country(country_info) { + try { + let system_timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + system_timezone = TZ_BACKWARD_COMPATBILITY_MAP[system_timezone] || system_timezone; + + for (let [country, info] of Object.entries(country_info)) { + let possible_timezones = (info.timezones || []).filter((t) => t == system_timezone); + if (possible_timezones.length) return country; + } + } catch (e) { + console.log("Could not guess country", e); + } +} diff --git a/xhiveframework/desk/page/setup_wizard/setup_wizard.json b/xhiveframework/desk/page/setup_wizard/setup_wizard.json new file mode 100644 index 0000000..058549c --- /dev/null +++ b/xhiveframework/desk/page/setup_wizard/setup_wizard.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2013-10-04 13:49:33", + "docstatus": 0, + "doctype": "Page", + "idx": 1, + "modified": "2017-04-12 18:45:00.774654", + "modified_by": "Administrator", + "module": "Desk", + "name": "setup-wizard", + "owner": "Administrator", + "page_name": "setup-wizard", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 1, + "title": "Setup Wizard" +} \ No newline at end of file diff --git a/xhiveframework/desk/page/setup_wizard/setup_wizard.py b/xhiveframework/desk/page/setup_wizard/setup_wizard.py new file mode 100755 index 0000000..f150402 --- /dev/null +++ b/xhiveframework/desk/page/setup_wizard/setup_wizard.py @@ -0,0 +1,438 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.geo.country_info import get_country_info +from xhiveframework.permissions import AUTOMATIC_ROLES +from xhiveframework.translate import get_messages_for_boot, send_translations, set_default_language +from xhiveframework.utils import cint, now, strip +from xhiveframework.utils.password import update_password + +from . import install_fixtures + + +def get_setup_stages(args): # nosemgrep + # App setup stage functions should not include xhiveframework.db.commit + # That is done by xhiveframework after successful completion of all stages + stages = [ + { + "status": "Updating global settings", + "fail_msg": "Failed to update global settings", + "tasks": [ + {"fn": update_global_settings, "args": args, "fail_msg": "Failed to update global settings"} + ], + } + ] + + stages += get_stages_hooks(args) + get_setup_complete_hooks(args) + + stages.append( + { + # post executing hooks + "status": "Wrapping up", + "fail_msg": "Failed to complete setup", + "tasks": [{"fn": run_post_setup_complete, "args": args, "fail_msg": "Failed to complete setup"}], + } + ) + + return stages + + +@xhiveframework.whitelist() +def setup_complete(args): + """Calls hooks for `setup_wizard_complete`, sets home page as `desktop` + and clears cache. If wizard breaks, calls `setup_wizard_exception` hook""" + + # Setup complete: do not throw an exception, let the user continue to desk + if cint(xhiveframework.db.get_single_value("System Settings", "setup_complete")): + return {"status": "ok"} + + args = parse_args(args) + stages = get_setup_stages(args) + is_background_task = xhiveframework.conf.get("trigger_site_setup_in_background") + + if is_background_task: + process_setup_stages.enqueue(stages=stages, user_input=args, is_background_task=True) + return {"status": "registered"} + else: + return process_setup_stages(stages, args) + + +@xhiveframework.task() +def process_setup_stages(stages, user_input, is_background_task=False): + from xhiveframework.utils.telemetry import capture + + capture("initated_server_side", "setup") + try: + xhiveframework.flags.in_setup_wizard = True + current_task = None + for idx, stage in enumerate(stages): + xhiveframework.publish_realtime( + "setup_task", + {"progress": [idx, len(stages)], "stage_status": stage.get("status")}, + user=xhiveframework.session.user, + ) + + for task in stage.get("tasks"): + current_task = task + task.get("fn")(task.get("args")) + except Exception: + handle_setup_exception(user_input) + message = current_task.get("fail_msg") if current_task else "Failed to complete setup" + xhiveframework.log_error(title=f"Setup failed: {message}") + if not is_background_task: + xhiveframework.response["setup_wizard_failure_message"] = message + raise + xhiveframework.publish_realtime( + "setup_task", + {"status": "fail", "fail_msg": message}, + user=xhiveframework.session.user, + ) + else: + run_setup_success(user_input) + capture("completed_server_side", "setup") + if not is_background_task: + return {"status": "ok"} + xhiveframework.publish_realtime("setup_task", {"status": "ok"}, user=xhiveframework.session.user) + finally: + xhiveframework.flags.in_setup_wizard = False + + +def update_global_settings(args): # nosemgrep + if args.language and args.language != "English": + set_default_language(get_language_code(args.lang)) + xhiveframework.db.commit() + xhiveframework.clear_cache() + + update_system_settings(args) + create_or_update_user(args) + set_timezone(args) + + +def run_post_setup_complete(args): # nosemgrep + disable_future_access() + xhiveframework.db.commit() + xhiveframework.clear_cache() + # HACK: due to race condition sometimes old doc stays in cache. + # Remove this when we have reliable cache reset for docs + xhiveframework.get_cached_doc("System Settings") and xhiveframework.get_doc("System Settings") + + +def run_setup_success(args): # nosemgrep + for hook in xhiveframework.get_hooks("setup_wizard_success"): + xhiveframework.get_attr(hook)(args) + install_fixtures.install() + + +def get_stages_hooks(args): # nosemgrep + stages = [] + for method in xhiveframework.get_hooks("setup_wizard_stages"): + stages += xhiveframework.get_attr(method)(args) + return stages + + +def get_setup_complete_hooks(args): # nosemgrep + return [ + { + "status": "Executing method", + "fail_msg": "Failed to execute method", + "tasks": [ + { + "fn": xhiveframework.get_attr(method), + "args": args, + "fail_msg": "Failed to execute method", + } + ], + } + for method in xhiveframework.get_hooks("setup_wizard_complete") + ] + + +def handle_setup_exception(args): # nosemgrep + xhiveframework.db.rollback() + if args: + traceback = xhiveframework.get_traceback(with_context=True) + print(traceback) + for hook in xhiveframework.get_hooks("setup_wizard_exception"): + xhiveframework.get_attr(hook)(traceback, args) + + +def update_system_settings(args): # nosemgrep + number_format = get_country_info(args.get("country")).get("number_format", "#,###.##") + + # replace these as float number formats, as they have 0 precision + # and are currency number formats and not for floats + if number_format == "#.###": + number_format = "#.###,##" + elif number_format == "#,###": + number_format = "#,###.##" + + system_settings = xhiveframework.get_doc("System Settings", "System Settings") + system_settings.update( + { + "country": args.get("country"), + "language": get_language_code(args.get("language")) or "en", + "time_zone": args.get("timezone"), + "float_precision": 3, + "rounding_method": "Banker's Rounding", + "date_format": xhiveframework.db.get_value("Country", args.get("country"), "date_format"), + "time_format": xhiveframework.db.get_value("Country", args.get("country"), "time_format"), + "number_format": number_format, + "enable_scheduler": 1 if not xhiveframework.flags.in_test else 0, + "backup_limit": 3, # Default for downloadable backups + "enable_telemetry": cint(args.get("enable_telemetry")), + } + ) + system_settings.save() + if args.get("allow_recording_first_session"): + xhiveframework.db.set_default("session_recording_start", now()) + + +def create_or_update_user(args): # nosemgrep + email = args.get("email") + if not email: + return + + first_name, last_name = args.get("full_name", ""), "" + if " " in first_name: + first_name, last_name = first_name.split(" ", 1) + + if user := xhiveframework.db.get_value("User", email, ["first_name", "last_name"], as_dict=True): + if user.first_name != first_name or user.last_name != last_name: + ( + xhiveframework.qb.update("User") + .set("first_name", first_name) + .set("last_name", last_name) + .set("full_name", args.get("full_name")) + ).run() + else: + _mute_emails, xhiveframework.flags.mute_emails = xhiveframework.flags.mute_emails, True + + user = xhiveframework.new_doc("User") + user.update( + { + "email": email, + "first_name": first_name, + "last_name": last_name, + } + ) + user.append_roles(*_get_default_roles()) + user.flags.no_welcome_mail = True + user.insert() + + xhiveframework.flags.mute_emails = _mute_emails + + if args.get("password"): + update_password(email, args.get("password")) + + +def set_timezone(args): # nosemgrep + if args.get("timezone"): + for name in xhiveframework.STANDARD_USERS: + xhiveframework.db.set_value("User", name, "time_zone", args.get("timezone")) + + +def parse_args(args): # nosemgrep + if not args: + args = xhiveframework.local.form_dict + if isinstance(args, str): + args = json.loads(args) + + args = xhiveframework._dict(args) + + # strip the whitespace + for key, value in args.items(): + if isinstance(value, str): + args[key] = strip(value) + + return args + + +def add_all_roles_to(name): + user = xhiveframework.get_doc("User", name) + user.append_roles(*_get_default_roles()) + user.save() + + +def _get_default_roles() -> set[str]: + skip_roles = { + "Administrator", + "Customer", + "Supplier", + "Partner", + "Employee", + }.union(AUTOMATIC_ROLES) + return set(xhiveframework.get_all("Role", pluck="name")) - skip_roles + + +def disable_future_access(): + xhiveframework.db.set_default("desktop:home_page", "workspace") + # Enable onboarding after install + xhiveframework.db.set_single_value("System Settings", "enable_onboarding", 1) + + xhiveframework.db.set_single_value("System Settings", "setup_complete", 1) + + +@xhiveframework.whitelist() +def load_messages(language): + """Load translation messages for given language from all `setup_wizard_requires` + javascript files""" + xhiveframework.clear_cache() + set_default_language(get_language_code(language)) + xhiveframework.db.commit() + send_translations(get_messages_for_boot()) + return xhiveframework.local.lang + + +@xhiveframework.whitelist() +def load_languages(): + language_codes = xhiveframework.db.sql( + "select language_code, language_name from tabLanguage order by name", as_dict=True + ) + codes_to_names = {} + for d in language_codes: + codes_to_names[d.language_code] = d.language_name + return { + "default_language": xhiveframework.db.get_value("Language", xhiveframework.local.lang, "language_name") + or xhiveframework.local.lang, + "languages": sorted(xhiveframework.db.sql_list("select language_name from tabLanguage order by name")), + "codes_to_names": codes_to_names, + } + + +@xhiveframework.whitelist() +def load_country(): + from xhiveframework.sessions import get_geo_ip_country + + return get_geo_ip_country(xhiveframework.local.request_ip) if xhiveframework.local.request_ip else None + + +@xhiveframework.whitelist() +def load_user_details(): + return { + "full_name": xhiveframework.cache.hget("full_name", "signup"), + "email": xhiveframework.cache.hget("email", "signup"), + } + + +def prettify_args(args): # nosemgrep + # remove attachments + for key, val in args.items(): + if isinstance(val, str) and "data:image" in val: + filename = val.split("data:image", 1)[0].strip(", ") + size = round((len(val) * 3 / 4) / 1048576.0, 2) + args[key] = f"Image Attached: '{filename}' of size {size} MB" + + pretty_args = [] + pretty_args.extend(f"{key} = {args[key]}" for key in sorted(args)) + return pretty_args + + +def email_setup_wizard_exception(traceback, args): # nosemgrep + if not xhiveframework.conf.setup_wizard_exception_email: + return + + pretty_args = prettify_args(args) + message = """ + +#### Traceback + +
      {traceback}
      + +--- + +#### Setup Wizard Arguments + +
      {args}
      + +--- + +#### Request Headers + +
      {headers}
      + +--- + +#### Basic Information + +- **Site:** {site} +- **User:** {user}""".format( + site=xhiveframework.local.site, + traceback=traceback, + args="\n".join(pretty_args), + user=xhiveframework.session.user, + headers=xhiveframework.request.headers if xhiveframework.request else "[no request]", + ) + + xhiveframework.sendmail( + recipients=xhiveframework.conf.setup_wizard_exception_email, + sender=xhiveframework.session.user, + subject=f"Setup failed: {xhiveframework.local.site}", + message=message, + delayed=False, + ) + + +def log_setup_wizard_exception(traceback, args): # nosemgrep + with open("../logs/setup-wizard.log", "w+") as setup_log: + setup_log.write(traceback) + setup_log.write(json.dumps(args)) + + +def get_language_code(lang): + return xhiveframework.db.get_value("Language", {"language_name": lang}) + + +def enable_twofactor_all_roles(): + all_role = xhiveframework.get_doc("Role", {"role_name": "All"}) + all_role.two_factor_auth = True + all_role.save(ignore_permissions=True) + + +def make_records(records, debug=False): + from xhiveframework import _dict + from xhiveframework.modules import scrub + + if debug: + print("make_records: in DEBUG mode") + + # LOG every success and failure + for record in records: + doctype = record.get("doctype") + condition = record.get("__condition") + + if condition and not condition(): + continue + + doc = xhiveframework.new_doc(doctype) + doc.update(record) + + # ignore mandatory for root + parent_link_field = "parent_" + scrub(doc.doctype) + if doc.meta.get_field(parent_link_field) and not doc.get(parent_link_field): + doc.flags.ignore_mandatory = True + + savepoint = "setup_fixtures_creation" + try: + xhiveframework.db.savepoint(savepoint) + doc.insert(ignore_permissions=True, ignore_if_duplicate=True) + except Exception as e: + xhiveframework.clear_last_message() + xhiveframework.db.rollback(save_point=savepoint) + exception = record.get("__exception") + if exception: + config = _dict(exception) + if isinstance(e, config.exception): + config.handler() + else: + show_document_insert_error() + else: + show_document_insert_error() + + +def show_document_insert_error(): + print("Document Insert Error") + print(xhiveframework.get_traceback()) + xhiveframework.log_error("Exception during Setup") diff --git a/xhiveframework/desk/page/user_profile/__init__.py b/xhiveframework/desk/page/user_profile/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/page/user_profile/user_profile.css b/xhiveframework/desk/page/user_profile/user_profile.css new file mode 100644 index 0000000..9bcfc33 --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile.css @@ -0,0 +1,30 @@ +.recent-activity .new-timeline { + padding-top: 0; +} + +.recent-activity .new-timeline:before { + top: 25px; +} + +.recent-activity-title { + font-weight: 700; + font-size: var(--text-xl); + color: var(--text-color); +} + +.recent-activity .recent-activity-footer { + margin-left: calc(var(--timeline-left-padding) + var(--timeline-item-left-margin)); + max-width: var(--timeline-content-max-width); +} + +.recent-activity .show-more-activity-btn { + display: block; + margin: auto; + width: max-content; + margin-top: 35px; + font-size: var(--text-md); +} + +.recent-activity { + padding-bottom: 60px; +} \ No newline at end of file diff --git a/xhiveframework/desk/page/user_profile/user_profile.html b/xhiveframework/desk/page/user_profile/user_profile.html new file mode 100644 index 0000000..5f3b275 --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile.html @@ -0,0 +1,44 @@ + diff --git a/xhiveframework/desk/page/user_profile/user_profile.js b/xhiveframework/desk/page/user_profile/user_profile.js new file mode 100644 index 0000000..5db3099 --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile.js @@ -0,0 +1,6 @@ +xhiveframework.pages["user-profile"].on_page_load = function (wrapper) { + xhiveframework.require("user_profile_controller.bundle.js", () => { + let user_profile = new xhiveframework.ui.UserProfile(wrapper); + user_profile.show(); + }); +}; diff --git a/xhiveframework/desk/page/user_profile/user_profile.json b/xhiveframework/desk/page/user_profile/user_profile.json new file mode 100644 index 0000000..43385f5 --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2019-07-22 12:23:38.425877", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2020-03-02 15:17:13.041650", + "modified_by": "Administrator", + "module": "Desk", + "name": "user-profile", + "owner": "Administrator", + "page_name": "User Profile", + "roles": [ + { + "role": "All" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "User Profile" +} \ No newline at end of file diff --git a/xhiveframework/desk/page/user_profile/user_profile.py b/xhiveframework/desk/page/user_profile/user_profile.py new file mode 100644 index 0000000..500df4a --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile.py @@ -0,0 +1,112 @@ +from datetime import datetime + +import xhiveframework +from xhiveframework.query_builder import Interval, Order +from xhiveframework.query_builder.functions import Date, Sum, UnixTimestamp +from xhiveframework.utils import getdate + + +@xhiveframework.whitelist() +def get_energy_points_heatmap_data(user, date): + try: + date = getdate(date) + except Exception: + date = getdate() + + eps_log = xhiveframework.qb.DocType("Energy Point Log") + + return dict( + xhiveframework.qb.from_(eps_log) + .select(UnixTimestamp(Date(eps_log.creation)), Sum(eps_log.points)) + .where(eps_log.user == user) + .where(eps_log["type"] != "Review") + .where(Date(eps_log.creation) > Date(date) - Interval(years=1)) + .where(Date(eps_log.creation) < Date(date) + Interval(years=1)) + .groupby(Date(eps_log.creation)) + .orderby(Date(eps_log.creation), order=Order.asc) + .run() + ) + + +@xhiveframework.whitelist() +def get_energy_points_percentage_chart_data(user, field): + result = xhiveframework.get_all( + "Energy Point Log", + filters={"user": user, "type": ["!=", "Review"]}, + group_by=field, + order_by=field, + fields=[field, "ABS(sum(points)) as points"], + as_list=True, + ) + + return { + "labels": [r[0] for r in result if r[0] is not None], + "datasets": [{"values": [r[1] for r in result]}], + } + + +@xhiveframework.whitelist() +def get_user_rank(user): + month_start = datetime.today().replace(day=1) + monthly_rank = xhiveframework.get_all( + "Energy Point Log", + group_by="`tabEnergy Point Log`.`user`", + filters={"creation": [">", month_start], "type": ["!=", "Review"]}, + fields=["user", "sum(points)"], + order_by="sum(points) desc", + as_list=True, + ) + + all_time_rank = xhiveframework.get_all( + "Energy Point Log", + group_by="`tabEnergy Point Log`.`user`", + filters={"type": ["!=", "Review"]}, + fields=["user", "sum(points)"], + order_by="sum(points) desc", + as_list=True, + ) + + return { + "monthly_rank": [i + 1 for i, r in enumerate(monthly_rank) if r[0] == user], + "all_time_rank": [i + 1 for i, r in enumerate(all_time_rank) if r[0] == user], + } + + +@xhiveframework.whitelist() +def update_profile_info(profile_info): + profile_info = xhiveframework.parse_json(profile_info) + keys = ["location", "interest", "user_image", "bio"] + + for key in keys: + if key not in profile_info: + profile_info[key] = None + + user = xhiveframework.get_doc("User", xhiveframework.session.user) + user.update(profile_info) + user.save() + return user + + +@xhiveframework.whitelist() +def get_energy_points_list(start, limit, user): + return xhiveframework.db.get_list( + "Energy Point Log", + filters={"user": user, "type": ["!=", "Review"]}, + fields=[ + "name", + "user", + "points", + "reference_doctype", + "reference_name", + "reason", + "type", + "seen", + "rule", + "owner", + "creation", + "revert_of", + ], + start=start, + limit=limit, + order_by="creation desc", + ) diff --git a/xhiveframework/desk/page/user_profile/user_profile_controller.js b/xhiveframework/desk/page/user_profile/user_profile_controller.js new file mode 100644 index 0000000..359b499 --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile_controller.js @@ -0,0 +1,494 @@ +import BaseTimeline from "../../../public/js/xhiveframework/form/footer/base_timeline"; +xhiveframework.provide("xhiveframework.energy_points"); + +class UserProfile { + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = xhiveframework.ui.make_app_page({ + parent: wrapper, + }); + this.sidebar = this.wrapper.find(".layout-side-section"); + this.main_section = this.wrapper.find(".layout-main-section"); + this.wrapper.bind("show", () => { + this.show(); + }); + } + + show() { + let route = xhiveframework.get_route(); + this.user_id = route[1] || xhiveframework.session.user; + xhiveframework.dom.freeze(__("Loading user profile") + "..."); + xhiveframework.db.exists("User", this.user_id).then((exists) => { + xhiveframework.dom.unfreeze(); + if (exists) { + this.make_user_profile(); + } else { + xhiveframework.msgprint(__("User does not exist")); + } + }); + } + + make_user_profile() { + this.user = xhiveframework.user_info(this.user_id); + this.page.set_title(this.user.fullname); + this.setup_user_search(); + this.main_section.empty().append(xhiveframework.render_template("user_profile")); + this.energy_points = 0; + this.review_points = 0; + this.rank = 0; + this.month_rank = 0; + this.render_user_details(); + this.render_points_and_rank(); + this.render_heatmap(); + this.render_line_chart(); + this.render_percentage_chart("type", "Type Distribution"); + this.create_percentage_chart_filters(); + this.setup_user_activity_timeline(); + } + + setup_user_search() { + this.$user_search_button = this.page.set_secondary_action( + __("Change User"), + () => this.show_user_search_dialog(), + { icon: "change", size: "sm" } + ); + } + + show_user_search_dialog() { + let dialog = new xhiveframework.ui.Dialog({ + title: __("Change User"), + fields: [ + { + fieldtype: "Link", + fieldname: "user", + options: "User", + label: __("User"), + }, + ], + primary_action_label: __("Go"), + primary_action: ({ user }) => { + dialog.hide(); + xhiveframework.set_route("user-profile", user); + }, + }); + dialog.show(); + } + + render_heatmap() { + this.heatmap = new xhiveframework.Chart(".performance-heatmap", { + type: "heatmap", + countLabel: "Energy Points", + data: {}, + discreteDomains: 1, + radius: 3, + height: 150, + }); + this.update_heatmap_data(); + this.create_heatmap_chart_filters(); + } + + update_heatmap_data(date_from) { + xhiveframework + .xcall("xhiveframework.desk.page.user_profile.user_profile.get_energy_points_heatmap_data", { + user: this.user_id, + date: date_from || xhiveframework.datetime.year_start(), + }) + .then((r) => { + this.heatmap.update({ dataPoints: r }); + }); + } + + render_line_chart() { + this.line_chart_filters = [ + ["Energy Point Log", "user", "=", this.user_id, false], + ["Energy Point Log", "type", "!=", "Review", false], + ]; + + this.line_chart_config = { + timespan: "Last Month", + time_interval: "Daily", + type: "Line", + value_based_on: "points", + chart_type: "Sum", + document_type: "Energy Point Log", + name: "Energy Points", + width: "half", + based_on: "creation", + }; + + this.line_chart = new xhiveframework.Chart(".performance-line-chart", { + type: "line", + height: 200, + data: { + labels: [], + datasets: [{}], + }, + colors: ["purple"], + axisOptions: { + xIsSeries: 1, + }, + }); + this.update_line_chart_data(); + this.create_line_chart_filters(); + } + + update_line_chart_data() { + this.line_chart_config.filters_json = JSON.stringify(this.line_chart_filters); + + xhiveframework + .xcall("xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.get", { + chart: this.line_chart_config, + no_cache: 1, + }) + .then((chart) => { + this.line_chart.update(chart); + }); + } + + render_percentage_chart(field, title) { + xhiveframework + .xcall( + "xhiveframework.desk.page.user_profile.user_profile.get_energy_points_percentage_chart_data", + { + user: this.user_id, + field: field, + } + ) + .then((chart) => { + if (chart.labels.length) { + this.percentage_chart = new xhiveframework.Chart(".performance-percentage-chart", { + type: "percentage", + data: { + labels: chart.labels, + datasets: chart.datasets, + }, + truncateLegends: 1, + barOptions: { + height: 11, + depth: 1, + }, + height: 200, + maxSlices: 8, + colors: [ + "purple", + "blue", + "cyan", + "teal", + "pink", + "red", + "orange", + "yellow", + ], + }); + } else { + this.wrapper.find(".percentage-chart-container").hide(); + } + }); + } + + create_line_chart_filters() { + let filters = [ + { + label: "All", + options: ["All", "Auto", "Criticism", "Appreciation", "Revert"], + action: (selected_item) => { + if (selected_item === "All") { + this.line_chart_filters = [ + ["Energy Point Log", "user", "=", this.user_id, false], + ["Energy Point Log", "type", "!=", "Review", false], + ]; + } else { + this.line_chart_filters[1] = [ + "Energy Point Log", + "type", + "=", + selected_item, + false, + ]; + } + this.update_line_chart_data(); + }, + }, + { + label: "Last Month", + options: ["Last Week", "Last Month", "Last Quarter", "Last Year"], + action: (selected_item) => { + this.line_chart_config.timespan = selected_item; + this.update_line_chart_data(); + }, + }, + { + label: "Daily", + options: ["Daily", "Weekly", "Monthly"], + action: (selected_item) => { + this.line_chart_config.time_interval = selected_item; + this.update_line_chart_data(); + }, + }, + ]; + xhiveframework.dashboard_utils.render_chart_filters( + filters, + "chart-filter", + ".line-chart-options", + 1 + ); + } + + create_percentage_chart_filters() { + let filters = [ + { + label: "Type", + options: ["Type", "Reference Doctype", "Rule"], + fieldnames: ["type", "reference_doctype", "rule"], + action: (selected_item, fieldname) => { + let title = selected_item + " Distribution"; + this.render_percentage_chart(fieldname, title); + }, + }, + ]; + xhiveframework.dashboard_utils.render_chart_filters( + filters, + "chart-filter", + ".percentage-chart-options" + ); + } + + create_heatmap_chart_filters() { + let filters = [ + { + label: xhiveframework.dashboard_utils.get_year(xhiveframework.datetime.now_date()), + options: xhiveframework.dashboard_utils.get_years_since_creation( + xhiveframework.boot.user.creation + ), + action: (selected_item) => { + this.update_heatmap_data(xhiveframework.datetime.obj_to_str(selected_item)); + }, + }, + ]; + xhiveframework.dashboard_utils.render_chart_filters(filters, "chart-filter", ".heatmap-options"); + } + + edit_profile() { + let edit_profile_dialog = new xhiveframework.ui.Dialog({ + title: __("Edit Profile"), + fields: [ + { + fieldtype: "Attach Image", + fieldname: "user_image", + label: "Profile Image", + }, + { + fieldtype: "Data", + fieldname: "interest", + label: "Interests", + }, + { + fieldtype: "Column Break", + }, + { + fieldtype: "Data", + fieldname: "location", + label: "Location", + }, + { + fieldtype: "Section Break", + fieldname: "Interest", + }, + { + fieldtype: "Small Text", + fieldname: "bio", + label: "Bio", + }, + ], + primary_action: (values) => { + edit_profile_dialog.disable_primary_action(); + xhiveframework + .xcall("xhiveframework.desk.page.user_profile.user_profile.update_profile_info", { + profile_info: values, + }) + .then((user) => { + user.image = user.user_image; + this.user = Object.assign(values, user); + edit_profile_dialog.hide(); + this.render_user_details(); + }) + .finally(() => { + edit_profile_dialog.enable_primary_action(); + }); + }, + primary_action_label: __("Save"), + }); + + edit_profile_dialog.set_values({ + user_image: this.user.image, + location: this.user.location, + interest: this.user.interest, + bio: this.user.bio, + }); + edit_profile_dialog.show(); + } + + render_user_details() { + this.sidebar.empty().append( + xhiveframework.render_template("user_profile_sidebar", { + user_image: this.user.image, + user_abbr: this.user.abbr, + user_location: this.user.location, + user_interest: this.user.interest, + user_bio: this.user.bio, + }) + ); + + this.setup_user_profile_links(); + } + + setup_user_profile_links() { + if (this.user_id !== xhiveframework.session.user) { + this.wrapper.find(".profile-links").hide(); + } else { + this.wrapper.find(".edit-profile-link").on("click", () => { + this.edit_profile(); + }); + + this.wrapper.find(".user-settings-link").on("click", () => { + this.go_to_user_settings(); + }); + } + } + + get_user_rank() { + return xhiveframework + .xcall("xhiveframework.desk.page.user_profile.user_profile.get_user_rank", { + user: this.user_id, + }) + .then((r) => { + if (r.monthly_rank.length) this.month_rank = r.monthly_rank[0]; + if (r.all_time_rank.length) this.rank = r.all_time_rank[0]; + }); + } + + get_user_points() { + return xhiveframework + .xcall( + "xhiveframework.social.doctype.energy_point_log.energy_point_log.get_user_energy_and_review_points", + { + user: this.user_id, + } + ) + .then((r) => { + if (r[this.user_id]) { + this.energy_points = r[this.user_id].energy_points; + this.review_points = r[this.user_id].review_points; + } + }); + } + + render_points_and_rank() { + let $profile_details = this.wrapper.find(".user-stats"); + let $profile_details_wrapper = this.wrapper.find(".user-stats-detail"); + + const _get_stat_dom = (value, label, icon) => { + return `
      + ${xhiveframework.utils.icon(icon, "lg", "no-stroke")} +
      +
      ${value}
      +
      ${label}
      +
      +
      `; + }; + + this.get_user_rank().then(() => { + this.get_user_points().then(() => { + let html = $(` + ${_get_stat_dom(this.energy_points, __("Energy Points"), "color-energy-points")} + ${_get_stat_dom(this.review_points, __("Review Points"), "color-review-points")} + ${_get_stat_dom(this.rank, __("Rank"), "color-rank")} + ${_get_stat_dom(this.month_rank, __("Monthly Rank"), "color-monthly-rank")} + `); + + $profile_details.append(html); + $profile_details_wrapper.removeClass("hide"); + }); + }); + } + + go_to_user_settings() { + xhiveframework.set_route("Form", "User", this.user_id); + } + + setup_user_activity_timeline() { + this.user_activity_timeline = new UserProfileTimeline({ + parent: this.wrapper.find(".recent-activity-list"), + footer: this.wrapper.find(".recent-activity-footer"), + user: this.user_id, + }); + + this.user_activity_timeline.refresh(); + } +} + +class UserProfileTimeline extends BaseTimeline { + make() { + super.make(); + this.activity_start = 0; + this.activity_limit = 20; + this.setup_show_more_activity(); + } + prepare_timeline_contents() { + return this.get_user_activity_data().then((activities) => { + if (!activities.length) { + this.show_more_button.hide(); + this.timeline_wrapper.html(`
      ${__("No activities to show")}
      `); + return; + } + this.show_more_button.toggle(activities.length === this.activity_limit); + this.timeline_items = activities.map((activity) => + this.get_activity_timeline_item(activity) + ); + }); + } + + get_user_activity_data() { + return xhiveframework.xcall("xhiveframework.desk.page.user_profile.user_profile.get_energy_points_list", { + start: this.activity_start, + limit: this.activity_limit, + user: this.user, + }); + } + + get_activity_timeline_item(data) { + let icon = + data.type == "Appreciation" ? "clap" : data.type == "Criticism" ? "criticize" : null; + return { + icon: icon, + creation: data.creation, + is_card: true, + content: xhiveframework.energy_points.format_history_log(data), + }; + } + + setup_show_more_activity() { + this.show_more_button = $( + `${__("Show More Activity")}` + ); + this.show_more_button.hide(); + this.footer.append(this.show_more_button); + this.show_more_button.on("click", () => this.show_more_activity()); + } + + show_more_activity() { + this.activity_start += this.activity_limit; + this.get_user_activity_data().then((activities) => { + if (!activities.length || activities.length < this.activity_limit) { + this.show_more_button.hide(); + } + let timeline_items = activities.map((activity) => + this.get_activity_timeline_item(activity) + ); + timeline_items.map((item) => this.add_timeline_item(item, true)); + }); + } +} + +xhiveframework.provide("xhiveframework.ui"); +xhiveframework.ui.UserProfile = UserProfile; diff --git a/xhiveframework/desk/page/user_profile/user_profile_sidebar.html b/xhiveframework/desk/page/user_profile/user_profile_sidebar.html new file mode 100644 index 0000000..9f8889f --- /dev/null +++ b/xhiveframework/desk/page/user_profile/user_profile_sidebar.html @@ -0,0 +1,60 @@ + diff --git a/xhiveframework/desk/query_report.py b/xhiveframework/desk/query_report.py new file mode 100644 index 0000000..b8ee09e --- /dev/null +++ b/xhiveframework/desk/query_report.py @@ -0,0 +1,774 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import datetime +import json +import os +from datetime import timedelta + +import xhiveframework +import xhiveframework.desk.reportview +from xhiveframework import _ +from xhiveframework.core.utils import ljust_list +from xhiveframework.desk.reportview import clean_params, parse_json +from xhiveframework.model.utils import render_include +from xhiveframework.modules import get_module_path, scrub +from xhiveframework.monitor import add_data_to_monitor +from xhiveframework.permissions import get_role_permissions +from xhiveframework.utils import cint, cstr, flt, format_duration, get_html_format, sbool + + +def get_report_doc(report_name): + doc = xhiveframework.get_doc("Report", report_name) + doc.custom_columns = [] + doc.custom_filters = [] + + if doc.report_type == "Custom Report": + custom_report_doc = doc + doc = get_reference_report(doc) + doc.custom_report = report_name + if custom_report_doc.json: + data = json.loads(custom_report_doc.json) + if data: + doc.custom_columns = data.get("columns") + doc.custom_filters = data.get("filters") + doc.is_custom_report = True + + if not doc.is_permitted(): + xhiveframework.throw( + _("You don't have access to Report: {0}").format(report_name), + xhiveframework.PermissionError, + ) + + if not xhiveframework.has_permission(doc.ref_doctype, "report"): + xhiveframework.throw( + _("You don't have permission to get a report on: {0}").format(doc.ref_doctype), + xhiveframework.PermissionError, + ) + + if doc.disabled: + xhiveframework.throw(_("Report {0} is disabled").format(report_name)) + + return doc + + +def get_report_result(report, filters): + res = None + + if report.report_type == "Query Report": + res = report.execute_query_report(filters) + + elif report.report_type == "Script Report": + res = report.execute_script_report(filters) + + elif report.report_type == "Custom Report": + ref_report = get_report_doc(report.report_name) + res = get_report_result(ref_report, filters) + + return res + + +@xhiveframework.read_only() +def generate_report_result( + report, filters=None, user=None, custom_columns=None, is_tree=False, parent_field=None +): + user = user or xhiveframework.session.user + filters = filters or [] + + if filters and isinstance(filters, str): + filters = json.loads(filters) + + res = get_report_result(report, filters) or [] + + columns, result, message, chart, report_summary, skip_total_row = ljust_list(res, 6) + columns = [get_column_as_dict(col) for col in (columns or [])] + report_column_names = [col["fieldname"] for col in columns] + # convert to list of dicts + + result = normalize_result(result, columns) + + if report.custom_columns: + # saved columns (with custom columns / with different column order) + columns = report.custom_columns + + # unsaved custom_columns + if custom_columns: + for custom_column in custom_columns: + columns.insert(custom_column["insert_after_index"] + 1, custom_column) + + # all columns which are not in original report + report_custom_columns = [column for column in columns if column["fieldname"] not in report_column_names] + + if report_custom_columns: + result = add_custom_column_data(report_custom_columns, result) + + if result: + result = get_filtered_data(report.ref_doctype, columns, result, user) + + if cint(report.add_total_row) and result and not skip_total_row: + result = add_total_row(result, columns, is_tree=is_tree, parent_field=parent_field) + + return { + "result": result, + "columns": columns, + "message": message, + "chart": chart, + "report_summary": report_summary, + "skip_total_row": skip_total_row or 0, + "status": None, + "execution_time": xhiveframework.cache.hget("report_execution_time", report.name) or 0, + } + + +def normalize_result(result, columns): + # Converts to list of dicts from list of lists/tuples + data = [] + column_names = [column["fieldname"] for column in columns] + if result and isinstance(result[0], list | tuple): + for row in result: + row_obj = {} + for idx, column_name in enumerate(column_names): + row_obj[column_name] = row[idx] + data.append(row_obj) + else: + data = result + + return data + + +@xhiveframework.whitelist() +def get_script(report_name): + report = get_report_doc(report_name) + module = report.module or xhiveframework.db.get_value("DocType", report.ref_doctype, "module") + + is_custom_module = xhiveframework.get_cached_value("Module Def", module, "custom") + + # custom modules are virtual modules those exists in DB but not in disk. + module_path = "" if is_custom_module else get_module_path(module) + report_folder = module_path and os.path.join(module_path, "report", scrub(report.name)) + script_path = report_folder and os.path.join(report_folder, scrub(report.name) + ".js") + print_path = report_folder and os.path.join(report_folder, scrub(report.name) + ".html") + + script = None + if os.path.exists(script_path): + with open(script_path) as f: + script = f.read() + script += f"\n\n//# sourceURL={scrub(report.name)}.js" + + html_format = get_html_format(print_path) + + if not script and report.javascript: + script = report.javascript + script += f"\n\n//# sourceURL={scrub(report.name)}__custom" + + if not script: + script = "xhiveframework.query_reports['%s']={}" % report_name + + return { + "script": render_include(script), + "html_format": html_format, + "execution_time": xhiveframework.cache.hget("report_execution_time", report_name) or 0, + "filters": report.filters, + "custom_report_name": report.name if report.get("is_custom_report") else None, + } + + +def get_reference_report(report): + if report.report_type != "Custom Report": + return report + reference_report = xhiveframework.get_doc("Report", report.reference_report) + return get_reference_report(reference_report) + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def run( + report_name, + filters=None, + user=None, + ignore_prepared_report=False, + custom_columns=None, + is_tree=False, + parent_field=None, + are_default_filters=True, +): + report = get_report_doc(report_name) + if not user: + user = xhiveframework.session.user + if not xhiveframework.has_permission(report.ref_doctype, "report"): + xhiveframework.msgprint( + _("Must have report permission to access this report."), + raise_exception=True, + ) + + result = None + + if sbool(are_default_filters) and report.custom_filters: + filters = report.custom_filters + + if report.prepared_report and not sbool(ignore_prepared_report) and not custom_columns: + if filters: + if isinstance(filters, str): + filters = json.loads(filters) + + dn = filters.pop("prepared_report_name", None) + else: + dn = "" + result = get_prepared_report_result(report, filters, dn, user) + else: + result = generate_report_result(report, filters, user, custom_columns, is_tree, parent_field) + add_data_to_monitor(report=report.reference_report or report.name) + + result["add_total_row"] = report.add_total_row and not result.get("skip_total_row", False) + + if sbool(are_default_filters) and report.custom_filters: + result["custom_filters"] = report.custom_filters + + return result + + +def add_custom_column_data(custom_columns, result): + doctype_names_from_custom_field = [] + for column in custom_columns: + if len(column["fieldname"].split("-")) > 1: + # length greater than 1, means that the column is a custom field with confilicting fieldname + doctype_name = xhiveframework.unscrub(column["fieldname"].split("-")[1]) + doctype_names_from_custom_field.append(doctype_name) + column["fieldname"] = column["fieldname"].split("-")[0] + + custom_column_data = get_data_for_custom_report(custom_columns, result) + + for column in custom_columns: + key = (column.get("doctype"), column.get("fieldname")) + if key in custom_column_data: + for row in result: + link_field = column.get("link_field") + + # backwards compatibile `link_field` + # old custom reports which use `str` should not break. + if isinstance(link_field, str): + link_field = xhiveframework._dict({"fieldname": link_field, "names": []}) + + row_reference = row.get(link_field.get("fieldname")) + # possible if the row is empty + if not row_reference: + continue + if key[0] in doctype_names_from_custom_field: + column["fieldname"] = column.get("id") + row[column.get("fieldname")] = custom_column_data.get(key).get(row_reference) + + return result + + +def get_prepared_report_result(report, filters, dn="", user=None): + from xhiveframework.core.doctype.prepared_report.prepared_report import get_completed_prepared_report + + def get_report_data(doc, data): + # backwards compatibility - prepared report used to have a columns field, + # we now directly fetch it from the result file + if doc.get("columns") or isinstance(data, list): + columns = (doc.get("columns") and json.loads(doc.columns)) or data[0] + data = {"result": data} + else: + columns = data.get("columns") + + for column in columns: + if isinstance(column, dict) and column.get("label"): + column["label"] = _(column["label"]) + + return data | {"columns": columns} + + report_data = {} + if not dn: + dn = get_completed_prepared_report( + filters, user, report.get("custom_report") or report.get("report_name") + ) + + doc = xhiveframework.get_doc("Prepared Report", dn) if dn else None + if doc: + try: + if data := json.loads(doc.get_prepared_data().decode("utf-8")): + report_data = get_report_data(doc, data) + except Exception as e: + doc.log_error("Prepared report render failed") + xhiveframework.msgprint(_("Prepared report render failed") + f": {e!s}") + doc = None + + return report_data | {"prepared_report": True, "doc": doc} + + +@xhiveframework.whitelist() +def export_query(): + """export from query reports""" + from xhiveframework.desk.utils import get_csv_bytes, pop_csv_params, provide_binary_file + + form_params = xhiveframework._dict(xhiveframework.local.form_dict) + csv_params = pop_csv_params(form_params) + clean_params(form_params) + parse_json(form_params) + + report_name = form_params.report_name + xhiveframework.permissions.can_export( + xhiveframework.get_cached_value("Report", report_name, "ref_doctype"), + raise_exception=True, + ) + + file_format_type = form_params.file_format_type + custom_columns = xhiveframework.parse_json(form_params.custom_columns or "[]") + include_indentation = form_params.include_indentation + include_filters = form_params.include_filters + visible_idx = form_params.visible_idx + + if isinstance(visible_idx, str): + visible_idx = json.loads(visible_idx) + + data = run(report_name, form_params.filters, custom_columns=custom_columns, are_default_filters=False) + data = xhiveframework._dict(data) + data.filters = form_params.applied_filters + + if not data.columns: + xhiveframework.respond_as_web_page( + _("No data to export"), + _("You can try changing the filters of your report."), + ) + return + + format_duration_fields(data) + xlsx_data, column_widths = build_xlsx_data( + data, visible_idx, include_indentation, include_filters=include_filters + ) + + if file_format_type == "CSV": + content = get_csv_bytes(xlsx_data, csv_params) + file_extension = "csv" + elif file_format_type == "Excel": + from xhiveframework.utils.xlsxutils import make_xlsx + + file_extension = "xlsx" + content = make_xlsx(xlsx_data, "Query Report", column_widths=column_widths).getvalue() + + provide_binary_file(report_name, file_extension, content) + + +def format_duration_fields(data: xhiveframework._dict) -> None: + for i, col in enumerate(data.columns): + if col.get("fieldtype") != "Duration": + continue + + for row in data.result: + index = col.get("fieldname") if isinstance(row, dict) else i + if row[index]: + row[index] = format_duration(row[index]) + + +def build_xlsx_data(data, visible_idx, include_indentation, include_filters=False, ignore_visible_idx=False): + EXCEL_TYPES = ( + str, + bool, + type(None), + int, + float, + datetime.datetime, + datetime.date, + datetime.time, + datetime.timedelta, + ) + + if len(visible_idx) == len(data.result) or not visible_idx: + # It's not possible to have same length and different content. + ignore_visible_idx = True + else: + # Note: converted for faster lookups + visible_idx = set(visible_idx) + + result = [] + column_widths = [] + + if cint(include_filters): + filter_data = [] + filters = data.filters + for filter_name, filter_value in filters.items(): + if not filter_value: + continue + filter_value = ( + ", ".join([cstr(x) for x in filter_value]) + if isinstance(filter_value, list) + else cstr(filter_value) + ) + filter_data.append([cstr(filter_name), filter_value]) + filter_data.append([]) + result += filter_data + + column_data = [] + for column in data.columns: + if column.get("hidden"): + continue + column_data.append(_(column.get("label"))) + column_width = cint(column.get("width", 0)) + # to convert into scale accepted by openpyxl + column_width /= 10 + column_widths.append(column_width) + result.append(column_data) + + # build table from result + for row_idx, row in enumerate(data.result): + # only pick up rows that are visible in the report + if ignore_visible_idx or row_idx in visible_idx: + row_data = [] + if isinstance(row, dict): + for col_idx, column in enumerate(data.columns): + if column.get("hidden"): + continue + label = column.get("label") + fieldname = column.get("fieldname") + cell_value = row.get(fieldname, row.get(label, "")) + if not isinstance(cell_value, EXCEL_TYPES): + cell_value = cstr(cell_value) + + if cint(include_indentation) and "indent" in row and col_idx == 0: + cell_value = (" " * cint(row["indent"])) + cstr(cell_value) + row_data.append(cell_value) + elif row: + row_data = row + + result.append(row_data) + + return result, column_widths + + +def add_total_row(result, columns, meta=None, is_tree=False, parent_field=None): + total_row = [""] * len(columns) + has_percent = [] + + for i, col in enumerate(columns): + fieldtype, options, fieldname = None, None, None + if isinstance(col, str): + if meta: + # get fieldtype from the meta + field = meta.get_field(col) + if field: + fieldtype = meta.get_field(col).fieldtype + fieldname = meta.get_field(col).fieldname + else: + col = col.split(":") + if len(col) > 1: + if col[1]: + fieldtype = col[1] + if "/" in fieldtype: + fieldtype, options = fieldtype.split("/") + else: + fieldtype = "Data" + else: + fieldtype = col.get("fieldtype") + fieldname = col.get("fieldname") + options = col.get("options") + + for row in result: + if i >= len(row): + continue + cell = row.get(fieldname) if isinstance(row, dict) else row[i] + if fieldtype in ["Currency", "Int", "Float", "Percent", "Duration"] and flt(cell): + if not (is_tree and row.get(parent_field)): + total_row[i] = flt(total_row[i]) + flt(cell) + + if fieldtype == "Percent" and i not in has_percent: + has_percent.append(i) + + if fieldtype == "Time" and cell: + if not total_row[i]: + total_row[i] = timedelta(hours=0, minutes=0, seconds=0) + total_row[i] = total_row[i] + cell + + if fieldtype == "Link" and options == "Currency": + total_row[i] = result[0].get(fieldname) if isinstance(result[0], dict) else result[0][i] + + for i in has_percent: + total_row[i] = flt(total_row[i]) / len(result) + + first_col_fieldtype = None + if isinstance(columns[0], str): + first_col = columns[0].split(":") + if len(first_col) > 1: + first_col_fieldtype = first_col[1].split("/", 1)[0] + else: + first_col_fieldtype = columns[0].get("fieldtype") + + if first_col_fieldtype not in ["Currency", "Int", "Float", "Percent", "Date"]: + total_row[0] = _("Total") + + result.append(total_row) + return result + + +@xhiveframework.whitelist() +def get_data_for_custom_field(doctype, field, names=None): + if not xhiveframework.has_permission(doctype, "read"): + xhiveframework.throw(_("Not Permitted to read {0}").format(doctype), xhiveframework.PermissionError) + + filters = {} + if names: + if isinstance(names, str | bytearray): + names = xhiveframework.json.loads(names) + filters.update({"name": ["in", names]}) + + return xhiveframework._dict(xhiveframework.get_list(doctype, filters=filters, fields=["name", field], as_list=1)) + + +def get_data_for_custom_report(columns, result): + doc_field_value_map = {} + + for column in columns: + if link_field := column.get("link_field"): + # backwards compatibile `link_field` + # old custom reports which use `str` should not break + if isinstance(link_field, str): + link_field = xhiveframework._dict({"fieldname": link_field, "names": []}) + + fieldname = column.get("fieldname") + doctype = column.get("doctype") + + row_key = link_field.get("fieldname") + names = [] + for row in result: + if row.get(row_key): + names.append(row.get(row_key)) + names = list(set(names)) + + doc_field_value_map[(doctype, fieldname)] = get_data_for_custom_field(doctype, fieldname, names) + return doc_field_value_map + + +@xhiveframework.whitelist() +def save_report(reference_report, report_name, columns, filters): + report_doc = get_report_doc(reference_report) + + docname = xhiveframework.db.exists( + "Report", + { + "report_name": report_name, + "is_standard": "No", + "report_type": "Custom Report", + }, + ) + + if docname: + report = xhiveframework.get_doc("Report", docname) + existing_jd = json.loads(report.json) + existing_jd["columns"] = json.loads(columns) + existing_jd["filters"] = json.loads(filters) + report.update({"json": json.dumps(existing_jd, separators=(",", ":"))}) + report.save() + xhiveframework.msgprint(_("Report updated successfully")) + + return docname + else: + new_report = xhiveframework.get_doc( + { + "doctype": "Report", + "report_name": report_name, + "json": f'{{"columns":{columns},"filters":{filters}}}', + "ref_doctype": report_doc.ref_doctype, + "is_standard": "No", + "report_type": "Custom Report", + "reference_report": reference_report, + } + ).insert(ignore_permissions=True) + xhiveframework.msgprint(_("{0} saved successfully").format(new_report.name)) + return new_report.name + + +def get_filtered_data(ref_doctype, columns, data, user): + result = [] + linked_doctypes = get_linked_doctypes(columns, data) + match_filters_per_doctype = get_user_match_filters(linked_doctypes, user=user) + shared = xhiveframework.share.get_shared(ref_doctype, user) + columns_dict = get_columns_dict(columns) + + role_permissions = get_role_permissions(xhiveframework.get_meta(ref_doctype), user) + if_owner = role_permissions.get("if_owner", {}).get("report") + + if match_filters_per_doctype: + for row in data: + # Why linked_doctypes.get(ref_doctype)? because if column is empty, linked_doctypes[ref_doctype] is removed + if ( + linked_doctypes.get(ref_doctype) + and shared + and row.get(linked_doctypes[ref_doctype]) in shared + ): + result.append(row) + + elif has_match( + row, + linked_doctypes, + match_filters_per_doctype, + ref_doctype, + if_owner, + columns_dict, + user, + ): + result.append(row) + else: + result = list(data) + + return result + + +def has_match( + row, + linked_doctypes, + doctype_match_filters, + ref_doctype, + if_owner, + columns_dict, + user, +): + """Returns True if after evaluating permissions for each linked doctype + - There is an owner match for the ref_doctype + - `and` There is a user permission match for all linked doctypes + + Returns True if the row is empty + + Note: + Each doctype could have multiple conflicting user permission doctypes. + Hence even if one of the sets allows a match, it is true. + This behavior is equivalent to the trickling of user permissions of linked doctypes to the ref doctype. + """ + resultant_match = True + + if not row: + # allow empty rows :) + return resultant_match + + for doctype, filter_list in doctype_match_filters.items(): + matched_for_doctype = False + + if doctype == ref_doctype and if_owner: + idx = linked_doctypes.get("User") + if idx is not None and row[idx] == user and columns_dict[idx] == columns_dict.get("owner"): + # owner match is true + matched_for_doctype = True + + if not matched_for_doctype: + for match_filters in filter_list: + match = True + for dt, idx in linked_doctypes.items(): + # case handled above + if dt == "User" and columns_dict[idx] == columns_dict.get("owner"): + continue + + cell_value = None + if isinstance(row, dict): + cell_value = row.get(idx) + elif isinstance(row, list | tuple): + cell_value = row[idx] + + if ( + dt in match_filters + and cell_value not in match_filters.get(dt) + and xhiveframework.db.exists(dt, cell_value) + ): + match = False + break + + # each doctype could have multiple conflicting user permission doctypes, hence using OR + # so that even if one of the sets allows a match, it is true + matched_for_doctype = matched_for_doctype or match + + if matched_for_doctype: + break + + # each doctype's user permissions should match the row! hence using AND + resultant_match = resultant_match and matched_for_doctype + + if not resultant_match: + break + + return resultant_match + + +def get_linked_doctypes(columns, data): + linked_doctypes = {} + + columns_dict = get_columns_dict(columns) + + for idx in range(len(columns)): + df = columns_dict[idx] + if df.get("fieldtype") == "Link": + if data and isinstance(data[0], list | tuple): + linked_doctypes[df["options"]] = idx + else: + # dict + linked_doctypes[df["options"]] = df["fieldname"] + + # remove doctype if column is empty + columns_with_value = [] + for row in data: + if row: + if len(row) != len(columns_with_value): + if isinstance(row, list | tuple): + row = enumerate(row) + elif isinstance(row, dict): + row = row.items() + + for col, val in row: + if val and col not in columns_with_value: + columns_with_value.append(col) + + items = list(linked_doctypes.items()) + + for doctype, key in items: + if key not in columns_with_value: + del linked_doctypes[doctype] + + return linked_doctypes + + +def get_columns_dict(columns): + """Returns a dict with column docfield values as dict + The keys for the dict are both idx and fieldname, + so either index or fieldname can be used to search for a column's docfield properties + """ + columns_dict = xhiveframework._dict() + for idx, col in enumerate(columns): + col_dict = get_column_as_dict(col) + columns_dict[idx] = col_dict + columns_dict[col_dict["fieldname"]] = col_dict + + return columns_dict + + +def get_column_as_dict(col): + col_dict = xhiveframework._dict() + + # string + if isinstance(col, str): + col = col.split(":") + if len(col) > 1: + if "/" in col[1]: + col_dict["fieldtype"], col_dict["options"] = col[1].split("/") + else: + col_dict["fieldtype"] = col[1] + if len(col) == 3: + col_dict["width"] = col[2] + + col_dict["label"] = col[0] + col_dict["fieldname"] = xhiveframework.scrub(col[0]) + + # dict + else: + col_dict.update(col) + if "fieldname" not in col_dict: + col_dict["fieldname"] = xhiveframework.scrub(col_dict["label"]) + + return col_dict + + +def get_user_match_filters(doctypes, user): + match_filters = {} + + for dt in doctypes: + filter_list = xhiveframework.desk.reportview.build_match_conditions(dt, user, False) + if filter_list: + match_filters[dt] = filter_list + + return match_filters diff --git a/xhiveframework/desk/report/__init__.py b/xhiveframework/desk/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/report/todo/__init__.py b/xhiveframework/desk/report/todo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/desk/report/todo/todo.js b/xhiveframework/desk/report/todo/todo.js new file mode 100644 index 0000000..b004b96 --- /dev/null +++ b/xhiveframework/desk/report/todo/todo.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.query_reports["ToDo"] = { + filters: [], +}; diff --git a/xhiveframework/desk/report/todo/todo.json b/xhiveframework/desk/report/todo/todo.json new file mode 100644 index 0000000..b42c4c9 --- /dev/null +++ b/xhiveframework/desk/report/todo/todo.json @@ -0,0 +1,23 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2013-02-25 14:26:30", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2017-06-21 18:18:50.748793", + "modified_by": "Administrator", + "module": "Desk", + "name": "ToDo", + "owner": "Administrator", + "ref_doctype": "ToDo", + "report_name": "ToDo", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + } + ] +} \ No newline at end of file diff --git a/xhiveframework/desk/report/todo/todo.py b/xhiveframework/desk/report/todo/todo.py new file mode 100644 index 0000000..cc66a44 --- /dev/null +++ b/xhiveframework/desk/report/todo/todo.py @@ -0,0 +1,69 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils import getdate + + +def execute(filters=None): + priority_map = {"High": 3, "Medium": 2, "Low": 1} + + todo_list = xhiveframework.get_list( + "ToDo", + fields=[ + "name", + "date", + "description", + "priority", + "reference_type", + "reference_name", + "assigned_by", + "owner", + ], + filters={"status": "Open"}, + ) + + todo_list.sort( + key=lambda todo: ( + priority_map.get(todo.priority, 0), + todo.date and getdate(todo.date) or getdate("1900-01-01"), + ), + reverse=True, + ) + + columns = [ + _("ID") + ":Link/ToDo:90", + _("Priority") + "::60", + _("Date") + ":Date", + _("Description") + "::150", + _("Assigned To/Owner") + ":Data:120", + _("Assigned By") + ":Data:120", + _("Reference") + "::200", + ] + + result = [] + for todo in todo_list: + if todo.owner == xhiveframework.session.user or todo.assigned_by == xhiveframework.session.user: + if todo.reference_type: + todo.reference = """{}: {}""".format( + todo.reference_type, + todo.reference_name, + todo.reference_type, + todo.reference_name, + ) + else: + todo.reference = None + result.append( + [ + todo.name, + todo.priority, + todo.date, + todo.description, + todo.owner, + todo.assigned_by, + todo.reference, + ] + ) + + return columns, result diff --git a/xhiveframework/desk/reportview.py b/xhiveframework/desk/reportview.py new file mode 100644 index 0000000..5167612 --- /dev/null +++ b/xhiveframework/desk/reportview.py @@ -0,0 +1,717 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +"""build query for doclistview and return results""" + +import json + +import xhiveframework +import xhiveframework.permissions +from xhiveframework import _ +from xhiveframework.core.doctype.access_log.access_log import make_access_log +from xhiveframework.model import child_table_fields, default_fields, get_permitted_fields, optional_fields +from xhiveframework.model.base_document import get_controller +from xhiveframework.model.db_query import DatabaseQuery +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.utils import add_user_info, cint, format_duration +from xhiveframework.utils.data import sbool + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get(): + args = get_form_params() + # If virtual doctype, get data from controller get_list method + if is_virtual_doctype(args.doctype): + controller = get_controller(args.doctype) + data = compress(controller.get_list(args)) + else: + data = compress(execute(**args), args=args) + return data + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_list(): + args = get_form_params() + + if is_virtual_doctype(args.doctype): + controller = get_controller(args.doctype) + data = controller.get_list(args) + else: + # uncompressed (refactored from xhiveframework.model.db_query.get_list) + data = execute(**args) + + return data + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_count() -> int: + args = get_form_params() + + if is_virtual_doctype(args.doctype): + controller = get_controller(args.doctype) + count = controller.get_count(args) + else: + args.distinct = sbool(args.distinct) + distinct = "distinct " if args.distinct else "" + args.limit = cint(args.limit) + fieldname = f"{distinct}`tab{args.doctype}`.name" + args.order_by = None + + if args.limit: + args.fields = [fieldname] + partial_query = execute(**args, run=0) + count = xhiveframework.db.sql(f"""select count(*) from ( {partial_query} ) p""")[0][0] + else: + args.fields = [f"count({fieldname}) as total_count"] + count = execute(**args)[0].get("total_count") + + return count + + +def execute(doctype, *args, **kwargs): + return DatabaseQuery(doctype).execute(*args, **kwargs) + + +def get_form_params(): + """parse GET request parameters.""" + data = xhiveframework._dict(xhiveframework.local.form_dict) + clean_params(data) + validate_args(data) + return data + + +def validate_args(data): + parse_json(data) + setup_group_by(data) + + validate_fields(data) + if data.filters: + validate_filters(data, data.filters) + if data.or_filters: + validate_filters(data, data.or_filters) + + data.strict = None + + return data + + +def validate_fields(data): + wildcard = update_wildcard_field_param(data) + + for field in list(data.fields or []): + fieldname = extract_fieldname(field) + if is_standard(fieldname): + continue + + meta, df = get_meta_and_docfield(fieldname, data) + + if not df: + if wildcard: + continue + else: + raise_invalid_field(fieldname) + + # remove the field from the query if the report hide flag is set and current view is Report + if df.report_hide and data.view == "Report": + data.fields.remove(field) + continue + + if df.fieldname in [_df.fieldname for _df in meta.get_high_permlevel_fields()]: + if df.get("permlevel") not in meta.get_permlevel_access(parenttype=data.doctype): + data.fields.remove(field) + + +def validate_filters(data, filters): + if isinstance(filters, list): + # filters as list + for condition in filters: + if len(condition) == 3: + # [fieldname, condition, value] + fieldname = condition[0] + if is_standard(fieldname): + continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(condition[0]) + else: + # [doctype, fieldname, condition, value] + fieldname = condition[1] + if is_standard(fieldname): + continue + meta = xhiveframework.get_meta(condition[0]) + if not meta.get_field(fieldname): + raise_invalid_field(fieldname) + + else: + for fieldname in filters: + if is_standard(fieldname): + continue + meta, df = get_meta_and_docfield(fieldname, data) + if not df: + raise_invalid_field(fieldname) + + +def setup_group_by(data): + """Add columns for aggregated values e.g. count(name)""" + if data.group_by and data.aggregate_function: + if data.aggregate_function.lower() not in ("count", "sum", "avg"): + xhiveframework.throw(_("Invalid aggregate function")) + + if xhiveframework.db.has_column(data.aggregate_on_doctype, data.aggregate_on_field): + data.fields.append( + f"{data.aggregate_function}(`tab{data.aggregate_on_doctype}`.`{data.aggregate_on_field}`) AS _aggregate_column" + ) + else: + raise_invalid_field(data.aggregate_on_field) + + data.pop("aggregate_on_doctype") + data.pop("aggregate_on_field") + data.pop("aggregate_function") + + +def raise_invalid_field(fieldname): + xhiveframework.throw(_("Field not permitted in query") + f": {fieldname}", xhiveframework.DataError) + + +def is_standard(fieldname): + if "." in fieldname: + fieldname = fieldname.split(".")[1].strip("`") + return fieldname in default_fields or fieldname in optional_fields or fieldname in child_table_fields + + +def extract_fieldname(field): + for text in (",", "/*", "#"): + if text in field: + raise_invalid_field(field) + + fieldname = field + for sep in (" as ", " AS "): + if sep in fieldname: + fieldname = fieldname.split(sep, 1)[0] + + # certain functions allowed, extract the fieldname from the function + if fieldname.startswith("count(") or fieldname.startswith("sum(") or fieldname.startswith("avg("): + if not fieldname.strip().endswith(")"): + raise_invalid_field(field) + fieldname = fieldname.split("(", 1)[1][:-1] + + return fieldname + + +def get_meta_and_docfield(fieldname, data): + parenttype, fieldname = get_parenttype_and_fieldname(fieldname, data) + meta = xhiveframework.get_meta(parenttype) + df = meta.get_field(fieldname) + return meta, df + + +def update_wildcard_field_param(data): + if (isinstance(data.fields, str) and data.fields == "*") or ( + isinstance(data.fields, list | tuple) and len(data.fields) == 1 and data.fields[0] == "*" + ): + data.fields = get_permitted_fields(data.doctype, parenttype=data.parenttype) + return True + + return False + + +def clean_params(data): + for param in ("cmd", "data", "ignore_permissions", "view", "user", "csrf_token", "join"): + data.pop(param, None) + + +def parse_json(data): + if (filters := data.get("filters")) and isinstance(filters, str): + data["filters"] = json.loads(filters) + if (applied_filters := data.get("applied_filters")) and isinstance(applied_filters, str): + data["applied_filters"] = json.loads(applied_filters) + if (or_filters := data.get("or_filters")) and isinstance(or_filters, str): + data["or_filters"] = json.loads(or_filters) + if (fields := data.get("fields")) and isinstance(fields, str): + data["fields"] = ["*"] if fields == "*" else json.loads(fields) + if isinstance(data.get("docstatus"), str): + data["docstatus"] = json.loads(data["docstatus"]) + if isinstance(data.get("save_user_settings"), str): + data["save_user_settings"] = json.loads(data["save_user_settings"]) + else: + data["save_user_settings"] = True + + +def get_parenttype_and_fieldname(field, data): + if "." in field: + parts = field.split(".") + parenttype = parts[0] + fieldname = parts[1] + if parenttype.startswith("`tab"): + # `tabChild DocType`.`fieldname` + parenttype = parenttype[4:-1] + fieldname = fieldname.strip("`") + else: + # tablefield.fieldname + parenttype = xhiveframework.get_meta(data.doctype).get_field(parenttype).options + else: + parenttype = data.doctype + fieldname = field.strip("`") + + return parenttype, fieldname + + +def compress(data, args=None): + """separate keys and values""" + from xhiveframework.desk.query_report import add_total_row + + user_info = {} + + if not data: + return data + if args is None: + args = {} + values = [] + keys = list(data[0]) + for row in data: + values.append([row.get(key) for key in keys]) + + # add user info for assignments (avatar) + if row.get("_assign", ""): + for user in json.loads(row._assign): + add_user_info(user, user_info) + + if args.get("add_total_row"): + meta = xhiveframework.get_meta(args.doctype) + values = add_total_row(values, keys, meta) + + return {"keys": keys, "values": values, "user_info": user_info} + + +@xhiveframework.whitelist() +def save_report(name, doctype, report_settings): + """Save reports of type Report Builder from Report View""" + + if xhiveframework.db.exists("Report", name): + report = xhiveframework.get_doc("Report", name) + if report.is_standard == "Yes": + xhiveframework.throw(_("Standard Reports cannot be edited")) + + if report.report_type != "Report Builder": + xhiveframework.throw(_("Only reports of type Report Builder can be edited")) + + if report.owner != xhiveframework.session.user and not report.has_permission("write"): + xhiveframework.throw(_("Insufficient Permissions for editing Report"), xhiveframework.PermissionError) + else: + report = xhiveframework.new_doc("Report") + report.report_name = name + report.ref_doctype = doctype + + report.report_type = "Report Builder" + report.json = report_settings + report.save(ignore_permissions=True) + xhiveframework.msgprint( + _("Report {0} saved").format(xhiveframework.bold(report.name)), + indicator="green", + alert=True, + ) + return report.name + + +@xhiveframework.whitelist() +def delete_report(name): + """Delete reports of type Report Builder from Report View""" + + report = xhiveframework.get_doc("Report", name) + if report.is_standard == "Yes": + xhiveframework.throw(_("Standard Reports cannot be deleted")) + + if report.report_type != "Report Builder": + xhiveframework.throw(_("Only reports of type Report Builder can be deleted")) + + if report.owner != xhiveframework.session.user and not report.has_permission("delete"): + xhiveframework.throw(_("Insufficient Permissions for deleting Report"), xhiveframework.PermissionError) + + report.delete(ignore_permissions=True) + xhiveframework.msgprint( + _("Report {0} deleted").format(xhiveframework.bold(report.name)), + indicator="green", + alert=True, + ) + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def export_query(): + """export from report builder""" + from xhiveframework.desk.utils import get_csv_bytes, pop_csv_params, provide_binary_file + + form_params = get_form_params() + form_params["limit_page_length"] = None + form_params["as_list"] = True + doctype = form_params.pop("doctype") + file_format_type = form_params.pop("file_format_type") + title = form_params.pop("title", doctype) + csv_params = pop_csv_params(form_params) + add_totals_row = 1 if form_params.pop("add_totals_row", None) == "1" else None + + xhiveframework.permissions.can_export(doctype, raise_exception=True) + + if selection := form_params.pop("selected_items", None): + form_params["filters"] = {"name": ("in", json.loads(selection))} + + make_access_log( + doctype=doctype, + file_type=file_format_type, + report_name=form_params.report_name, + filters=form_params.filters, + ) + + db_query = DatabaseQuery(doctype) + ret = db_query.execute(**form_params) + + if add_totals_row: + ret = append_totals_row(ret) + + data = [[_("Sr"), *get_labels(db_query.fields, doctype)]] + data.extend([i + 1, *list(row)] for i, row in enumerate(ret)) + data = handle_duration_fieldtype_values(doctype, data, db_query.fields) + + if file_format_type == "CSV": + from xhiveframework.utils.xlsxutils import handle_html + + file_extension = "csv" + content = get_csv_bytes( + [[handle_html(xhiveframework.as_unicode(v)) if isinstance(v, str) else v for v in r] for r in data], + csv_params, + ) + elif file_format_type == "Excel": + from xhiveframework.utils.xlsxutils import make_xlsx + + file_extension = "xlsx" + content = make_xlsx(data, doctype).getvalue() + + provide_binary_file(title, file_extension, content) + + +def append_totals_row(data): + if not data: + return data + data = list(data) + totals = [] + totals.extend([""] * len(data[0])) + + for row in data: + for i in range(len(row)): + if isinstance(row[i], float | int): + totals[i] = (totals[i] or 0) + row[i] + + if not isinstance(totals[0], int | float): + totals[0] = "Total" + + data.append(totals) + + return data + + +def get_labels(fields, doctype): + """get column labels based on column names""" + labels = [] + for key in fields: + try: + parenttype, fieldname = parse_field(key) + except ValueError: + continue + + parenttype = parenttype or doctype + + if parenttype == doctype and fieldname == "name": + label = _("ID", context="Label of name column in report") + else: + df = xhiveframework.get_meta(parenttype).get_field(fieldname) + label = _(df.label if df else fieldname.title()) + if parenttype != doctype: + # If the column is from a child table, append the child doctype. + # For example, "Item Code (Sales Invoice Item)". + label += f" ({ _(parenttype) })" + + labels.append(label) + + return labels + + +def handle_duration_fieldtype_values(doctype, data, fields): + for field in fields: + try: + parenttype, fieldname = parse_field(field) + except ValueError: + continue + + parenttype = parenttype or doctype + df = xhiveframework.get_meta(parenttype).get_field(fieldname) + + if df and df.fieldtype == "Duration": + index = fields.index(field) + 1 + for i in range(1, len(data)): + val_in_seconds = data[i][index] + if val_in_seconds: + duration_val = format_duration(val_in_seconds, df.hide_days) + data[i][index] = duration_val + return data + + +def parse_field(field: str) -> tuple[str | None, str]: + """Parse a field into parenttype and fieldname.""" + key = field.split(" as ", 1)[0] + + if key.startswith(("count(", "sum(", "avg(")): + raise ValueError + + if "." in key: + table, column = key.split(".", 2)[:2] + return table[4:-1], column.strip("`") + + return None, key.strip("`") + + +@xhiveframework.whitelist() +def delete_items(): + """delete selected items""" + import json + + items = sorted(json.loads(xhiveframework.form_dict.get("items")), reverse=True) + doctype = xhiveframework.form_dict.get("doctype") + + if len(items) > 10: + xhiveframework.enqueue("xhiveframework.desk.reportview.delete_bulk", doctype=doctype, items=items) + else: + delete_bulk(doctype, items) + + +def delete_bulk(doctype, items): + undeleted_items = [] + for i, d in enumerate(items): + try: + xhiveframework.delete_doc(doctype, d) + if len(items) >= 5: + xhiveframework.publish_realtime( + "progress", + dict( + progress=[i + 1, len(items)], title=_("Deleting {0}").format(doctype), description=d + ), + user=xhiveframework.session.user, + ) + # Commit after successful deletion + xhiveframework.db.commit() + except Exception: + # rollback if any record failed to delete + # if not rollbacked, queries get committed on after_request method in app.py + undeleted_items.append(d) + xhiveframework.db.rollback() + if undeleted_items and len(items) != len(undeleted_items): + xhiveframework.clear_messages() + delete_bulk(doctype, undeleted_items) + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_sidebar_stats(stats, doctype, filters=None): + if filters is None: + filters = [] + + if is_virtual_doctype(doctype): + controller = get_controller(doctype) + args = {"stats": stats, "filters": filters} + data = controller.get_stats(args) + else: + data = get_stats(stats, doctype, filters) + + return {"stats": data} + + +@xhiveframework.whitelist() +@xhiveframework.read_only() +def get_stats(stats, doctype, filters=None): + """get tag info""" + import json + + if filters is None: + filters = [] + columns = json.loads(stats) + if filters: + filters = json.loads(filters) + results = {} + + try: + db_columns = xhiveframework.db.get_table_columns(doctype) + except (xhiveframework.db.InternalError, xhiveframework.db.ProgrammingError): + # raised when _user_tags column is added on the fly + # raised if its a virtual doctype + db_columns = [] + + for column in columns: + if column not in db_columns: + continue + try: + tag_count = xhiveframework.get_list( + doctype, + fields=[column, "count(*)"], + filters=[*filters, [column, "!=", ""]], + group_by=column, + as_list=True, + distinct=1, + ) + + if column == "_user_tags": + results[column] = scrub_user_tags(tag_count) + no_tag_count = xhiveframework.get_list( + doctype, + fields=[column, "count(*)"], + filters=[*filters, [column, "in", ("", ",")]], + as_list=True, + group_by=column, + order_by=column, + ) + + no_tag_count = no_tag_count[0][1] if no_tag_count else 0 + + results[column].append([_("No Tags"), no_tag_count]) + else: + results[column] = tag_count + + except xhiveframework.db.SQLError: + pass + except xhiveframework.db.InternalError: + # raised when _user_tags column is added on the fly + pass + + return results + + +@xhiveframework.whitelist() +def get_filter_dashboard_data(stats, doctype, filters=None): + """get tags info""" + import json + + tags = json.loads(stats) + filters = json.loads(filters or []) + stats = {} + + columns = xhiveframework.db.get_table_columns(doctype) + for tag in tags: + if tag["name"] not in columns: + continue + tagcount = [] + if tag["type"] not in ["Date", "Datetime"]: + tagcount = xhiveframework.get_list( + doctype, + fields=[tag["name"], "count(*)"], + filters=[*filters, "ifnull(`%s`,'')!=''" % tag["name"]], + group_by=tag["name"], + as_list=True, + ) + + if tag["type"] not in [ + "Check", + "Select", + "Date", + "Datetime", + "Int", + "Float", + "Currency", + "Percent", + ] and tag["name"] not in ["docstatus"]: + stats[tag["name"]] = list(tagcount) + if stats[tag["name"]]: + data = [ + "No Data", + xhiveframework.get_list( + doctype, + fields=[tag["name"], "count(*)"], + filters=[*filters, "({0} = '' or {0} is null)".format(tag["name"])], + as_list=True, + )[0][1], + ] + if data and data[1] != 0: + stats[tag["name"]].append(data) + else: + stats[tag["name"]] = tagcount + + return stats + + +def scrub_user_tags(tagcount): + """rebuild tag list for tags""" + rdict = {} + tagdict = dict(tagcount) + for t in tagdict: + if not t: + continue + alltags = t.split(",") + for tag in alltags: + if tag: + if tag not in rdict: + rdict[tag] = 0 + + rdict[tag] += tagdict[t] + + return [[tag, rdict[tag]] for tag in rdict] + + +# used in building query in queries.py +def get_match_cond(doctype, as_condition=True): + cond = DatabaseQuery(doctype).build_match_conditions(as_condition=as_condition) + if not as_condition: + return cond + + return ((" and " + cond) if cond else "").replace("%", "%%") + + +def build_match_conditions(doctype, user=None, as_condition=True): + match_conditions = DatabaseQuery(doctype, user=user).build_match_conditions(as_condition=as_condition) + if as_condition: + return match_conditions.replace("%", "%%") + return match_conditions + + +def get_filters_cond(doctype, filters, conditions, ignore_permissions=None, with_match_conditions=False): + if isinstance(filters, str): + filters = json.loads(filters) + + if filters: + flt = filters + if isinstance(filters, dict): + filters = filters.items() + flt = [] + for f in filters: + if isinstance(f[1], str) and f[1][0] == "!": + flt.append([doctype, f[0], "!=", f[1][1:]]) + elif isinstance(f[1], list | tuple) and f[1][0].lower() in ( + "=", + ">", + "<", + ">=", + "<=", + "!=", + "like", + "not like", + "in", + "not in", + "between", + "is", + ): + flt.append([doctype, f[0], f[1][0], f[1][1]]) + else: + flt.append([doctype, f[0], "=", f[1]]) + + query = DatabaseQuery(doctype) + query.filters = flt + query.conditions = conditions + + if with_match_conditions: + query.build_match_conditions() + + query.build_filter_conditions(flt, conditions, ignore_permissions) + + cond = " and " + " and ".join(query.conditions) + else: + cond = "" + return cond diff --git a/xhiveframework/desk/search.py b/xhiveframework/desk/search.py new file mode 100644 index 0000000..ca66f95 --- /dev/null +++ b/xhiveframework/desk/search.py @@ -0,0 +1,333 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import re +from typing import TypedDict + +from typing_extensions import NotRequired # not required in 3.11+ + +import xhiveframework + +# Backward compatbility +from xhiveframework import _, is_whitelisted, validate_and_sanitize_search_inputs +from xhiveframework.database.schema import SPECIAL_CHAR_PATTERN +from xhiveframework.model.db_query import get_order_by +from xhiveframework.permissions import has_permission +from xhiveframework.utils import cint, cstr, unique +from xhiveframework.utils.data import make_filter_tuple + + +def sanitize_searchfield(searchfield: str): + if not searchfield: + return + + if SPECIAL_CHAR_PATTERN.search(searchfield): + xhiveframework.throw(_("Invalid Search Field {0}").format(searchfield), xhiveframework.DataError) + + +class LinkSearchResults(TypedDict): + value: str + description: str + label: NotRequired[str] + + +# this is called by the Link Field +@xhiveframework.whitelist() +def search_link( + doctype: str, + txt: str, + query: str | None = None, + filters: str | dict | list | None = None, + page_length: int = 10, + searchfield: str | None = None, + reference_doctype: str | None = None, + ignore_user_permissions: bool = False, +) -> list[LinkSearchResults]: + results = search_widget( + doctype, + txt.strip(), + query, + searchfield=searchfield, + page_length=page_length, + filters=filters, + reference_doctype=reference_doctype, + ignore_user_permissions=ignore_user_permissions, + ) + return build_for_autosuggest(results, doctype=doctype) + + +# this is called by the search box +@xhiveframework.whitelist() +def search_widget( + doctype: str, + txt: str, + query: str | None = None, + searchfield: str | None = None, + start: int = 0, + page_length: int = 10, + filters: str | None | dict | list = None, + filter_fields=None, + as_dict: bool = False, + reference_doctype: str | None = None, + ignore_user_permissions: bool = False, +): + start = cint(start) + + if isinstance(filters, str): + filters = json.loads(filters) + + if searchfield: + sanitize_searchfield(searchfield) + + if not searchfield: + searchfield = "name" + + standard_queries = xhiveframework.get_hooks().standard_queries or {} + + if not query and doctype in standard_queries: + query = standard_queries[doctype][-1] + + if query: # Query = custom search query i.e. python function + try: + is_whitelisted(xhiveframework.get_attr(query)) + return xhiveframework.call( + query, + doctype, + txt, + searchfield, + start, + page_length, + filters, + as_dict=as_dict, + reference_doctype=reference_doctype, + ) + except (xhiveframework.PermissionError, xhiveframework.AppNotInstalledError, ImportError): + if xhiveframework.local.conf.developer_mode: + raise + else: + xhiveframework.respond_as_web_page( + title="Invalid Method", + html="Method not found", + indicator_color="red", + http_status_code=404, + ) + return [] + + meta = xhiveframework.get_meta(doctype) + + if isinstance(filters, dict): + filters_items = filters.items() + filters = [] + for key, value in filters_items: + filters.append(make_filter_tuple(doctype, key, value)) + + if filters is None: + filters = [] + or_filters = [] + + # build from doctype + if txt: + field_types = { + "Data", + "Text", + "Small Text", + "Long Text", + "Link", + "Select", + "Read Only", + "Text Editor", + } + search_fields = ["name"] + if meta.title_field: + search_fields.append(meta.title_field) + + if meta.search_fields: + search_fields.extend(meta.get_search_fields()) + + for f in search_fields: + fmeta = meta.get_field(f.strip()) + if not meta.translated_doctype and (f == "name" or (fmeta and fmeta.fieldtype in field_types)): + or_filters.append([doctype, f.strip(), "like", f"%{txt}%"]) + + if meta.get("fields", {"fieldname": "enabled", "fieldtype": "Check"}): + filters.append([doctype, "enabled", "=", 1]) + if meta.get("fields", {"fieldname": "disabled", "fieldtype": "Check"}): + filters.append([doctype, "disabled", "!=", 1]) + + # format a list of fields combining search fields and filter fields + fields = get_std_fields_list(meta, searchfield or "name") + if filter_fields: + fields = list(set(fields + json.loads(filter_fields))) + formatted_fields = [f"`tab{meta.name}`.`{f.strip()}`" for f in fields] + + # Insert title field query after name + if meta.show_title_field_in_link: + formatted_fields.insert(1, f"`tab{meta.name}`.{meta.title_field} as `label`") + + order_by_based_on_meta = get_order_by(doctype, meta) + # `idx` is number of times a document is referred, check link_count.py + order_by = f"`tab{doctype}`.idx desc, {order_by_based_on_meta}" + + if not meta.translated_doctype: + _txt = xhiveframework.db.escape((txt or "").replace("%", "").replace("@", "")) + # locate returns 0 if string is not found, convert 0 to null and then sort null to end in order by + _relevance = f"(1 / nullif(locate({_txt}, `tab{doctype}`.`name`), 0))" + formatted_fields.append(f"""{_relevance} as `_relevance`""") + # Since we are sorting by alias postgres needs to know number of column we are sorting + if xhiveframework.db.db_type == "mariadb": + order_by = f"ifnull(_relevance, -9999) desc, {order_by}" + elif xhiveframework.db.db_type == "postgres": + # Since we are sorting by alias postgres needs to know number of column we are sorting + order_by = f"{len(formatted_fields)} desc nulls last, {order_by}" + + ignore_permissions = doctype == "DocType" or ( + cint(ignore_user_permissions) + and has_permission( + doctype, + ptype="select" if xhiveframework.only_has_select_perm(doctype) else "read", + parent_doctype=reference_doctype, + ) + ) + + values = xhiveframework.get_list( + doctype, + filters=filters, + fields=formatted_fields, + or_filters=or_filters, + limit_start=start, + limit_page_length=None if meta.translated_doctype else page_length, + order_by=order_by, + ignore_permissions=ignore_permissions, + reference_doctype=reference_doctype, + as_list=not as_dict, + strict=False, + ) + + if meta.translated_doctype: + # Filtering the values array so that query is included in very element + values = ( + result + for result in values + if any( + re.search(f"{re.escape(txt)}.*", _(cstr(value)) or "", re.IGNORECASE) + for value in (result.values() if as_dict else result) + ) + ) + + # Sorting the values array so that relevant results always come first + # This will first bring elements on top in which query is a prefix of element + # Then it will bring the rest of the elements and sort them in lexicographical order + values = sorted(values, key=lambda x: relevance_sorter(x, txt, as_dict)) + + # remove _relevance from results + if not meta.translated_doctype: + if as_dict: + for r in values: + r.pop("_relevance", None) + else: + values = [r[:-1] for r in values] + + return values + + +def get_std_fields_list(meta, key): + # get additional search fields + sflist = ["name"] + + if meta.title_field and meta.title_field not in sflist: + sflist.append(meta.title_field) + + if key not in sflist: + sflist.append(key) + + if meta.search_fields: + for d in meta.search_fields.split(","): + if d.strip() not in sflist: + sflist.append(d.strip()) + + return sflist + + +def build_for_autosuggest(res: list[tuple], doctype: str) -> list[LinkSearchResults]: + def to_string(parts): + return ", ".join( + unique(_(cstr(part)) if meta.translated_doctype else cstr(part) for part in parts if part) + ) + + results = [] + meta = xhiveframework.get_meta(doctype) + if meta.show_title_field_in_link: + for item in res: + item = list(item) + if len(item) == 1: + item = [item[0], item[0]] + label = item[1] # use title as label + item[1] = item[0] # show name in description instead of title + if len(item) >= 3 and item[2] == label: + # remove redundant title ("label") value + del item[2] + results.append({"value": item[0], "label": label, "description": to_string(item[1:])}) + else: + results.extend({"value": item[0], "description": to_string(item[1:])} for item in res) + + return results + + +def scrub_custom_query(query, key, txt): + if "%(key)s" in query: + query = query.replace("%(key)s", key) + if "%s" in query: + query = query.replace("%s", ((txt or "") + "%")) + return query + + +def relevance_sorter(key, query, as_dict): + value = _(key.name if as_dict else key[0]) + return (cstr(value).casefold().startswith(query.casefold()) is not True, value) + + +@xhiveframework.whitelist() +def get_names_for_mentions(search_term): + users_for_mentions = xhiveframework.cache.get_value("users_for_mentions", get_users_for_mentions) + user_groups = xhiveframework.cache.get_value("user_groups", get_user_groups) + + filtered_mentions = [] + for mention_data in users_for_mentions + user_groups: + if search_term.lower() not in mention_data.value.lower(): + continue + + mention_data["link"] = xhiveframework.utils.get_url_to_form( + "User Group" if mention_data.get("is_group") else "User Profile", mention_data["id"] + ) + + filtered_mentions.append(mention_data) + + return sorted(filtered_mentions, key=lambda d: d["value"]) + + +def get_users_for_mentions(): + return xhiveframework.get_all( + "User", + fields=["name as id", "full_name as value"], + filters={ + "name": ["not in", ("Administrator", "Guest")], + "allowed_in_mentions": True, + "user_type": "System User", + "enabled": True, + }, + ) + + +def get_user_groups(): + return xhiveframework.get_all("User Group", fields=["name as id", "name as value"], update={"is_group": True}) + + +@xhiveframework.whitelist() +def get_link_title(doctype, docname): + meta = xhiveframework.get_meta(doctype) + + if meta.show_title_field_in_link: + return xhiveframework.db.get_value(doctype, docname, meta.title_field) + + return docname diff --git a/xhiveframework/desk/treeview.py b/xhiveframework/desk/treeview.py new file mode 100644 index 0000000..c752839 --- /dev/null +++ b/xhiveframework/desk/treeview.py @@ -0,0 +1,84 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ + + +@xhiveframework.whitelist() +def get_all_nodes(doctype, label, parent, tree_method, **filters): + """Recursively gets all data from tree nodes""" + + if "cmd" in filters: + del filters["cmd"] + filters.pop("data", None) + + tree_method = xhiveframework.get_attr(tree_method) + + if tree_method not in xhiveframework.whitelisted: + xhiveframework.throw(_("Not Permitted"), xhiveframework.PermissionError) + + data = tree_method(doctype, parent, **filters) + out = [dict(parent=label, data=data)] + + if "is_root" in filters: + del filters["is_root"] + to_check = [d.get("value") for d in data if d.get("expandable")] + + while to_check: + parent = to_check.pop() + data = tree_method(doctype, parent, is_root=False, **filters) + out.append(dict(parent=parent, data=data)) + for d in data: + if d.get("expandable"): + to_check.append(d.get("value")) + + return out + + +@xhiveframework.whitelist() +def get_children(doctype, parent="", **filters): + return _get_children(doctype, parent) + + +def _get_children(doctype, parent="", ignore_permissions=False): + parent_field = "parent_" + doctype.lower().replace(" ", "_") + filters = [[f"ifnull(`{parent_field}`,'')", "=", parent], ["docstatus", "<", 2]] + + meta = xhiveframework.get_meta(doctype) + + return xhiveframework.get_list( + doctype, + fields=[ + "name as value", + "{} as title".format(meta.get("title_field") or "name"), + "is_group as expandable", + ], + filters=filters, + order_by="name", + ignore_permissions=ignore_permissions, + ) + + +@xhiveframework.whitelist() +def add_node(): + args = make_tree_args(**xhiveframework.form_dict) + doc = xhiveframework.get_doc(args) + + doc.save() + + +def make_tree_args(**kwarg): + kwarg.pop("cmd", None) + + doctype = kwarg["doctype"] + parent_field = "parent_" + doctype.lower().replace(" ", "_") + + if kwarg["is_root"] == "false": + kwarg["is_root"] = False + if kwarg["is_root"] == "true": + kwarg["is_root"] = True + + kwarg.update({parent_field: kwarg.get("parent") or kwarg.get(parent_field)}) + + return xhiveframework._dict(kwarg) diff --git a/xhiveframework/desk/utils.py b/xhiveframework/desk/utils.py new file mode 100644 index 0000000..59efd39 --- /dev/null +++ b/xhiveframework/desk/utils.py @@ -0,0 +1,60 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def validate_route_conflict(doctype, name): + """ + Raises exception if name clashes with routes from other documents for /app routing + """ + + if xhiveframework.flags.in_migrate: + return + + all_names = [] + for _doctype in ["Page", "Workspace", "DocType"]: + all_names.extend( + [slug(d) for d in xhiveframework.get_all(_doctype, pluck="name") if (doctype != _doctype and d != name)] + ) + + if slug(name) in all_names: + xhiveframework.msgprint(xhiveframework._("Name already taken, please set a new name")) + raise xhiveframework.NameError + + +def slug(name): + return name.lower().replace(" ", "-") + + +def pop_csv_params(form_dict): + """Pop csv params from form_dict and return them as a dict.""" + from csv import QUOTE_NONNUMERIC + + from xhiveframework.utils.data import cint, cstr + + return { + "delimiter": cstr(form_dict.pop("csv_delimiter", ","))[0], + "quoting": cint(form_dict.pop("csv_quoting", QUOTE_NONNUMERIC)), + } + + +def get_csv_bytes(data: list[list], csv_params: dict) -> bytes: + """Convert data to csv bytes.""" + from csv import writer + from io import StringIO + + file = StringIO() + csv_writer = writer(file, **csv_params) + csv_writer.writerows(data) + + return file.getvalue().encode("utf-8") + + +def provide_binary_file(filename: str, extension: str, content: bytes) -> None: + """Provide a binary file to the client.""" + from xhiveframework import _ + + xhiveframework.response["type"] = "binary" + xhiveframework.response["filecontent"] = content + xhiveframework.response["filename"] = f"{_(filename)}.{extension}" diff --git a/xhiveframework/email/__init__.py b/xhiveframework/email/__init__.py new file mode 100644 index 0000000..96f50f7 --- /dev/null +++ b/xhiveframework/email/__init__.py @@ -0,0 +1,121 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def sendmail_to_system_managers(subject, content): + xhiveframework.sendmail(recipients=get_system_managers(), subject=subject, content=content) + + +@xhiveframework.whitelist() +def get_contact_list(txt, page_length=20) -> list[dict]: + """Return email ids for a multiselect field.""" + from xhiveframework.contacts.doctype.contact.contact import get_full_name + + if cached_contacts := get_cached_contacts(txt): + return cached_contacts[:page_length] + + fields = ["first_name", "middle_name", "last_name", "company_name"] + contacts = xhiveframework.get_list( + "Contact", + fields=[*fields, "`tabContact Email`.email_id"], + filters=[ + ["Contact Email", "email_id", "is", "set"], + ], + or_filters=[[field, "like", f"%{txt}%"] for field in fields] + + [["Contact Email", "email_id", "like", f"%{txt}%"]], + limit_page_length=page_length, + ) + + # The multiselect field will store the `label` as the selected value. + # The `value` is just used as a unique key to distinguish between the options. + # https://lab.membtech.com/xhiveframework/xhiveframework15/blob/6c6a89bcdd9454060a1333e23b855d0505c9ebc2/xhiveframework/public/js/xhiveframework/form/controls/autocomplete.js#L29-L35 + result = [ + xhiveframework._dict( + value=d.email_id, + label=d.email_id, + description=get_full_name(d.first_name, d.middle_name, d.last_name, d.company_name), + ) + for d in contacts + ] + + update_contact_cache(result) + + return result + + +def get_system_managers(): + return xhiveframework.db.sql_list( + """select parent FROM `tabHas Role` + WHERE role='System Manager' + AND parent!='Administrator' + AND parent IN (SELECT email FROM tabUser WHERE enabled=1)""" + ) + + +@xhiveframework.whitelist() +def relink(name, reference_doctype=None, reference_name=None): + xhiveframework.db.sql( + """update + `tabCommunication` + set + reference_doctype = %s, + reference_name = %s, + status = "Linked" + where + communication_type = "Communication" and + name = %s""", + (reference_doctype, reference_name, name), + ) + + +@xhiveframework.whitelist() +@xhiveframework.validate_and_sanitize_search_inputs +def get_communication_doctype(doctype, txt, searchfield, start, page_len, filters): + user_perms = xhiveframework.utils.user.UserPermissions(xhiveframework.session.user) + user_perms.build_permissions() + can_read = user_perms.can_read + from xhiveframework.modules import load_doctype_module + + com_doctypes = [] + if len(txt) < 2: + for name in xhiveframework.get_hooks("communication_doctypes"): + try: + module = load_doctype_module(name, suffix="_dashboard") + if hasattr(module, "get_data"): + for i in module.get_data()["transactions"]: + com_doctypes += i["items"] + except ImportError: + pass + else: + com_doctypes = [ + d[0] for d in xhiveframework.db.get_values("DocType", {"issingle": 0, "istable": 0, "hide_toolbar": 0}) + ] + + return [[dt] for dt in com_doctypes if txt.lower().replace("%", "") in dt.lower() and dt in can_read] + + +def get_cached_contacts(txt): + contacts = xhiveframework.cache.hget("contacts", xhiveframework.session.user) or [] + + if not contacts: + return + + if not txt: + return contacts + + return [ + d + for d in contacts + if (d.value and ((d.value and txt in d.value) or (d.description and txt in d.description))) + ] + + +def update_contact_cache(contacts): + cached_contacts = xhiveframework.cache.hget("contacts", xhiveframework.session.user) or [] + + uncached_contacts = [d for d in contacts if d not in cached_contacts] + cached_contacts.extend(uncached_contacts) + + xhiveframework.cache.hset("contacts", xhiveframework.session.user, cached_contacts) diff --git a/xhiveframework/email/assets/images/email-pull-flow.png b/xhiveframework/email/assets/images/email-pull-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..1518f9f6d589be0d5fdeded7b212896b54134cd3 GIT binary patch literal 19057 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/email/doctype/__init__.py b/xhiveframework/email/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/auto_email_report/__init__.py b/xhiveframework/email/doctype/auto_email_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/auto_email_report/auto_email_report.js b/xhiveframework/email/doctype/auto_email_report/auto_email_report.js new file mode 100644 index 0000000..8a29891 --- /dev/null +++ b/xhiveframework/email/doctype/auto_email_report/auto_email_report.js @@ -0,0 +1,180 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Auto Email Report", { + refresh: function (frm) { + frm.trigger("fetch_report_filters"); + if (!frm.is_new()) { + frm.add_custom_button(__("Download"), function () { + var w = window.open( + xhiveframework.urllib.get_full_url( + "/api/method/xhiveframework.email.doctype.auto_email_report.auto_email_report.download?" + + "name=" + + encodeURIComponent(frm.doc.name) + ) + ); + if (!w) { + xhiveframework.msgprint(__("Please enable pop-ups")); + return; + } + }); + frm.add_custom_button(__("Send Now"), function () { + xhiveframework.call({ + method: "xhiveframework.email.doctype.auto_email_report.auto_email_report.send_now", + args: { name: frm.doc.name }, + callback: function () { + xhiveframework.msgprint(__("Scheduled to send")); + }, + }); + }); + } else { + if (!frm.doc.user) { + frm.set_value("user", xhiveframework.session.user); + } + if (!frm.doc.email_to) { + frm.set_value("email_to", xhiveframework.session.user); + } + } + + frm.set_query("sender", function () { + return { + filters: { + enable_outgoing: 1, + awaiting_password: 0, + }, + }; + }); + }, + report: function (frm) { + frm.set_value("filters", ""); + frm.trigger("fetch_report_filters"); + }, + fetch_report_filters(frm) { + if ( + frm.doc.report && + frm.doc.report_type !== "Report Builder" && + frm.script_setup_for !== frm.doc.report + ) { + xhiveframework.call({ + method: "xhiveframework.desk.query_report.get_script", + args: { + report_name: frm.doc.report, + }, + callback: function (r) { + xhiveframework.dom.eval(r.message.script || ""); + frm.script_setup_for = frm.doc.report; + frm.trigger("show_filters"); + }, + }); + } else { + frm.trigger("show_filters"); + } + }, + show_filters: async function (frm) { + var wrapper = $(frm.get_field("filters_display").wrapper); + wrapper.empty(); + let reference_report = xhiveframework.query_reports[frm.doc.report]; + if (!reference_report || !reference_report.filters) { + reference_report = await xhiveframework.model.with_doc("Report", frm.doc.report); + } + if ( + frm.doc.report_type === "Custom Report" || + (frm.doc.report_type !== "Report Builder" && + reference_report && + reference_report.filters) + ) { + // make a table to show filters + var table = $( + '
      Id + Time + State + Info + Progress / Wait Event +
      \ + \ +
      ' + + __("Filter") + + "" + + __("Value") + + "
      " + ).appendTo(wrapper); + $('

      ' + __("Click table to edit") + "

      ").appendTo( + wrapper + ); + + var filters = {}; + + let report_filters; + + if ( + frm.doc.report_type === "Custom Report" && + reference_report && + reference_report.filters + ) { + if (frm.doc.filters) { + filters = JSON.parse(frm.doc.filters); + } else { + xhiveframework.db.get_value("Report", frm.doc.report, "json", (r) => { + if (r && r.json) { + filters = JSON.parse(r.json).filters || {}; + } + }); + } + + report_filters = xhiveframework.query_reports[frm.doc.reference_report].filters; + } else { + filters = JSON.parse(frm.doc.filters || "{}"); + report_filters = reference_report.filters; + } + + if (report_filters && report_filters.length > 0) { + frm.set_value("filter_meta", JSON.stringify(report_filters)); + if (frm.is_dirty()) { + frm.save(); + } + } + + var report_filters_list = []; + $.each(report_filters, function (key, val) { + // Remove break fieldtype from the filters + if (val.fieldtype != "Break") { + report_filters_list.push(val); + } + }); + report_filters = report_filters_list; + + const mandatory_css = { + "background-color": "var(--error-bg)", + "font-weight": "bold", + }; + + report_filters.forEach((f) => { + const css = f.reqd ? mandatory_css : {}; + const row = $("").appendTo(table.find("tbody")); + $("" + f.label + "").appendTo(row); + $("" + xhiveframework.format(filters[f.fieldname], f) + "") + .css(css) + .appendTo(row); + }); + + table.on("click", function () { + var dialog = new xhiveframework.ui.Dialog({ + fields: report_filters, + primary_action: function () { + var values = this.get_values(); + if (values) { + this.hide(); + frm.set_value("filters", JSON.stringify(values)); + frm.trigger("show_filters"); + } + }, + }); + dialog.show(); + dialog.set_values(filters); + }); + + // populate dynamic date field selection + let date_fields = report_filters + .filter((df) => df.fieldtype === "Date") + .map((df) => ({ label: df.label, value: df.fieldname })); + frm.set_df_property("from_date_field", "options", date_fields); + frm.set_df_property("to_date_field", "options", date_fields); + frm.toggle_display("dynamic_report_filters_section", date_fields.length > 0); + } + }, +}); diff --git a/xhiveframework/email/doctype/auto_email_report/auto_email_report.json b/xhiveframework/email/doctype/auto_email_report/auto_email_report.json new file mode 100644 index 0000000..41beac3 --- /dev/null +++ b/xhiveframework/email/doctype/auto_email_report/auto_email_report.json @@ -0,0 +1,258 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2016-09-01 01:34:34.985457", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "report", + "user", + "enabled", + "column_break_4", + "report_type", + "reference_report", + "filter_data", + "send_if_data", + "data_modified_till", + "no_of_rows", + "report_filters", + "filters_display", + "filters", + "filter_meta", + "dynamic_report_filters_section", + "from_date_field", + "to_date_field", + "column_break_17", + "dynamic_date_period", + "use_first_day_of_period", + "email_settings", + "email_to", + "day_of_week", + "column_break_13", + "sender", + "frequency", + "format", + "section_break_15", + "description" + ], + "fields": [ + { + "fieldname": "report", + "fieldtype": "Link", + "label": "Report", + "options": "Report", + "reqd": 1 + }, + { + "default": "User", + "fieldname": "user", + "fieldtype": "Link", + "label": "Based on Permissions For User", + "options": "User", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fetch_from": "report.report_type", + "fieldname": "report_type", + "fieldtype": "Read Only", + "label": "Report Type" + }, + { + "fieldname": "filter_data", + "fieldtype": "Section Break", + "label": "Filter Data" + }, + { + "default": "1", + "fieldname": "send_if_data", + "fieldtype": "Check", + "label": "Send only if there is any data" + }, + { + "depends_on": "eval:doc.report_type=='Report Builder'", + "description": "Zero means send records updated at anytime", + "fieldname": "data_modified_till", + "fieldtype": "Int", + "label": "Only Send Records Updated in Last X Hours" + }, + { + "default": "100", + "depends_on": "eval:doc.report_type=='Report Builder'", + "fieldname": "no_of_rows", + "fieldtype": "Int", + "label": "No of Rows (Max 500)" + }, + { + "collapsible": 1, + "depends_on": "eval:doc.report_type !== 'Report Builder'", + "fieldname": "report_filters", + "fieldtype": "Section Break", + "label": "Report Filters" + }, + { + "fieldname": "filters_display", + "fieldtype": "HTML", + "label": "Filters Display" + }, + { + "fieldname": "filters", + "fieldtype": "Text", + "hidden": 1, + "label": "Filters" + }, + { + "fieldname": "filter_meta", + "fieldtype": "Text", + "hidden": 1, + "label": "Filter Meta", + "read_only": 1 + }, + { + "collapsible": 1, + "depends_on": "eval:doc.report_type !== 'Report Builder'", + "fieldname": "dynamic_report_filters_section", + "fieldtype": "Section Break", + "label": "Dynamic Report Filters" + }, + { + "fieldname": "from_date_field", + "fieldtype": "Select", + "label": "From Date Field" + }, + { + "fieldname": "to_date_field", + "fieldtype": "Select", + "label": "To Date Field" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "dynamic_date_period", + "fieldtype": "Select", + "label": "Period", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf Yearly\nYearly" + }, + { + "fieldname": "email_settings", + "fieldtype": "Section Break", + "label": "Email Settings" + }, + { + "description": "For multiple addresses, enter the address on different line. e.g. test@test.com \u23ce test1@test.com", + "fieldname": "email_to", + "fieldtype": "Small Text", + "label": "Email To", + "reqd": 1 + }, + { + "default": "Monday", + "depends_on": "eval:doc.frequency=='Weekly'", + "fieldname": "day_of_week", + "fieldtype": "Select", + "label": "Day of Week", + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Frequency", + "options": "Daily\nWeekdays\nWeekly\nMonthly", + "reqd": 1 + }, + { + "fieldname": "format", + "fieldtype": "Select", + "label": "Format", + "options": "HTML\nXLSX\nCSV", + "reqd": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_15", + "fieldtype": "Section Break", + "label": "Message" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Message" + }, + { + "fetch_from": "report.reference_report", + "fieldname": "reference_report", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Report", + "read_only": 1 + }, + { + "fieldname": "sender", + "fieldtype": "Link", + "label": "Sender", + "options": "Email Account" + }, + { + "default": "0", + "depends_on": "eval: doc.dynamic_date_period != 'Daily'", + "description": "To begin the date range at the start of the chosen period. For example, if 'Year' is selected as the period, the report will start from January 1st of the current year.", + "fieldname": "use_first_day_of_period", + "fieldtype": "Check", + "label": "Use First Day of Period" + } + ], + "links": [], + "modified": "2024-02-04 13:31:08.624648", + "modified_by": "Administrator", + "module": "Email", + "name": "Auto Email Report", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Report Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/auto_email_report/auto_email_report.py b/xhiveframework/email/doctype/auto_email_report/auto_email_report.py new file mode 100644 index 0000000..77ccf9d --- /dev/null +++ b/xhiveframework/email/doctype/auto_email_report/auto_email_report.py @@ -0,0 +1,381 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import calendar +import datetime +from datetime import timedelta +from email.utils import formataddr + +import xhiveframework +from xhiveframework import _ +from xhiveframework.desk.query_report import build_xlsx_data +from xhiveframework.model.document import Document +from xhiveframework.model.naming import append_number_if_name_exists +from xhiveframework.utils import ( + add_to_date, + cint, + format_time, + get_first_day, + get_first_day_of_week, + get_link_to_form, + get_quarter_start, + get_url_to_report, + get_year_start, + getdate, + global_date_format, + now, + now_datetime, + today, + validate_email_address, +) +from xhiveframework.utils.csvutils import to_csv +from xhiveframework.utils.xlsxutils import make_xlsx + + +class AutoEmailReport(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data_modified_till: DF.Int + day_of_week: DF.Literal["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + description: DF.TextEditor | None + dynamic_date_period: DF.Literal[ + "", "Daily", "Weekly", "Monthly", "Quarterly", "Half Yearly", "Yearly" + ] + email_to: DF.SmallText + enabled: DF.Check + filter_meta: DF.Text | None + filters: DF.Text | None + format: DF.Literal["HTML", "XLSX", "CSV"] + frequency: DF.Literal["Daily", "Weekdays", "Weekly", "Monthly"] + from_date_field: DF.Literal[None] + no_of_rows: DF.Int + reference_report: DF.Data | None + report: DF.Link + report_type: DF.ReadOnly | None + send_if_data: DF.Check + sender: DF.Link | None + to_date_field: DF.Literal[None] + use_first_day_of_period: DF.Check + user: DF.Link + # end: auto-generated types + + def autoname(self): + self.name = _(self.report) + if xhiveframework.db.exists("Auto Email Report", self.name): + self.name = append_number_if_name_exists("Auto Email Report", self.name) + + def validate(self): + self.validate_report_count() + self.validate_emails() + self.validate_report_format() + self.validate_mandatory_fields() + + @property + def sender_email(self): + return xhiveframework.db.get_value("Email Account", self.sender, "email_id") + + def validate_emails(self): + """Cleanup list of emails""" + if "," in self.email_to: + self.email_to.replace(",", "\n") + + valid = [] + for email in self.email_to.split(): + if email: + validate_email_address(email, True) + valid.append(email) + + self.email_to = "\n".join(valid) + + def validate_report_count(self): + count = xhiveframework.db.count("Auto Email Report", {"user": self.user, "enabled": 1}) + + max_reports_per_user = ( + cint(xhiveframework.local.conf.max_reports_per_user) # kept for backward compatibilty + or cint(xhiveframework.db.get_single_value("System Settings", "max_auto_email_report_per_user")) + or 20 + ) + + if count > max_reports_per_user + (-1 if self.flags.in_insert else 0): + msg = _("Only {0} emailed reports are allowed per user.").format(max_reports_per_user) + msg += " " + _("To allow more reports update limit in System Settings.") + xhiveframework.throw(msg, title=_("Report limit reached")) + + def validate_report_format(self): + """check if user has select correct report format""" + valid_report_formats = ["HTML", "XLSX", "CSV"] + if self.format not in valid_report_formats: + xhiveframework.throw( + _("{0} is not a valid report format. Report format should one of the following {1}").format( + xhiveframework.bold(self.format), xhiveframework.bold(", ".join(valid_report_formats)) + ) + ) + + def validate_mandatory_fields(self): + # Check if all Mandatory Report Filters are filled by the User + filters = xhiveframework.parse_json(self.filters) if self.filters else {} + filter_meta = xhiveframework.parse_json(self.filter_meta) if self.filter_meta else {} + throw_list = [ + meta["label"] for meta in filter_meta if meta.get("reqd") and not filters.get(meta["fieldname"]) + ] + if throw_list: + xhiveframework.throw( + title=_("Missing Filters Required"), + msg=_("Following Report Filters have missing values:") + + "

      • " + + "
      • ".join(throw_list) + + "
      ", + ) + + def get_report_content(self): + """Returns file in for the report in given format""" + report = xhiveframework.get_doc("Report", self.report) + + self.filters = xhiveframework.parse_json(self.filters) if self.filters else {} + + if self.report_type == "Report Builder" and self.data_modified_till: + self.filters["modified"] = (">", now_datetime() - timedelta(hours=self.data_modified_till)) + + if self.report_type != "Report Builder" and self.dynamic_date_filters_set(): + self.prepare_dynamic_filters() + + columns, data = report.get_data( + limit=self.no_of_rows or 100, + user=self.user, + filters=self.filters, + as_dict=True, + ignore_prepared_report=True, + are_default_filters=False, + ) + + # add serial numbers + columns.insert(0, xhiveframework._dict(fieldname="idx", label="", width="30px")) + for i in range(len(data)): + data[i]["idx"] = i + 1 + + if len(data) == 0 and self.send_if_data: + return None + + if self.format == "HTML": + columns, data = make_links(columns, data) + columns = update_field_types(columns) + return self.get_html_table(columns, data) + + elif self.format == "XLSX": + report_data = xhiveframework._dict() + report_data["columns"] = columns + report_data["result"] = data + + xlsx_data, column_widths = build_xlsx_data(report_data, [], 1, ignore_visible_idx=True) + xlsx_file = make_xlsx(xlsx_data, "Auto Email Report", column_widths=column_widths) + return xlsx_file.getvalue() + + elif self.format == "CSV": + report_data = xhiveframework._dict() + report_data["columns"] = columns + report_data["result"] = data + + xlsx_data, column_widths = build_xlsx_data(report_data, [], 1, ignore_visible_idx=True) + return to_csv(xlsx_data) + + else: + xhiveframework.throw(_("Invalid Output Format")) + + def get_html_table(self, columns=None, data=None): + date_time = global_date_format(now()) + " " + format_time(now()) + report_doctype = xhiveframework.db.get_value("Report", self.report, "ref_doctype") + + return xhiveframework.render_template( + "xhiveframework/templates/emails/auto_email_report.html", + { + "title": self.name, + "description": self.description, + "date_time": date_time, + "columns": columns, + "data": data, + "report_url": get_url_to_report(self.report, self.report_type, report_doctype), + "report_name": self.report, + "edit_report_settings": get_link_to_form("Auto Email Report", self.name), + }, + ) + + def get_file_name(self): + return "{}.{}".format(self.report.replace(" ", "-").replace("/", "-"), self.format.lower()) + + def prepare_dynamic_filters(self): + self.filters = xhiveframework.parse_json(self.filters) + + to_date = today() + + if self.use_first_day_of_period: + from_date = to_date + if self.dynamic_date_period == "Daily": + from_date = add_to_date(to_date, days=-1) + elif self.dynamic_date_period == "Weekly": + from_date = get_first_day_of_week(from_date) + elif self.dynamic_date_period == "Monthly": + from_date = get_first_day(from_date) + elif self.dynamic_date_period == "Quarterly": + from_date = get_quarter_start(from_date) + elif self.dynamic_date_period == "Half Yearly": + from_date = get_half_year_start(from_date) + elif self.dynamic_date_period == "Yearly": + from_date = get_year_start(from_date) + + self.set_date_filters(from_date, to_date) + else: + from_date_value = { + "Daily": ("days", -1), + "Weekly": ("weeks", -1), + "Monthly": ("months", -1), + "Quarterly": ("months", -3), + "Half Yearly": ("months", -6), + "Yearly": ("years", -1), + }[self.dynamic_date_period] + + from_date = add_to_date(to_date, **{from_date_value[0]: from_date_value[1]}) + self.set_date_filters(from_date, to_date) + + def set_date_filters(self, from_date, to_date): + self.filters[self.from_date_field] = from_date + self.filters[self.to_date_field] = to_date + + def send(self): + if self.filter_meta and not self.filters: + xhiveframework.throw(_("Please set filters value in Report Filter table.")) + + data = self.get_report_content() + if not data: + return + + attachments = None + if self.format == "HTML": + message = data + else: + message = self.get_html_table() + + if not self.format == "HTML": + attachments = [{"fname": self.get_file_name(), "fcontent": data}] + + xhiveframework.sendmail( + recipients=self.email_to.split(), + sender=formataddr((self.sender, self.sender_email)) if self.sender else "", + subject=self.name, + message=message, + attachments=attachments, + reference_doctype=self.doctype, + reference_name=self.name, + ) + + def dynamic_date_filters_set(self): + return self.dynamic_date_period and self.from_date_field and self.to_date_field + + +@xhiveframework.whitelist() +def download(name): + """Download report locally""" + auto_email_report = xhiveframework.get_doc("Auto Email Report", name) + auto_email_report.check_permission() + data = auto_email_report.get_report_content() + + if not data: + xhiveframework.msgprint(_("No Data")) + return + + xhiveframework.local.response.filecontent = data + xhiveframework.local.response.type = "download" + xhiveframework.local.response.filename = auto_email_report.get_file_name() + + +@xhiveframework.whitelist() +def send_now(name): + """Send Auto Email report now""" + auto_email_report = xhiveframework.get_doc("Auto Email Report", name) + auto_email_report.check_permission() + auto_email_report.send() + + +def send_daily(): + """Check reports to be sent daily""" + + current_day = calendar.day_name[now_datetime().weekday()] + enabled_reports = xhiveframework.get_all( + "Auto Email Report", filters={"enabled": 1, "frequency": ("in", ("Daily", "Weekdays", "Weekly"))} + ) + + for report in enabled_reports: + auto_email_report = xhiveframework.get_doc("Auto Email Report", report.name) + + # if not correct weekday, skip + if auto_email_report.frequency == "Weekdays": + if current_day in ("Saturday", "Sunday"): + continue + elif auto_email_report.frequency == "Weekly": + if auto_email_report.day_of_week != current_day: + continue + try: + auto_email_report.send() + except Exception: + auto_email_report.log_error(f"Failed to send {auto_email_report.name} Auto Email Report") + + +def send_monthly(): + """Check reports to be sent monthly""" + for report in xhiveframework.get_all("Auto Email Report", {"enabled": 1, "frequency": "Monthly"}): + xhiveframework.get_doc("Auto Email Report", report.name).send() + + +def make_links(columns, data): + for row in data: + doc_name = row.get("name") + for col in columns: + if not row.get(col.fieldname): + continue + + if col.fieldtype == "Link": + if col.options and col.options != "Currency": + row[col.fieldname] = get_link_to_form(col.options, row[col.fieldname]) + elif col.fieldtype == "Dynamic Link": + if col.options and row.get(col.options): + row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname]) + elif col.fieldtype == "Currency": + doc = None + if doc_name and col.get("parent") and not xhiveframework.get_meta(col.parent).istable: + doc = xhiveframework.get_doc(col.parent, doc_name) + # Pass the Document to get the currency based on docfield option + row[col.fieldname] = xhiveframework.format_value(row[col.fieldname], col, doc=doc) + return columns, data + + +def update_field_types(columns): + for col in columns: + if col.fieldtype in ("Link", "Dynamic Link", "Currency") and col.options != "Currency": + col.fieldtype = "Data" + col.options = "" + return columns + + +DATE_FORMAT = "%Y-%m-%d" + + +def get_half_year_start(as_str=False): + """ + Returns the first day of the current half-year based on the current date. + """ + today_date = getdate(today()) + + half_year = 1 if today_date.month <= 6 else 2 + + year = today_date.year if half_year == 1 else today_date.year + 1 + month = 1 if half_year == 1 else 7 + day = 1 + + result_date = datetime.date(year, month, day) + + return result_date if not as_str else result_date.strftime(DATE_FORMAT) diff --git a/xhiveframework/email/doctype/auto_email_report/test_auto_email_report.py b/xhiveframework/email/doctype/auto_email_report/test_auto_email_report.py new file mode 100644 index 0000000..69dfe99 --- /dev/null +++ b/xhiveframework/email/doctype/auto_email_report/test_auto_email_report.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import json + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import add_to_date, get_link_to_form, today +from xhiveframework.utils.data import is_html + +# test_records = xhiveframework.get_test_records('Auto Email Report') + + +class TestAutoEmailReport(XhiveFrameworkTestCase): + def test_auto_email(self): + xhiveframework.delete_doc("Auto Email Report", "Permitted Documents For User") + + auto_email_report = get_auto_email_report() + + data = auto_email_report.get_report_content() + + self.assertTrue(is_html(data)) + self.assertTrue(str(get_link_to_form("Module Def", "Core")) in data) + + auto_email_report.format = "CSV" + + data = auto_email_report.get_report_content() + self.assertTrue('"Language","Core"' in data) + + auto_email_report.format = "XLSX" + + data = auto_email_report.get_report_content() + + def test_dynamic_date_filters(self): + auto_email_report = get_auto_email_report() + + auto_email_report.dynamic_date_period = "Weekly" + auto_email_report.from_date_field = "from_date" + auto_email_report.to_date_field = "to_date" + + auto_email_report.prepare_dynamic_filters() + + self.assertEqual(auto_email_report.filters["from_date"], add_to_date(today(), weeks=-1)) + self.assertEqual(auto_email_report.filters["to_date"], today()) + + +def get_auto_email_report(): + if not xhiveframework.db.exists("Auto Email Report", "Permitted Documents For User"): + auto_email_report = xhiveframework.get_doc( + dict( + doctype="Auto Email Report", + report="Permitted Documents For User", + report_type="Script Report", + user="Administrator", + enabled=1, + email_to="test@example.com", + format="HTML", + frequency="Daily", + filters=json.dumps(dict(user="Administrator", doctype="DocType")), + ) + ).insert() + else: + auto_email_report = xhiveframework.get_doc("Auto Email Report", "Permitted Documents For User") + + return auto_email_report diff --git a/xhiveframework/email/doctype/document_follow/__init__.py b/xhiveframework/email/doctype/document_follow/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/document_follow/document_follow.js b/xhiveframework/email/doctype/document_follow/document_follow.js new file mode 100644 index 0000000..b5f235c --- /dev/null +++ b/xhiveframework/email/doctype/document_follow/document_follow.js @@ -0,0 +1,4 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Document Follow", {}); diff --git a/xhiveframework/email/doctype/document_follow/document_follow.json b/xhiveframework/email/doctype/document_follow/document_follow.json new file mode 100644 index 0000000..5dec268 --- /dev/null +++ b/xhiveframework/email/doctype/document_follow/document_follow.json @@ -0,0 +1,79 @@ +{ + "actions": [], + "creation": "2019-01-09 16:39:23.746535", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ref_doctype", + "ref_docname", + "user" + ], + "fields": [ + { + "fieldname": "ref_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Doctype", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "ref_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Document Name", + "options": "ref_doctype", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "reqd": 1, + "search_index": 1 + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 22:34:53.394652", + "modified_by": "Administrator", + "module": "Email", + "name": "Document Follow", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/document_follow/document_follow.py b/xhiveframework/email/doctype/document_follow/document_follow.py new file mode 100644 index 0000000..7bcfbf2 --- /dev/null +++ b/xhiveframework/email/doctype/document_follow/document_follow.py @@ -0,0 +1,20 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class DocumentFollow(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + ref_docname: DF.DynamicLink + ref_doctype: DF.Link + user: DF.Link + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/document_follow/test_document_follow.py b/xhiveframework/email/doctype/document_follow/test_document_follow.py new file mode 100644 index 0000000..35dad86 --- /dev/null +++ b/xhiveframework/email/doctype/document_follow/test_document_follow.py @@ -0,0 +1,241 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from dataclasses import dataclass + +import xhiveframework +import xhiveframework.desk.form.document_follow as document_follow +from xhiveframework.desk.form.assign_to import add +from xhiveframework.desk.form.document_follow import get_document_followed_by_user +from xhiveframework.desk.form.utils import add_comment +from xhiveframework.desk.like import toggle_like +from xhiveframework.query_builder import DocType +from xhiveframework.query_builder.functions import Cast_ +from xhiveframework.share import add as share +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDocumentFollow(XhiveFrameworkTestCase): + def test_document_follow_version(self): + user = get_user() + event_doc = get_event() + + event_doc.description = "This is a test description for sending mail" + event_doc.save(ignore_version=False) + + document_follow.unfollow_document("Event", event_doc.name, user.name) + doc = document_follow.follow_document("Event", event_doc.name, user.name) + self.assertEqual(doc.user, user.name) + + document_follow.send_hourly_updates() + emails = get_emails(event_doc, "%This is a test description for sending mail%") + self.assertIsNotNone(emails) + + def test_document_follow_comment(self): + user = get_user() + event_doc = get_event() + + add_comment( + event_doc.doctype, event_doc.name, "This is a test comment", "Administrator@example.com", "Bosh" + ) + + document_follow.unfollow_document("Event", event_doc.name, user.name) + doc = document_follow.follow_document("Event", event_doc.name, user.name) + self.assertEqual(doc.user, user.name) + + document_follow.send_hourly_updates() + emails = get_emails(event_doc, "%This is a test comment%") + self.assertIsNotNone(emails) + + def test_follow_limit(self): + user = get_user() + for _ in range(25): + event_doc = get_event() + document_follow.unfollow_document("Event", event_doc.name, user.name) + doc = document_follow.follow_document("Event", event_doc.name, user.name) + self.assertEqual(doc.user, user.name) + self.assertEqual(len(get_document_followed_by_user(user.name)), 20) + + def test_follow_on_create(self): + user = get_user(DocumentFollowConditions(1)) + xhiveframework.set_user(user.name) + event = get_event() + + event.description = "This is a test description for sending mail" + event.save(ignore_version=False) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertTrue(documents_followed) + + def test_do_not_follow_on_create(self): + user = get_user() + xhiveframework.set_user(user.name) + + event = get_event() + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def test_do_not_follow_on_update(self): + user = get_user() + xhiveframework.set_user(user.name) + event = get_event() + + event.description = "This is a test description for sending mail" + event.save(ignore_version=False) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def test_follow_on_comment(self): + user = get_user(DocumentFollowConditions(0, 1)) + xhiveframework.set_user(user.name) + event = get_event() + + add_comment(event.doctype, event.name, "This is a test comment", "Administrator@example.com", "Bosh") + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertTrue(documents_followed) + + def test_do_not_follow_on_comment(self): + user = get_user() + xhiveframework.set_user(user.name) + event = get_event() + + add_comment(event.doctype, event.name, "This is a test comment", "Administrator@example.com", "Bosh") + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def test_follow_on_like(self): + user = get_user(DocumentFollowConditions(0, 0, 1)) + xhiveframework.set_user(user.name) + event = get_event() + + toggle_like(event.doctype, event.name, add="Yes") + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertTrue(documents_followed) + + def test_do_not_follow_on_like(self): + user = get_user() + xhiveframework.set_user(user.name) + event = get_event() + + toggle_like(event.doctype, event.name) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def test_follow_on_assign(self): + user = get_user(DocumentFollowConditions(0, 0, 0, 1)) + event = get_event() + + add({"assign_to": [user.name], "doctype": event.doctype, "name": event.name}) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertTrue(documents_followed) + + def test_do_not_follow_on_assign(self): + user = get_user() + xhiveframework.set_user(user.name) + event = get_event() + + add({"assign_to": [user.name], "doctype": event.doctype, "name": event.name}) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def test_follow_on_share(self): + user = get_user(DocumentFollowConditions(0, 0, 0, 0, 1)) + event = get_event() + + share(user=user.name, doctype=event.doctype, name=event.name) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertTrue(documents_followed) + + def test_do_not_follow_on_share(self): + user = get_user() + event = get_event() + + share(user=user.name, doctype=event.doctype, name=event.name) + + documents_followed = get_events_followed_by_user(event.name, user.name) + self.assertFalse(documents_followed) + + def tearDown(self): + xhiveframework.db.rollback() + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Email Queue Recipient") + xhiveframework.db.delete("Document Follow") + xhiveframework.db.delete("Event") + + +def get_events_followed_by_user(event_name, user_name): + DocumentFollow = DocType("Document Follow") + return ( + xhiveframework.qb.from_(DocumentFollow) + .where(DocumentFollow.ref_doctype == "Event") + .where(DocumentFollow.ref_docname == event_name) + .where(DocumentFollow.user == user_name) + .select(DocumentFollow.name) + ).run() + + +def get_event(): + doc = xhiveframework.get_doc( + { + "doctype": "Event", + "subject": "_Test_Doc_Follow", + "doc.starts_on": xhiveframework.utils.now(), + "doc.ends_on": xhiveframework.utils.add_days(xhiveframework.utils.now(), 5), + "doc.description": "Hello", + } + ) + doc.insert() + return doc + + +def get_user(document_follow=None): + xhiveframework.set_user("Administrator") + if xhiveframework.db.exists("User", "test@docsub.com"): + doc = xhiveframework.delete_doc("User", "test@docsub.com") + doc = xhiveframework.new_doc("User") + doc.email = "test@docsub.com" + doc.first_name = "Test" + doc.last_name = "User" + doc.send_welcome_email = 0 + doc.document_follow_notify = 1 + doc.document_follow_frequency = "Hourly" + doc.__dict__.update(document_follow.__dict__ if document_follow else {}) + doc.insert() + doc.add_roles("System Manager") + return doc + + +def get_emails(event_doc, search_string): + EmailQueue = DocType("Email Queue") + EmailQueueRecipient = DocType("Email Queue Recipient") + + return ( + xhiveframework.qb.from_(EmailQueue) + .join(EmailQueueRecipient) + .on(EmailQueueRecipient.parent == Cast_(EmailQueue.name, "varchar")) + .where( + EmailQueueRecipient.recipient == "test@docsub.com", + ) + .where(EmailQueue.message.like(f"%{event_doc.doctype}%")) + .where(EmailQueue.message.like(f"%{event_doc.name}%")) + .where(EmailQueue.message.like(search_string)) + .select(EmailQueue.message) + .limit(1) + ).run() + + +@dataclass +class DocumentFollowConditions: + follow_created_documents: int = 0 + follow_commented_documents: int = 0 + follow_liked_documents: int = 0 + follow_assigned_documents: int = 0 + follow_shared_documents: int = 0 diff --git a/xhiveframework/email/doctype/email_account/__init__.py b/xhiveframework/email/doctype/email_account/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_account/email_account.js b/xhiveframework/email/doctype/email_account/email_account.js new file mode 100644 index 0000000..6ff676d --- /dev/null +++ b/xhiveframework/email/doctype/email_account/email_account.js @@ -0,0 +1,232 @@ +xhiveframework.email_defaults = { + GMail: { + email_server: "imap.gmail.com", + incoming_port: 993, + use_ssl: 1, + enable_outgoing: 1, + smtp_server: "smtp.gmail.com", + smtp_port: 587, + use_tls: 1, + use_imap: 1, + }, + "Outlook.com": { + email_server: "imap-mail.outlook.com", + use_ssl: 1, + enable_outgoing: 1, + smtp_server: "smtp-mail.outlook.com", + smtp_port: 587, + use_tls: 1, + use_imap: 1, + }, + Sendgrid: { + enable_outgoing: 1, + smtp_server: "smtp.sendgrid.net", + smtp_port: 587, + use_tls: 1, + }, + SparkPost: { + enable_incoming: 0, + enable_outgoing: 1, + smtp_server: "smtp.sparkpostmail.com", + smtp_port: 587, + use_tls: 1, + }, + "Yahoo Mail": { + email_server: "imap.mail.yahoo.com", + use_ssl: 1, + enable_outgoing: 1, + smtp_server: "smtp.mail.yahoo.com", + smtp_port: 587, + use_tls: 1, + use_imap: 1, + }, + "Yandex.Mail": { + email_server: "imap.yandex.com", + use_ssl: 1, + enable_outgoing: 1, + smtp_server: "smtp.yandex.com", + smtp_port: 587, + use_tls: 1, + use_imap: 1, + }, +}; + +xhiveframework.email_defaults_pop = { + GMail: { + email_server: "pop.gmail.com", + }, + "Outlook.com": { + email_server: "pop3-mail.outlook.com", + }, + "Yahoo Mail": { + email_server: "pop.mail.yahoo.com", + }, + "Yandex.Mail": { + email_server: "pop.yandex.com", + }, +}; + +function oauth_access(frm) { + xhiveframework.model.with_doc("Connected App", frm.doc.connected_app, () => { + const connected_app = xhiveframework.get_doc("Connected App", frm.doc.connected_app); + return xhiveframework.call({ + doc: connected_app, + method: "initiate_web_application_flow", + args: { + success_uri: window.location.pathname, + user: frm.doc.connected_user, + }, + callback: function (r) { + window.open(r.message, "_self"); + }, + }); + }); +} + +function set_default_max_attachment_size(frm) { + if (frm.doc.__islocal && !frm.doc["attachment_limit"]) { + xhiveframework.call({ + method: "xhiveframework.core.api.file.get_max_file_size", + callback: function (r) { + if (!r.exc) { + frm.set_value("attachment_limit", Number(r.message) / (1024 * 1024)); + } + }, + }); + } +} + +xhiveframework.ui.form.on("Email Account", { + service: function (frm) { + $.each(xhiveframework.email_defaults[frm.doc.service], function (key, value) { + frm.set_value(key, value); + }); + if (!frm.doc.use_imap) { + $.each(xhiveframework.email_defaults_pop[frm.doc.service], function (key, value) { + frm.set_value(key, value); + }); + } + }, + + use_imap: function (frm) { + if (!frm.doc.use_imap) { + $.each(xhiveframework.email_defaults_pop[frm.doc.service], function (key, value) { + frm.set_value(key, value); + }); + } else { + $.each(xhiveframework.email_defaults[frm.doc.service], function (key, value) { + frm.set_value(key, value); + }); + } + }, + + enable_incoming: function (frm) { + frm.trigger("warn_autoreply_on_incoming"); + }, + + enable_auto_reply: function (frm) { + frm.trigger("warn_autoreply_on_incoming"); + }, + + notify_if_unreplied: function (frm) { + frm.set_df_property("send_notification_to", "reqd", frm.doc.notify_if_unreplied); + }, + + onload: function (frm) { + frm.set_df_property("append_to", "only_select", true); + frm.set_query( + "append_to", + "xhiveframework.email.doctype.email_account.email_account.get_append_to" + ); + frm.set_query("append_to", "imap_folder", function () { + return { + query: "xhiveframework.email.doctype.email_account.email_account.get_append_to", + }; + }); + if (frm.doc.__islocal) { + frm.add_child("imap_folder", { folder_name: "INBOX" }); + frm.refresh_field("imap_folder"); + } + set_default_max_attachment_size(frm); + frm.events.show_oauth_authorization_message(frm); + }, + + refresh: function (frm) { + frm.events.enable_incoming(frm); + frm.events.notify_if_unreplied(frm); + + if (xhiveframework.route_flags.delete_user_from_locals && xhiveframework.route_flags.linked_user) { + delete xhiveframework.route_flags.delete_user_from_locals; + delete locals["User"][xhiveframework.route_flags.linked_user]; + } + }, + + authorize_api_access: function (frm) { + oauth_access(frm); + }, + + show_oauth_authorization_message(frm) { + if (frm.doc.auth_method === "OAuth" && frm.doc.connected_app) { + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.connected_app.connected_app.has_token", + args: { + connected_app: frm.doc.connected_app, + connected_user: frm.doc.connected_user, + }, + callback: (r) => { + if (!r.message) { + let msg = __( + 'OAuth has been enabled but not authorised. Please use "Authorise API Access" button to do the same.' + ); + frm.dashboard.clear_headline(); + frm.dashboard.set_headline_alert(msg, "yellow"); + } + }, + }); + } + }, + + domain: xhiveframework.utils.debounce((frm) => { + if (frm.doc.domain) { + xhiveframework.call({ + method: "get_domain_values", + doc: frm.doc, + args: { + domain: frm.doc.domain, + }, + callback: function (r) { + if (!r.exc) { + for (let field in r.message) { + frm.set_value(field, r.message[field]); + } + } + }, + }); + } + }), + + email_sync_option: function (frm) { + // confirm if the ALL sync option is selected + + if (frm.doc.email_sync_option == "ALL") { + var msg = __( + "You are selecting Sync Option as ALL, It will resync all read as well as unread message from server. This may also cause the duplication of Communication (emails)." + ); + xhiveframework.confirm(msg, null, function () { + frm.set_value("email_sync_option", "UNSEEN"); + }); + } + }, + + warn_autoreply_on_incoming: function (frm) { + if (frm.doc.enable_incoming && frm.doc.enable_auto_reply && frm.doc.__islocal) { + var msg = __( + "Enabling auto reply on an incoming email account will send automated replies to all the synchronized emails. Do you wish to continue?" + ); + xhiveframework.confirm(msg, null, function () { + frm.set_value("enable_auto_reply", 0); + xhiveframework.show_alert({ message: __("Disabled Auto Reply"), indicator: "blue" }); + }); + } + }, +}); diff --git a/xhiveframework/email/doctype/email_account/email_account.json b/xhiveframework/email/doctype/email_account/email_account.json new file mode 100644 index 0000000..44e6ead --- /dev/null +++ b/xhiveframework/email/doctype/email_account/email_account.json @@ -0,0 +1,642 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:email_account_name", + "creation": "2014-09-11 12:04:34.163728", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "account_section", + "email_id", + "email_account_name", + "column_break_3", + "domain", + "service", + "authentication_column", + "auth_method", + "authorize_api_access", + "password", + "awaiting_password", + "ascii_encode_password", + "column_break_10", + "connected_app", + "connected_user", + "login_id_is_different", + "login_id", + "mailbox_settings", + "enable_incoming", + "default_incoming", + "use_imap", + "use_ssl", + "use_starttls", + "email_server", + "incoming_port", + "column_break_18", + "attachment_limit", + "email_sync_option", + "initial_sync_count", + "section_break_25", + "imap_folder", + "section_break_12", + "append_emails_to_sent_folder", + "append_to", + "create_contact", + "enable_automatic_linking", + "section_break_13", + "notify_if_unreplied", + "unreplied_for_mins", + "send_notification_to", + "outgoing_mail_settings", + "enable_outgoing", + "use_tls", + "use_ssl_for_outgoing", + "smtp_server", + "smtp_port", + "column_break_38", + "default_outgoing", + "always_use_account_email_id_as_sender", + "always_use_account_name_as_sender_name", + "send_unsubscribe_message", + "track_email_status", + "no_smtp_authentication", + "signature_section", + "add_signature", + "signature", + "auto_reply", + "enable_auto_reply", + "auto_reply_message", + "set_footer", + "footer", + "brand_logo", + "uidvalidity", + "uidnext", + "no_failed" + ], + "fields": [ + { + "fieldname": "email_id", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "in_global_search": 1, + "in_list_view": 1, + "label": "Email Address", + "options": "Email", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "login_id_is_different", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use different Email ID" + }, + { + "depends_on": "login_id_is_different", + "fieldname": "login_id", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Alternative Email ID" + }, + { + "depends_on": "eval: doc.auth_method === \"Basic\"", + "fieldname": "password", + "fieldtype": "Password", + "hide_days": 1, + "hide_seconds": 1, + "label": "Password" + }, + { + "default": "0", + "depends_on": "eval: doc.auth_method === \"Basic\"", + "fieldname": "awaiting_password", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Awaiting password" + }, + { + "default": "0", + "depends_on": "eval: doc.auth_method === \"Basic\"", + "fieldname": "ascii_encode_password", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use ASCII encoding for password" + }, + { + "description": "e.g. \"Support\", \"Sales\", \"Jerry Yang\"", + "fieldname": "email_account_name", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Email Account Name", + "unique": 1 + }, + { + "depends_on": "eval:!doc.service", + "fieldname": "domain", + "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Domain", + "options": "Email Domain" + }, + { + "depends_on": "eval:!doc.domain", + "fieldname": "service", + "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, + "label": "Service", + "options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail" + }, + { + "fieldname": "mailbox_settings", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Incoming (POP/IMAP) Settings" + }, + { + "default": "0", + "fieldname": "enable_incoming", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Enable Incoming" + }, + { + "default": "0", + "depends_on": "eval: !doc.domain && doc.enable_incoming", + "fetch_from": "domain.use_imap", + "fieldname": "use_imap", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use IMAP" + }, + { + "depends_on": "eval:!doc.domain && doc.enable_incoming", + "description": "e.g. pop.gmail.com / imap.gmail.com", + "fetch_from": "domain.email_server", + "fieldname": "email_server", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Incoming Server" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_incoming", + "fetch_from": "domain.use_ssl", + "fieldname": "use_ssl", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use SSL" + }, + { + "depends_on": "eval:!doc.domain && doc.enable_incoming", + "description": "Ignore attachments over this size", + "fetch_from": "domain.attachment_limit", + "fieldname": "attachment_limit", + "fieldtype": "Int", + "hide_days": 1, + "hide_seconds": 1, + "label": "Attachment Limit (MB)" + }, + { + "depends_on": "eval: doc.enable_incoming && !doc.use_imap", + "description": "Append as communication against this DocType (must have fields: \"Sender\" and \"Subject\"). These fields can be defined in the email settings section of the appended doctype.", + "fieldname": "append_to", + "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, + "in_standard_filter": 1, + "label": "Append To", + "options": "DocType" + }, + { + "default": "0", + "depends_on": "enable_incoming", + "description": "e.g. replies@yourcomany.com. All replies will come to this inbox.", + "fieldname": "default_incoming", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Default Incoming" + }, + { + "default": "UNSEEN", + "depends_on": "eval: doc.enable_incoming && doc.use_imap", + "fieldname": "email_sync_option", + "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, + "label": "Email Sync Option", + "options": "ALL\nUNSEEN" + }, + { + "default": "250", + "depends_on": "eval: doc.enable_incoming && doc.use_imap", + "description": "Total number of emails to sync in initial sync process ", + "fieldname": "initial_sync_count", + "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, + "label": "Initial Sync Count", + "options": "100\n250\n500" + }, + { + "depends_on": "enable_incoming", + "fieldname": "section_break_13", + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 + }, + { + "default": "0", + "fieldname": "notify_if_unreplied", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Notify if unreplied" + }, + { + "default": "30", + "depends_on": "notify_if_unreplied", + "fieldname": "unreplied_for_mins", + "fieldtype": "Int", + "hide_days": 1, + "hide_seconds": 1, + "label": "Notify if unreplied for (in mins)" + }, + { + "depends_on": "notify_if_unreplied", + "description": "Email Addresses", + "fieldname": "send_notification_to", + "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, + "label": "Send Notification to" + }, + { + "fieldname": "outgoing_mail_settings", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Outgoing (SMTP) Settings" + }, + { + "default": "0", + "description": "SMTP Settings for outgoing emails", + "fieldname": "enable_outgoing", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Enable Outgoing" + }, + { + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "description": "e.g. smtp.gmail.com", + "fetch_from": "domain.smtp_server", + "fieldname": "smtp_server", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Outgoing Server" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "fetch_from": "domain.use_tls", + "fieldname": "use_tls", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use TLS" + }, + { + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "description": "If non standard port (e.g. 587). If on Google Cloud, try port 2525.", + "fetch_from": "domain.smtp_port", + "fieldname": "smtp_port", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Port" + }, + { + "default": "0", + "depends_on": "enable_outgoing", + "description": "Notifications and bulk mails will be sent from this outgoing server.", + "fieldname": "default_outgoing", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Default Outgoing" + }, + { + "default": "0", + "depends_on": "enable_outgoing", + "fieldname": "always_use_account_email_id_as_sender", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Always use this email address as sender address" + }, + { + "default": "0", + "depends_on": "enable_outgoing", + "fieldname": "always_use_account_name_as_sender_name", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Always use this name as sender name" + }, + { + "default": "1", + "fieldname": "send_unsubscribe_message", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Send unsubscribe message in email" + }, + { + "default": "1", + "description": "Track if your email has been opened by the recipient.\n
      \nNote: If you're sending to multiple recipients, even if 1 recipient reads the email, it'll be considered \"Opened\"", + "fieldname": "track_email_status", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Track Email Status" + }, + { + "default": "0", + "fieldname": "no_smtp_authentication", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Disable SMTP server authentication" + }, + { + "collapsible": 1, + "collapsible_depends_on": "add_signature", + "fieldname": "signature_section", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Signature" + }, + { + "default": "0", + "fieldname": "add_signature", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Add Signature" + }, + { + "depends_on": "add_signature", + "fieldname": "signature", + "fieldtype": "Text Editor", + "hide_days": 1, + "hide_seconds": 1, + "label": "Signature" + }, + { + "collapsible": 1, + "collapsible_depends_on": "enable_auto_reply", + "fieldname": "auto_reply", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Auto Reply" + }, + { + "default": "0", + "fieldname": "enable_auto_reply", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Enable Auto Reply" + }, + { + "depends_on": "enable_auto_reply", + "description": "ProTip: Add Reference: {{ reference_doctype }} {{ reference_name }} to send document reference", + "fieldname": "auto_reply_message", + "fieldtype": "Text Editor", + "hide_days": 1, + "hide_seconds": 1, + "label": "Auto Reply Message" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:xhiveframework.utils.html2text(doc.footer || '')!=''", + "fieldname": "set_footer", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Footer" + }, + { + "fieldname": "footer", + "fieldtype": "Text Editor", + "hide_days": 1, + "hide_seconds": 1, + "label": "Footer Content" + }, + { + "fieldname": "uidvalidity", + "fieldtype": "Data", + "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, + "label": "UIDVALIDITY", + "no_copy": 1 + }, + { + "fieldname": "uidnext", + "fieldtype": "Int", + "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, + "label": "UIDNEXT", + "no_copy": 1 + }, + { + "fieldname": "no_failed", + "fieldtype": "Int", + "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, + "label": "no failed attempts", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_12", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Document Linking" + }, + { + "default": "0", + "description": "For more information, click here.", + "fieldname": "enable_automatic_linking", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Enable Automatic Linking in Documents" + }, + { + "depends_on": "eval:!doc.domain && doc.enable_incoming", + "description": "If non-standard port (e.g. POP3: 995/110, IMAP: 993/143)", + "fieldname": "incoming_port", + "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, + "label": "Port" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_outgoing && doc.enable_incoming && doc.use_imap", + "fieldname": "append_emails_to_sent_folder", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Append Emails to Sent Folder" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_outgoing", + "fieldname": "use_ssl_for_outgoing", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Use SSL" + }, + { + "default": "1", + "fieldname": "create_contact", + "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, + "label": "Create Contacts from Incoming Emails" + }, + { + "fieldname": "brand_logo", + "fieldtype": "Attach Image", + "label": "Brand Logo" + }, + { + "fieldname": "authentication_column", + "fieldtype": "Section Break", + "label": "Authentication" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "account_section", + "fieldtype": "Section Break", + "label": "Account" + }, + { + "depends_on": "eval: doc.use_imap && doc.enable_incoming", + "fieldname": "imap_folder", + "fieldtype": "Table", + "label": "IMAP Folder", + "options": "IMAP Folder" + }, + { + "fieldname": "section_break_25", + "fieldtype": "Section Break", + "label": "IMAP Details" + }, + { + "depends_on": "eval: doc.auth_method === \"OAuth\" && !doc.__islocal && !doc.__unsaved", + "fieldname": "authorize_api_access", + "fieldtype": "Button", + "label": "Authorize API Access" + }, + { + "default": "Basic", + "fieldname": "auth_method", + "fieldtype": "Select", + "label": "Method", + "options": "Basic\nOAuth" + }, + { + "default": "0", + "depends_on": "eval:!doc.domain && doc.enable_incoming && doc.use_imap && !doc.use_ssl", + "fetch_from": "domain.use_starttls", + "fieldname": "use_starttls", + "fieldtype": "Check", + "label": "Use STARTTLS" + }, + { + "depends_on": "eval: doc.auth_method === \"OAuth\"", + "fieldname": "connected_app", + "fieldtype": "Link", + "label": "Connected App", + "mandatory_depends_on": "eval: doc.auth_method === \"OAuth\"", + "options": "Connected App" + }, + { + "depends_on": "eval: doc.auth_method === \"OAuth\"", + "fieldname": "connected_user", + "fieldtype": "Link", + "label": "Connected User", + "mandatory_depends_on": "eval: doc.auth_method === \"OAuth\"", + "options": "User" + } + ], + "icon": "fa fa-inbox", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-06-05 15:03:08.538819", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Account", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "read": 1, + "role": "Inbox User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/email/doctype/email_account/email_account.py b/xhiveframework/email/doctype/email_account/email_account.py new file mode 100755 index 0000000..cb83e18 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/email_account.py @@ -0,0 +1,952 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import email.utils +import functools +import imaplib +import time +from datetime import datetime, timedelta +from poplib import error_proto + +import xhiveframework +from xhiveframework import _, are_emails_muted, safe_encode +from xhiveframework.desk.form import assign_to +from xhiveframework.email.doctype.email_domain.email_domain import EMAIL_DOMAIN_FIELDS +from xhiveframework.email.receive import EmailServer, InboundMail, SentEmailInInboxError +from xhiveframework.email.smtp import SMTPServer +from xhiveframework.email.utils import get_port +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, comma_or, cstr, parse_addr, validate_email_address +from xhiveframework.utils.background_jobs import enqueue, get_jobs +from xhiveframework.utils.jinja import render_template +from xhiveframework.utils.user import get_system_managers + + +class SentEmailInInbox(Exception): + pass + + +def cache_email_account(cache_name): + def decorator_cache_email_account(func): + @functools.wraps(func) + def wrapper_cache_email_account(*args, **kwargs): + if not hasattr(xhiveframework.local, cache_name): + setattr(xhiveframework.local, cache_name, {}) + + cached_accounts = getattr(xhiveframework.local, cache_name) + match_by = [*list(kwargs.values()), "default"] + matched_accounts = list(filter(None, [cached_accounts.get(key) for key in match_by])) + if matched_accounts: + return matched_accounts[0] + + matched_accounts = func(*args, **kwargs) + cached_accounts.update(matched_accounts or {}) + return matched_accounts and next(iter(matched_accounts.values())) + + return wrapper_cache_email_account + + return decorator_cache_email_account + + +class EmailAccount(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.email.doctype.imap_folder.imap_folder import IMAPFolder + from xhiveframework.types import DF + + add_signature: DF.Check + always_use_account_email_id_as_sender: DF.Check + always_use_account_name_as_sender_name: DF.Check + append_emails_to_sent_folder: DF.Check + append_to: DF.Link | None + ascii_encode_password: DF.Check + attachment_limit: DF.Int + auth_method: DF.Literal["Basic", "OAuth"] + auto_reply_message: DF.TextEditor | None + awaiting_password: DF.Check + brand_logo: DF.AttachImage | None + connected_app: DF.Link | None + connected_user: DF.Link | None + create_contact: DF.Check + default_incoming: DF.Check + default_outgoing: DF.Check + domain: DF.Link | None + email_account_name: DF.Data | None + email_id: DF.Data + email_server: DF.Data | None + email_sync_option: DF.Literal["ALL", "UNSEEN"] + enable_auto_reply: DF.Check + enable_automatic_linking: DF.Check + enable_incoming: DF.Check + enable_outgoing: DF.Check + footer: DF.TextEditor | None + imap_folder: DF.Table[IMAPFolder] + incoming_port: DF.Data | None + initial_sync_count: DF.Literal["100", "250", "500"] + login_id: DF.Data | None + login_id_is_different: DF.Check + no_failed: DF.Int + no_smtp_authentication: DF.Check + notify_if_unreplied: DF.Check + password: DF.Password | None + send_notification_to: DF.SmallText | None + send_unsubscribe_message: DF.Check + service: DF.Literal[ + "", + "GMail", + "Sendgrid", + "SparkPost", + "Yahoo Mail", + "Outlook.com", + "Yandex.Mail", + ] + signature: DF.TextEditor | None + smtp_port: DF.Data | None + smtp_server: DF.Data | None + track_email_status: DF.Check + uidnext: DF.Int + uidvalidity: DF.Data | None + unreplied_for_mins: DF.Int + use_imap: DF.Check + use_ssl: DF.Check + use_ssl_for_outgoing: DF.Check + use_starttls: DF.Check + use_tls: DF.Check + # end: auto-generated types + DOCTYPE = "Email Account" + + def autoname(self): + """Set name as `email_account_name` or make title from Email Address.""" + if not self.email_account_name: + self.email_account_name = ( + self.email_id.split("@", 1)[0].replace("_", " ").replace(".", " ").replace("-", " ").title() + ) + + self.name = self.email_account_name + + def validate(self): + """Validate Email Address and check POP3/IMAP and SMTP connections is enabled.""" + + if self.email_id: + validate_email_address(self.email_id, True) + + if self.login_id_is_different: + if not self.login_id: + xhiveframework.throw(_("Login Id is required")) + else: + self.login_id = None + + # validate the imap settings + if self.enable_incoming and self.use_imap and len(self.imap_folder) <= 0: + xhiveframework.throw(_("You need to set one IMAP folder for {0}").format(xhiveframework.bold(self.email_id))) + + if xhiveframework.local.flags.in_patch or xhiveframework.local.flags.in_test: + return + + use_oauth = self.auth_method == "OAuth" + validate_oauth = use_oauth and not (self.is_new() and not self.get_oauth_token()) + self.use_starttls = cint(self.use_imap and self.use_starttls and not self.use_ssl) + + if use_oauth: + # no need for awaiting password for oauth + self.awaiting_password = 0 + self.password = None + + if not xhiveframework.local.flags.in_install and not self.awaiting_password: + if validate_oauth or self.password or self.smtp_server in ("127.0.0.1", "localhost"): + if self.enable_incoming: + self.get_incoming_server() + self.no_failed = 0 + + if self.enable_outgoing: + self.validate_smtp_conn() + else: + if self.enable_incoming or (self.enable_outgoing and not self.no_smtp_authentication): + if not use_oauth: + xhiveframework.throw(_("Password is required or select Awaiting Password")) + + if self.notify_if_unreplied: + if not self.send_notification_to: + xhiveframework.throw(_("{0} is mandatory").format(self.meta.get_label("send_notification_to"))) + for e in self.get_unreplied_notification_emails(): + validate_email_address(e, True) + + if self.enable_incoming: + for folder in self.imap_folder: + if folder.append_to: + valid_doctypes = [d[0] for d in get_append_to()] + if folder.append_to not in valid_doctypes: + xhiveframework.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes))) + + def validate_smtp_conn(self): + if not self.smtp_server: + xhiveframework.throw(_("SMTP Server is required")) + + server = self.get_smtp_server() + return server.session + + def before_save(self): + messages = [] + as_list = 1 + if not self.enable_incoming and self.default_incoming: + self.default_incoming = False + messages.append( + _("{} has been disabled. It can only be enabled if {} is checked.").format( + xhiveframework.bold(_("Default Incoming")), + xhiveframework.bold(_("Enable Incoming")), + ) + ) + if not self.enable_outgoing and self.default_outgoing: + self.default_outgoing = False + messages.append( + _("{} has been disabled. It can only be enabled if {} is checked.").format( + xhiveframework.bold(_("Default Outgoing")), + xhiveframework.bold(_("Enable Outgoing")), + ) + ) + if messages: + if len(messages) == 1: + (as_list, messages) = (0, messages[0]) + xhiveframework.msgprint( + messages, + as_list=as_list, + indicator="orange", + title=_("Defaults Updated"), + ) + + def on_update(self): + """Check there is only one default of each type.""" + self.check_automatic_linking_email_account() + self.there_must_be_only_one_default() + setup_user_email_inbox( + email_account=self.name, + awaiting_password=self.awaiting_password, + email_id=self.email_id, + enable_outgoing=self.enable_outgoing, + used_oauth=self.auth_method == "OAuth", + ) + + def there_must_be_only_one_default(self): + """If current Email Account is default, un-default all other accounts.""" + for field in ("default_incoming", "default_outgoing"): + if not self.get(field): + continue + + for email_account in xhiveframework.get_all("Email Account", filters={field: 1}): + if email_account.name == self.name: + continue + + email_account = xhiveframework.get_doc("Email Account", email_account.name) + email_account.set(field, 0) + email_account.save() + + @xhiveframework.whitelist() + def get_domain_values(self, domain: str): + return xhiveframework.db.get_value("Email Domain", domain, EMAIL_DOMAIN_FIELDS, as_dict=True) + + def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"): + """Returns logged in POP3/IMAP connection object.""" + oauth_token = self.get_oauth_token() + args = xhiveframework._dict( + { + "email_account_name": self.email_account_name, + "email_account": self.name, + "host": self.email_server, + "use_ssl": self.use_ssl, + "use_starttls": self.use_starttls, + "username": getattr(self, "login_id", None) or self.email_id, + "use_imap": self.use_imap, + "email_sync_rule": email_sync_rule, + "incoming_port": get_port(self), + "initial_sync_count": self.initial_sync_count or 100, + "use_oauth": self.auth_method == "OAuth", + "access_token": oauth_token.get_password("access_token") if oauth_token else None, + } + ) + + if self.password: + args.password = self.get_password() + + if not args.get("host"): + xhiveframework.throw(_("{0} is required").format("Email Server")) + + email_server = EmailServer(xhiveframework._dict(args)) + self.check_email_server_connection(email_server, in_receive) + + if not in_receive and self.use_imap: + email_server.imap.logout() + + return email_server + + def check_email_server_connection(self, email_server, in_receive): + # tries to connect to email server and handles failure + try: + email_server.connect() + + # reset failed attempts count - do it after succesful connection + self.set_failed_attempts_count(0) + except (error_proto, imaplib.IMAP4.error) as e: + message = cstr(e).lower().replace(" ", "") + auth_error_codes = [ + "authenticationfailed", + "loginfailed", + ] + + other_error_codes = [ + "err[auth]", + "errtemporaryerror", + "loginviayourwebbrowser", + ] + + all_error_codes = auth_error_codes + other_error_codes + + if in_receive and any(map(lambda t: t in message, all_error_codes)): + # if called via self.receive and it leads to authentication error, + # disable incoming and send email to System Manager + error_message = _( + "Authentication failed while receiving emails from Email Account: {0}." + ).format(self.name) + + error_message = _("Email Account Disabled.") + " " + error_message + error_message += "
      " + _("Message from server: {0}").format(cstr(e)) + self.handle_incoming_connect_error(description=error_message) + return None + + elif not in_receive and any(map(lambda t: t in message, auth_error_codes)): + SMTPServer.throw_invalid_credentials_exception() + else: + xhiveframework.throw(cstr(e)) + + except OSError: + if in_receive: + # timeout while connecting, see receive.py connect method + description = xhiveframework.message_log.pop() if xhiveframework.message_log else "Socket Error" + self.db_set("no_failed", self.no_failed + 1) + if self.no_failed > 2: + self.handle_incoming_connect_error(description=description) + return + + raise + + @property + def _password(self): + raise_exception = not ( + self.auth_method == "OAuth" or self.no_smtp_authentication or xhiveframework.flags.in_test + ) + return self.get_password(raise_exception=raise_exception) + + @property + def default_sender(self): + return email.utils.formataddr((self.name, self.get("email_id"))) + + def is_exists_in_db(self): + """Some of the Email Accounts we create from configs and those doesn't exists in DB. + This is is to check the specific email account exists in DB or not. + """ + return self.find_one_by_filters(name=self.name) + + @classmethod + def from_record(cls, record): + email_account = xhiveframework.new_doc(cls.DOCTYPE) + email_account.update(record) + return email_account + + @classmethod + def find(cls, name): + return xhiveframework.get_doc(cls.DOCTYPE, name) + + @classmethod + def find_one_by_filters(cls, **kwargs) -> "EmailAccount": + name = xhiveframework.db.get_value(cls.DOCTYPE, kwargs) + return cls.find(name) if name else None + + @classmethod + def find_from_config(cls): + config = cls.get_account_details_from_site_config() + if config: + account = cls.from_record(config) + account._from_site_config = True + return account + + @classmethod + def create_dummy(cls): + return cls.from_record({"sender": "notifications@example.com"}) + + @classmethod + @cache_email_account("outgoing_email_account") + def find_outgoing(cls, match_by_email=None, match_by_doctype=None, _raise_error=False): + """Find the outgoing Email account to use. + + :param match_by_email: Find account using emailID + :param match_by_doctype: Find account by matching `Append To` doctype + :param _raise_error: This is used by raise_error_on_no_output decorator to raise error. + """ + if match_by_email: + match_by_email = parse_addr(match_by_email)[1] + doc = cls.find_one_by_filters(enable_outgoing=1, email_id=match_by_email) + if doc: + return {match_by_email: doc} + + if match_by_doctype: + doc = cls.find_one_by_filters(enable_outgoing=1, enable_incoming=1, append_to=match_by_doctype) + if doc: + return {match_by_doctype: doc} + + doc = cls.find_default_outgoing() + if doc: + return {"default": doc} + + if _raise_error: + xhiveframework.throw( + _("Please setup default Email Account from Settings > Email Account"), + xhiveframework.OutgoingEmailError, + ) + + @classmethod + def find_default_outgoing(cls): + """Find default outgoing account.""" + doc = cls.find_one_by_filters(enable_outgoing=1, default_outgoing=1) + doc = doc or cls.find_from_config() + return doc or (are_emails_muted() and cls.create_dummy()) + + @classmethod + def find_incoming(cls, match_by_email=None, match_by_doctype=None): + """Find the incoming Email account to use. + :param match_by_email: Find account using emailID + :param match_by_doctype: Find account by matching `Append To` doctype + """ + doc = cls.find_one_by_filters(enable_incoming=1, email_id=match_by_email) + if doc: + return doc + + doc = cls.find_one_by_filters(enable_incoming=1, append_to=match_by_doctype) + if doc: + return doc + + doc = cls.find_default_incoming() + return doc + + @classmethod + def find_default_incoming(cls): + return cls.find_one_by_filters(enable_incoming=1, default_incoming=1) + + @classmethod + def get_account_details_from_site_config(cls): + if not xhiveframework.conf.get("mail_server"): + return {} + + field_to_conf_name_map = { + "smtp_server": {"conf_names": ("mail_server",)}, + "smtp_port": {"conf_names": ("mail_port",)}, + "use_tls": {"conf_names": ("use_tls", "mail_login")}, + "login_id": {"conf_names": ("mail_login",)}, + "email_id": { + "conf_names": ("auto_email_id", "mail_login"), + "default": "notifications@example.com", + }, + "password": {"conf_names": ("mail_password",)}, + "always_use_account_email_id_as_sender": { + "conf_names": ("always_use_account_email_id_as_sender",), + "default": 0, + }, + "always_use_account_name_as_sender_name": { + "conf_names": ("always_use_account_name_as_sender_name",), + "default": 0, + }, + "name": {"conf_names": ("email_sender_name",), "default": "XhiveFramework"}, + "auth_method": {"conf_names": ("auth_method"), "default": "Basic"}, + "from_site_config": {"default": True}, + "no_smtp_authentication": { + "conf_names": ("disable_mail_smtp_authentication",), + "default": 0, + }, + } + + account_details = {} + for doc_field_name, d in field_to_conf_name_map.items(): + conf_names, default = d.get("conf_names") or [], d.get("default") + value = [xhiveframework.conf.get(k) for k in conf_names if xhiveframework.conf.get(k)] + account_details[doc_field_name] = (value and value[0]) or default + + return account_details + + def sendmail_config(self): + oauth_token = self.get_oauth_token() + + return { + "email_account": self.name, + "server": self.smtp_server, + "port": cint(self.smtp_port), + "login": getattr(self, "login_id", None) or self.email_id, + "password": self._password, + "use_ssl": cint(self.use_ssl_for_outgoing), + "use_tls": cint(self.use_tls), + "use_oauth": self.auth_method == "OAuth", + "access_token": oauth_token.get_password("access_token") if oauth_token else None, + } + + def get_smtp_server(self): + """Get SMTPServer (wrapper around actual smtplib object) for this account. + + Implementation Detail: Since SMTPServer is same for each email connection, the same *instance* + is returned every time this function is called from same EmailAccount object. + This enables reusabilty of connection for better performance.""" + return self._smtp_server_instance + + @functools.cached_property + def _smtp_server_instance(self): + config = self.sendmail_config() + return SMTPServer(**config) + + def remove_unpicklable_values(self, state): + super().remove_unpicklable_values(state) + state.pop("_smtp_server_instance", None) + + def handle_incoming_connect_error(self, description): + if self.get_failed_attempts_count() > 5: + # This is done in background to avoid committing here. + xhiveframework.enqueue(self._disable_broken_incoming_account, description=description) + else: + self.set_failed_attempts_count(self.get_failed_attempts_count() + 1) + + def _disable_broken_incoming_account(self, description): + if xhiveframework.flags.in_test: + return + self.db_set("enable_incoming", 0) + + for user in get_system_managers(only_name=True): + try: + assign_to.add( + { + "assign_to": [user], + "doctype": self.doctype, + "name": self.name, + "description": description, + "priority": "High", + "notify": 1, + } + ) + except assign_to.DuplicateToDoError: + pass + + def set_failed_attempts_count(self, value): + xhiveframework.cache.set_value(f"{self.name}:email-account-failed-attempts", value) + + def get_failed_attempts_count(self): + return cint(xhiveframework.cache.get_value(f"{self.name}:email-account-failed-attempts")) + + def receive(self): + """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" + exceptions = [] + inbound_mails = self.get_inbound_mails() + for mail in inbound_mails: + try: + communication = mail.process() + xhiveframework.db.commit() + # If email already exists in the system + # then do not send notifications for the same email. + if communication and mail.flags.is_new_communication: + # notify all participants of this thread + if self.enable_auto_reply: + self.send_auto_reply(communication, mail) + + communication.send_email(is_inbound_mail_communcation=True) + except SentEmailInInboxError: + xhiveframework.db.rollback() + except Exception: + xhiveframework.db.rollback() + self.log_error(title="EmailAccount.receive") + if self.use_imap: + self.handle_bad_emails(mail.uid, mail.raw_message, xhiveframework.get_traceback()) + exceptions.append(xhiveframework.get_traceback()) + else: + xhiveframework.db.commit() + + if exceptions: + raise Exception(xhiveframework.as_json(exceptions)) + + def get_inbound_mails(self) -> list[InboundMail]: + """retrive and return inbound mails.""" + mails = [] + + def process_mail(messages, append_to=None): + for index, message in enumerate(messages.get("latest_messages", [])): + uid = messages["uid_list"][index] if messages.get("uid_list") else None + seen_status = messages.get("seen_status", {}).get(uid) + if self.email_sync_option != "UNSEEN" or seen_status != "SEEN": + # only append the emails with status != 'SEEN' if sync option is set to 'UNSEEN' + mails.append( + InboundMail( + message, + self, + xhiveframework.safe_decode(uid), + seen_status, + append_to, + ) + ) + + if not self.enable_incoming: + return [] + + email_sync_rule = self.build_email_sync_rule() + try: + email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) + if self.use_imap: + # process all given imap folder + for folder in self.imap_folder: + if email_server.select_imap_folder(folder.folder_name): + email_server.settings["uid_validity"] = folder.uidvalidity + messages = email_server.get_messages(folder=f'"{folder.folder_name}"') or {} + process_mail(messages, folder.append_to) + else: + # process the pop3 account + messages = email_server.get_messages() or {} + process_mail(messages) + # close connection to mailserver + email_server.logout() + except Exception: + self.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + return [] + return mails + + def handle_bad_emails(self, uid, raw, reason): + if cint(self.use_imap): + import email + + try: + if isinstance(raw, bytes): + mail = email.message_from_bytes(raw) + else: + mail = email.message_from_string(raw) + + message_id = mail.get("Message-ID") + except Exception: + message_id = "can't be parsed" + + unhandled_email = xhiveframework.get_doc( + { + "raw": raw, + "uid": uid, + "reason": reason, + "message_id": message_id, + "doctype": "Unhandled Email", + "email_account": self.name, + } + ) + unhandled_email.insert(ignore_permissions=True) + xhiveframework.db.commit() + + def send_auto_reply(self, communication, email): + """Send auto reply if set.""" + from xhiveframework.core.doctype.communication.email import ( + set_incoming_outgoing_accounts, + ) + + if self.enable_auto_reply: + set_incoming_outgoing_accounts(communication) + + unsubscribe_message = (self.send_unsubscribe_message and _("Leave this conversation")) or "" + + xhiveframework.sendmail( + recipients=[email.from_email], + sender=self.email_id, + reply_to=communication.incoming_email_account, + subject=" ".join([_("Re:"), communication.subject]), + content=render_template(self.auto_reply_message or "", communication.as_dict()) + or xhiveframework.get_template("templates/emails/auto_reply.html").render(communication.as_dict()), + reference_doctype=communication.reference_doctype, + reference_name=communication.reference_name, + in_reply_to=email.mail.get("Message-Id"), # send back the Message-Id as In-Reply-To + unsubscribe_message=unsubscribe_message, + ) + + def get_unreplied_notification_emails(self): + """Return list of emails listed""" + self.send_notification_to = self.send_notification_to.replace(",", "\n") + return [e.strip() for e in self.send_notification_to.split("\n") if e.strip()] + + def on_trash(self): + """Clear communications where email account is linked""" + Communication = xhiveframework.qb.DocType("Communication") + xhiveframework.qb.update(Communication).set(Communication.email_account, "").where( + Communication.email_account == self.name + ).run() + + remove_user_email_inbox(email_account=self.name) + + def after_rename(self, old, new, merge=False): + xhiveframework.db.set_value("Email Account", new, "email_account_name", new) + + def build_email_sync_rule(self): + if not self.use_imap: + return "UNSEEN" + + if self.email_sync_option == "ALL": + max_uid = get_max_email_uid(self.name) + last_uid = max_uid + int(self.initial_sync_count or 100) if max_uid == 1 else "*" + return f"UID {max_uid}:{last_uid}" + else: + return self.email_sync_option or "UNSEEN" + + def check_automatic_linking_email_account(self): + if self.enable_automatic_linking: + if not self.enable_incoming: + xhiveframework.throw(_("Automatic Linking can be activated only if Incoming is enabled.")) + + if xhiveframework.db.exists( + "Email Account", + {"enable_automatic_linking": 1, "name": ("!=", self.name)}, + ): + xhiveframework.throw(_("Automatic Linking can be activated only for one Email Account.")) + + def append_email_to_sent_folder(self, message): + if not (self.enable_incoming and self.use_imap): + # don't try appending if enable incoming and imap is not set + return + + try: + email_server = self.get_incoming_server(in_receive=True) + message = safe_encode(message) + email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message) + except Exception: + self.log_error("Unable to add to Sent folder") + + def get_oauth_token(self): + if self.auth_method == "OAuth": + connected_app = xhiveframework.get_doc("Connected App", self.connected_app) + return connected_app.get_active_token(self.connected_user) + + +@xhiveframework.whitelist() +def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): + txt = txt if txt else "" + + filters = {"istable": 0, "issingle": 0, "email_append_to": 1} + # Set Email Append To DocTypes via DocType + email_append_to_list = [ + dt.name for dt in xhiveframework.get_all("DocType", filters=filters, fields=["name", "email_append_to"]) + ] + # Set Email Append To DocTypes set via Customize Form + email_append_to_list.extend( + dt.doc_type + for dt in xhiveframework.get_list( + "Property Setter", + filters={"property": "email_append_to", "value": 1}, + fields=["doc_type"], + ) + ) + return [[d] for d in set(email_append_to_list) if txt in d] + + +def notify_unreplied(): + """Sends email notifications if there are unreplied Communications + and `notify_if_unreplied` is set as true.""" + for email_account in xhiveframework.get_all( + "Email Account", + "name", + filters={"enable_incoming": 1, "notify_if_unreplied": 1}, + ): + email_account = xhiveframework.get_doc("Email Account", email_account.name) + + if email_account.use_imap: + append_to = [folder.get("append_to") for folder in email_account.imap_folder] + else: + append_to = email_account.append_to + + if append_to: + # get open communications younger than x mins, for given doctype + for comm in xhiveframework.get_all( + "Communication", + "name", + filters=[ + {"sent_or_received": "Received"}, + {"reference_doctype": ("in", append_to)}, + {"unread_notification_sent": 0}, + {"email_account": email_account.name}, + { + "creation": ( + "<", + datetime.now() - timedelta(seconds=(email_account.unreplied_for_mins or 30) * 60), + ) + }, + { + "creation": ( + ">", + datetime.now() + - timedelta(seconds=(email_account.unreplied_for_mins or 30) * 60 * 3), + ) + }, + ], + ): + comm = xhiveframework.get_doc("Communication", comm.name) + + if xhiveframework.db.get_value(comm.reference_doctype, comm.reference_name, "status") == "Open": + # if status is still open + xhiveframework.sendmail( + recipients=email_account.get_unreplied_notification_emails(), + content=comm.content, + subject=comm.subject, + doctype=comm.reference_doctype, + name=comm.reference_name, + ) + + # update flag + comm.db_set("unread_notification_sent", 1) + + +def pull(now=False): + """Will be called via scheduler, pull emails from all enabled Email accounts.""" + from xhiveframework.integrations.doctype.connected_app.connected_app import has_token + + doctype = xhiveframework.qb.DocType("Email Account") + email_accounts = ( + xhiveframework.qb.from_(doctype) + .select( + doctype.name, + doctype.auth_method, + doctype.connected_app, + doctype.connected_user, + ) + .where(doctype.enable_incoming == 1) + .where(doctype.awaiting_password == 0) + .run(as_dict=1) + ) + + for email_account in email_accounts: + if email_account.auth_method == "OAuth" and not has_token( + email_account.connected_app, email_account.connected_user + ): + # don't try to pull from accounts which dont have access token (for Oauth) + continue + + if now: + pull_from_email_account(email_account.name) + + else: + # job_name is used to prevent duplicates in queue + job_name = f"pull_from_email_account|{email_account.name}" + + queued_jobs = get_jobs(site=xhiveframework.local.site, key="job_name")[xhiveframework.local.site] + if job_name not in queued_jobs: + enqueue( + pull_from_email_account, + "short", + event="all", + job_name=job_name, + email_account=email_account.name, + ) + + +def pull_from_email_account(email_account): + """Runs within a worker process""" + email_account = xhiveframework.get_doc("Email Account", email_account) + email_account.receive() + + +def get_max_email_uid(email_account): + """get maximum uid of emails""" + + if result := xhiveframework.get_all( + "Communication", + filters={ + "communication_medium": "Email", + "sent_or_received": "Received", + "email_account": email_account, + }, + fields=["max(uid) as uid"], + ): + return cint(result[0].get("uid", 0)) + 1 + return 1 + + +def setup_user_email_inbox(email_account, awaiting_password, email_id, enable_outgoing, used_oauth): + """setup email inbox for user""" + from xhiveframework.core.doctype.user.user import ask_pass_update + + def add_user_email(user): + user = xhiveframework.get_doc("User", user) + row = user.append("user_emails", {}) + + row.email_id = email_id + row.email_account = email_account + row.awaiting_password = awaiting_password or 0 + row.used_oauth = used_oauth or 0 + row.enable_outgoing = enable_outgoing or 0 + + user.save(ignore_permissions=True) + + update_user_email_settings = False + if not all([email_account, email_id]): + return + + user_names = xhiveframework.db.get_values("User", {"email": email_id}, as_dict=True) + if not user_names: + return + + for user in user_names: + user_name = user.get("name") + + # check if inbox is alreay configured + user_inbox = ( + xhiveframework.db.get_value( + "User Email", + {"email_account": email_account, "parent": user_name}, + ["name"], + ) + or None + ) + + if not user_inbox: + add_user_email(user_name) + else: + # update awaiting password for email account + update_user_email_settings = True + + if update_user_email_settings: + UserEmail = xhiveframework.qb.DocType("User Email") + xhiveframework.qb.update(UserEmail).set(UserEmail.awaiting_password, (awaiting_password or 0)).set( + UserEmail.enable_outgoing, (enable_outgoing or 0) + ).set(UserEmail.used_oauth, (used_oauth or 0)).where(UserEmail.email_account == email_account).run() + + else: + users = " and ".join([xhiveframework.bold(user.get("name")) for user in user_names]) + xhiveframework.msgprint(_("Enabled email inbox for user {0}").format(users)) + ask_pass_update() + + +def remove_user_email_inbox(email_account): + """remove user email inbox settings if email account is deleted""" + if not email_account: + return + + users = xhiveframework.get_all( + "User Email", + filters={"email_account": email_account}, + fields=["parent as name"], + ) + + for user in users: + doc = xhiveframework.get_doc("User", user.get("name")) + to_remove = [row for row in doc.user_emails if row.email_account == email_account] + [doc.remove(row) for row in to_remove] + + doc.save(ignore_permissions=True) + + +@xhiveframework.whitelist() +def set_email_password(email_account, password): + account = xhiveframework.get_doc("Email Account", email_account) + if account.awaiting_password and account.auth_method != "OAuth": + account.awaiting_password = 0 + account.password = password + try: + account.save(ignore_permissions=True) + except Exception: + xhiveframework.db.rollback() + return False + + return True diff --git a/xhiveframework/email/doctype/email_account/email_account_list.js b/xhiveframework/email/doctype/email_account/email_account_list.js new file mode 100644 index 0000000..3b3bb9a --- /dev/null +++ b/xhiveframework/email/doctype/email_account/email_account_list.js @@ -0,0 +1,24 @@ +xhiveframework.listview_settings["Email Account"] = { + add_fields: ["default_incoming", "default_outgoing", "enable_incoming", "enable_outgoing"], + get_indicator: function (doc) { + if (doc.default_incoming && doc.default_outgoing) { + var color = doc.enable_incoming && doc.enable_outgoing ? "blue" : "gray"; + return [ + __("Default Sending and Inbox"), + color, + "default_incoming,=,Yes|default_outgoing,=,Yes", + ]; + } else if (doc.default_incoming) { + color = doc.enable_incoming ? "blue" : "gray"; + return [__("Default Inbox"), color, "default_incoming,=,Yes"]; + } else if (doc.default_outgoing) { + color = doc.enable_outgoing ? "blue" : "gray"; + return [__("Default Sending"), color, "default_outgoing,=,Yes"]; + } else { + color = doc.enable_incoming ? "blue" : "gray"; + return [__("Inbox"), color, "is_global,=,No|is_default=No"]; + } + }, +}; + +xhiveframework.help.youtube_id["Email Account"] = "ww"; diff --git a/xhiveframework/email/doctype/email_account/test_email_account.py b/xhiveframework/email/doctype/email_account/test_email_account.py new file mode 100644 index 0000000..59791b4 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_email_account.py @@ -0,0 +1,648 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import email +import os +import unittest +from datetime import datetime, timedelta +from unittest.mock import patch + +import xhiveframework +from xhiveframework.core.doctype.communication.email import make +from xhiveframework.desk.form.load import get_attachments +from xhiveframework.email.doctype.email_account.email_account import notify_unreplied +from xhiveframework.email.email_body import get_message_id +from xhiveframework.email.receive import Email, InboundMail, SentEmailInInboxError +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestEmailAccount(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + email_account.db_set("enable_auto_reply", 1) + email_account.db_set("use_imap", 1) + + @classmethod + def tearDownClass(cls): + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + + def setUp(self): + xhiveframework.flags.mute_emails = False + xhiveframework.flags.sent_mail = None + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Unhandled Email") + + def get_test_mail(self, fname): + with open(os.path.join(os.path.dirname(__file__), "test_mails", fname)) as f: + return f.read() + + def test_incoming(self): + cleanup("test_sender@example.com") + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [self.get_test_mail("incoming-1.raw")], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue("test_receiver@example.com" in comm.recipients) + # check if todo is created + self.assertTrue(xhiveframework.db.get_value(comm.reference_doctype, comm.reference_name, "name")) + + def test_unread_notification(self): + todo = xhiveframework.get_last_doc("ToDo") + + comm = xhiveframework.new_doc( + "Communication", + sender="test_sender@example.com", + subject="test unread reminder", + sent_or_received="Received", + reference_doctype=todo.doctype, + reference_name=todo.name, + email_account="_Test Email Account 1", + ) + comm.insert() + comm.db_set("creation", datetime.now() - timedelta(seconds=30 * 60)) + + xhiveframework.db.delete("Email Queue") + notify_unreplied() + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + { + "reference_doctype": comm.reference_doctype, + "reference_name": comm.reference_name, + }, + ) + ) + + def test_incoming_with_attach(self): + cleanup("test_sender@example.com") + + existing_file = xhiveframework.get_doc({"doctype": "File", "file_name": "xhiveerp-conf-14.png"}) + xhiveframework.delete_doc("File", existing_file.name) + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [self.get_test_mail("incoming-2.raw")], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue("test_receiver@example.com" in comm.recipients) + + # check attachment + attachments = get_attachments(comm.doctype, comm.name) + self.assertTrue("xhiveerp-conf-14.png" in [f.file_name for f in attachments]) + + # cleanup + existing_file = xhiveframework.get_doc({"doctype": "File", "file_name": "xhiveerp-conf-14.png"}) + xhiveframework.delete_doc("File", existing_file.name) + + def test_incoming_attached_email_from_outlook_plain_text_only(self): + cleanup("test_sender@example.com") + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [self.get_test_mail("incoming-3.raw")], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue("From: "Microsoft Outlook" <test_sender@example.com>" in comm.content) + self.assertTrue( + "This is an e-mail message sent automatically by Microsoft Outlook while" in comm.content + ) + + def test_incoming_attached_email_from_outlook_layers(self): + cleanup("test_sender@example.com") + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [self.get_test_mail("incoming-4.raw")], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue("From: "Microsoft Outlook" <test_sender@example.com>" in comm.content) + self.assertTrue( + "This is an e-mail message sent automatically by Microsoft Outlook while" in comm.content + ) + + def test_outgoing(self): + make( + subject="test-mail-000", + content="test mail 000", + recipients="test_receiver@example.com", + send_email=True, + sender="test_sender@example.com", + ) + + mail = email.message_from_string(xhiveframework.get_last_doc("Email Queue").message) + self.assertTrue("test-mail-000" in mail.get("Subject")) + + def test_sendmail(self): + xhiveframework.sendmail( + sender="test_sender@example.com", + recipients="test_recipient@example.com", + content="test mail 001", + subject="test-mail-001", + delayed=False, + ) + + sent_mail = email.message_from_string(xhiveframework.safe_decode(xhiveframework.flags.sent_mail)) + self.assertTrue("test-mail-001" in sent_mail.get("Subject")) + + def test_print_format(self): + make( + sender="test_sender@example.com", + recipients="test_recipient@example.com", + content="test mail 001", + subject="test-mail-002", + doctype="Email Account", + name="_Test Email Account 1", + print_format="Standard", + send_email=True, + ) + + sent_mail = email.message_from_string(xhiveframework.get_last_doc("Email Queue").message) + self.assertTrue("test-mail-002" in sent_mail.get("Subject")) + + def test_threading(self): + cleanup(["in", ["test_sender@example.com", "test@example.com"]]) + + # send + sent_name = make( + subject="Test", + content="test content", + recipients="test_receiver@example.com", + sender="test@example.com", + doctype="ToDo", + name=xhiveframework.get_last_doc("ToDo").name, + send_email=True, + )["name"] + + sent_mail = email.message_from_string(xhiveframework.get_last_doc("Email Queue").message) + + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-1.raw")) as f: + raw = f.read() + raw = raw.replace("<-- in-reply-to -->", sent_mail.get("Message-Id")) + + # parse reply + messages = { + # append_to = ToDo + '"INBOX"': {"latest_messages": [raw], "seen_status": {2: "UNSEEN"}, "uid_list": [2]} + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + sent = xhiveframework.get_doc("Communication", sent_name) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertEqual(comm.reference_doctype, sent.reference_doctype) + self.assertEqual(comm.reference_name, sent.reference_name) + + def test_threading_by_subject(self): + cleanup(["in", ["test_sender@example.com", "test@example.com"]]) + + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-2.raw")) as f: + test_mails = [f.read()] + + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-3.raw")) as f: + test_mails.append(f.read()) + + # parse reply + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": test_mails, + "seen_status": {2: "UNSEEN", 3: "UNSEEN"}, + "uid_list": [2, 3], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm_list = xhiveframework.get_all( + "Communication", + filters={"sender": "test_sender@example.com"}, + fields=["name", "reference_doctype", "reference_name"], + ) + # both communications attached to the same reference + self.assertEqual(comm_list[0].reference_doctype, comm_list[1].reference_doctype) + self.assertEqual(comm_list[0].reference_name, comm_list[1].reference_name) + + def test_threading_by_message_id(self): + cleanup() + xhiveframework.db.delete("Email Queue") + + # reference document for testing + event = xhiveframework.get_doc(dict(doctype="Event", subject="test-message")).insert() + + # send a mail against this + xhiveframework.sendmail( + recipients="test@example.com", + subject="test message for threading", + message="testing", + reference_doctype=event.doctype, + reference_name=event.name, + ) + + last_mail = xhiveframework.get_doc("Email Queue", dict(reference_name=event.name)) + + # get test mail with message-id as in-reply-to + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-4.raw")) as f: + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [ + f.read().replace("{{ message_id }}", "<" + last_mail.message_id + ">") + ], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + # pull the mail + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm_list = xhiveframework.get_all( + "Communication", + filters={"sender": "test_sender@example.com"}, + fields=["name", "reference_doctype", "reference_name"], + ) + + # check if threaded correctly + self.assertEqual(comm_list[0].reference_doctype, event.doctype) + self.assertEqual(comm_list[0].reference_name, event.name) + + def test_auto_reply(self): + cleanup("test_sender@example.com") + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [self.get_test_mail("incoming-1.raw")], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + } + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + TestEmailAccount.mocked_email_receive(email_account, messages) + + comm = xhiveframework.get_doc("Communication", {"sender": "test_sender@example.com"}) + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": comm.reference_doctype, "reference_name": comm.reference_name}, + ) + ) + + def test_handle_bad_emails(self): + mail_content = self.get_test_mail(fname="incoming-1.raw") + message_id = Email(mail_content).mail.get("Message-ID") + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.handle_bad_emails(uid=-1, raw=mail_content, reason="Testing") + self.assertTrue(xhiveframework.db.get_value("Unhandled Email", {"message_id": message_id})) + + def test_imap_folder(self): + # assert tests if imap_folder >= 1 and imap is checked + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + + self.assertTrue(email_account.use_imap) + self.assertTrue(email_account.enable_incoming) + self.assertTrue(len(email_account.imap_folder) > 0) + + def test_imap_folder_missing(self): + # Test the Exception in validate() that verifies the imap_folder list + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.imap_folder = [] + + with self.assertRaises(Exception): + email_account.validate() + + def test_append_to(self): + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + mail_content = self.get_test_mail(fname="incoming-2.raw") + + inbound_mail = InboundMail(mail_content, email_account, 12345, 1, "ToDo") + communication = inbound_mail.process() + # the append_to for the email is set to ToDO in "_Test Email Account 1" + self.assertEqual(communication.reference_doctype, "ToDo") + self.assertTrue(communication.reference_name) + self.assertTrue(xhiveframework.db.exists(communication.reference_doctype, communication.reference_name)) + + @unittest.skip("poorly written and flaky") + def test_append_to_with_imap_folders(self): + mail_content_1 = self.get_test_mail(fname="incoming-1.raw") + mail_content_2 = self.get_test_mail(fname="incoming-2.raw") + mail_content_3 = self.get_test_mail(fname="incoming-3.raw") + + messages = { + # append_to = ToDo + '"INBOX"': { + "latest_messages": [mail_content_1, mail_content_2], + "seen_status": {0: "UNSEEN", 1: "UNSEEN"}, + "uid_list": [0, 1], + }, + # append_to = Communication + '"Test Folder"': { + "latest_messages": [mail_content_3], + "seen_status": {2: "UNSEEN"}, + "uid_list": [2], + }, + } + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + mails = TestEmailAccount.mocked_get_inbound_mails(email_account, messages) + self.assertEqual(len(mails), 3) + + inbox_mails = 0 + test_folder_mails = 0 + + for mail in mails: + communication = mail.process() + if mail.append_to == "ToDo": + inbox_mails += 1 + self.assertEqual(communication.reference_doctype, "ToDo") + self.assertTrue(communication.reference_name) + self.assertTrue( + xhiveframework.db.exists(communication.reference_doctype, communication.reference_name) + ) + else: + test_folder_mails += 1 + self.assertEqual(communication.reference_doctype, None) + + self.assertEqual(inbox_mails, 2) + self.assertEqual(test_folder_mails, 1) + + @patch("xhiveframework.email.receive.EmailServer.select_imap_folder", return_value=True) + @patch("xhiveframework.email.receive.EmailServer.logout", side_effect=lambda: None) + def mocked_get_inbound_mails( + email_account, messages=None, mocked_logout=None, mocked_select_imap_folder=None + ): + from xhiveframework.email.receive import EmailServer + + if messages is None: + messages = {} + + def get_mocked_messages(**kwargs): + return messages.get(kwargs["folder"], {}) + + with patch.object(EmailServer, "get_messages", side_effect=get_mocked_messages): + mails = email_account.get_inbound_mails() + + return mails + + @patch("xhiveframework.email.receive.EmailServer.select_imap_folder", return_value=True) + @patch("xhiveframework.email.receive.EmailServer.logout", side_effect=lambda: None) + def mocked_email_receive( + email_account, messages=None, mocked_logout=None, mocked_select_imap_folder=None + ): + if messages is None: + messages = {} + + def get_mocked_messages(**kwargs): + return messages.get(kwargs["folder"], {}) + + from xhiveframework.email.receive import EmailServer + + with patch.object(EmailServer, "get_messages", side_effect=get_mocked_messages): + email_account.receive() + + +class TestInboundMail(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 1) + + @classmethod + def tearDownClass(cls): + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set("enable_incoming", 0) + + def setUp(self): + cleanup() + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("ToDo") + + def get_test_mail(self, fname): + with open(os.path.join(os.path.dirname(__file__), "test_mails", fname)) as f: + return f.read() + + def new_doc(self, doctype, **data): + doc = xhiveframework.new_doc(doctype) + for field, value in data.items(): + setattr(doc, field, value) + doc.insert() + return doc + + def new_communication(self, **kwargs): + defaults = {"subject": "Test Subject"} + d = {**defaults, **kwargs} + return self.new_doc("Communication", **d) + + def new_email_queue(self, **kwargs): + defaults = {"message_id": get_message_id().strip(" <>")} + d = {**defaults, **kwargs} + return self.new_doc("Email Queue", **d) + + def new_todo(self, **kwargs): + defaults = {"description": "Description"} + d = {**defaults, **kwargs} + return self.new_doc("ToDo", **d) + + def test_self_sent_mail(self): + """Check that we raise SentEmailInInboxError if the inbound mail is self sent mail.""" + mail_content = self.get_test_mail(fname="incoming-self-sent.raw") + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 1, 1) + with self.assertRaises(SentEmailInInboxError): + inbound_mail.process() + + def test_mail_exist_validation(self): + """Do not create communication record if the mail is already downloaded into the system.""" + mail_content = self.get_test_mail(fname="incoming-1.raw") + message_id = Email(mail_content).message_id + # Create new communication record in DB + communication = self.new_communication(message_id=message_id) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + new_communication = inbound_mail.process() + + # Make sure that uid is changed to new uid + self.assertEqual(new_communication.uid, 12345) + self.assertEqual(communication.name, new_communication.name) + + def test_find_parent_email_queue(self): + """If the mail is reply to the already sent mail, there will be a email queue record.""" + # Create email queue record + queue_record = self.new_email_queue() + + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_queue = inbound_mail.parent_email_queue() + self.assertEqual(queue_record.name, parent_queue.name) + + def test_find_parent_communication_through_queue(self): + """Find parent communication of an inbound mail. + Cases where parent communication does exist: + 1. No parent communication is the mail is not a reply. + + Cases where parent communication does not exist: + 2. If mail is not a reply to system sent mail, then there can exist co + """ + # Create email queue record + communication = self.new_communication() + queue_record = self.new_email_queue(communication=communication.name) + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_find_parent_communication_for_self_reply(self): + """If the inbound email is a reply but not reply to system sent mail. + + Ex: User replied to his/her mail. + """ + message_id = "new-message-id" + mail_content = self.get_test_mail(fname="reply-4.raw").replace("{{ message_id }}", message_id) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertFalse(parent_communication) + + communication = self.new_communication(message_id=message_id) + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_find_parent_communication_from_header(self): + """Incase of header contains parent communication name""" + communication = self.new_communication() + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", f"<{communication.name}@{xhiveframework.local.site}>" + ) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + parent_communication = inbound_mail.parent_communication() + self.assertEqual(parent_communication.name, communication.name) + + def test_reference_document(self): + # Create email queue record + todo = self.new_todo() + # communication = self.new_communication(reference_doctype='ToDo', reference_name=todo.name) + queue_record = self.new_email_queue(reference_doctype="ToDo", reference_name=todo.name) + mail_content = self.get_test_mail(fname="reply-4.raw").replace( + "{{ message_id }}", queue_record.message_id + ) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_reference_document_by_record_name_in_subject(self): + # Create email queue record + todo = self.new_todo() + + mail_content = self.get_test_mail(fname="incoming-subject-placeholder.raw").replace( + "{{ subject }}", f"RE: (#{todo.name})" + ) + + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_reference_document_by_subject_match(self): + subject = "New todo" + todo = self.new_todo(sender="test_sender@example.com", description=subject) + + mail_content = self.get_test_mail(fname="incoming-subject-placeholder.raw").replace( + "{{ subject }}", f"RE: {subject}" + ) + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_reference_document_by_subject_match_with_accents(self): + subject = "Nouvelle tâche à faire 😃" + todo = self.new_todo(sender="test_sender@example.com", description=subject) + + mail_content = ( + self.get_test_mail(fname="incoming-subject-placeholder.raw") + .replace("{{ subject }}", f"RE: {subject}") + .encode("utf-8") + ) # note: encode to bytes because that's what triggered the error + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + reference_doc = inbound_mail.reference_document() + self.assertEqual(todo.name, reference_doc.name) + + def test_create_communication_from_mail(self): + # Create email queue record + mail_content = self.get_test_mail(fname="incoming-2.raw") + email_account = xhiveframework.get_doc("Email Account", "_Test Email Account 1") + inbound_mail = InboundMail(mail_content, email_account, 12345, 1) + communication = inbound_mail.process() + self.assertTrue(communication._attachments) + + +def cleanup(sender=None): + filters = {} + if sender: + filters.update({"sender": sender}) + + names = xhiveframework.get_list("Communication", filters=filters, fields=["name"]) + for name in names: + xhiveframework.delete_doc_if_exists("Communication", name.name) + xhiveframework.delete_doc_if_exists("Communication Link", {"parent": name.name}) diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-1.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-1.raw new file mode 100644 index 0000000..287ee2b --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-1.raw @@ -0,0 +1,91 @@ +Delivered-To: test_receiver@example.com +Received: by 10.96.153.227 with SMTP id vj3csp416144qdb; + Mon, 15 Sep 2014 03:35:07 -0700 (PDT) +X-Received: by 10.66.119.103 with SMTP id kt7mr36981968pab.95.1410777306321; + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Return-Path: +Received: from mail-pa0-x230.google.com (mail-pa0-x230.google.com [2607:f8b0:400e:c03::230]) + by mx.google.com with ESMTPS id dg10si22178346pdb.115.2014.09.15.03.35.06 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Received-SPF: pass (google.com: domain of test_sender@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) client-ip=2607:f8b0:400e:c03::230; +Authentication-Results: mx.google.com; + spf=pass (google.com: domain of test_sender@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) smtp.mail=test_sender@example.com; + dkim=pass header.i=@gmail.com; + dmarc=pass (p=NONE dis=NONE) header.from=gmail.com +Received: by mail-pa0-f48.google.com with SMTP id hz1so6118714pad.21 + for ; Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=from:content-type:subject:message-id:date:to:mime-version; + bh=rwiLijtF3lfy9M6cP/7dv2Hm7NJuBwFZn1OFsN8Tlvs=; + b=x7U4Ny3Kz2ULRJ7a04NDBrBTVhP2ImIB9n3LVNGQDnDonPUM5Ro/wZcxPTVnBWZ2L1 + o1bGfP+lhBrvYUlHsd5r4FYC0Uvpad6hbzLr0DGUQgPTxW4cGKbtDEAq+BR2JWd9f803 + vdjSWdGk8w2dt2qbngTqIZkm5U2XWjICDOAYuPIseLUgCFwi9lLyOSARFB7mjAa2YL7Q + Nswk7mbWU1hbnHP6jaBb0m8QanTc7Up944HpNDRxIrB1ZHgKzYhXtx8nhnOx588ZGIAe + E6tyG8IwogR11vLkkrBhtMaOme9PohYx4F1CSTiwspmDCadEzJFGRe//lEXKmZHAYH6g + 90Zg== +X-Received: by 10.70.38.135 with SMTP id g7mr22078275pdk.100.1410777305744; + Mon, 15 Sep 2014 03:35:05 -0700 (PDT) +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id zr6sm11025126pbc.50.2014.09.15.03.35.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:04 -0700 (PDT) +From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA" +Subject: test mail 🦄🌈😎 +Message-Id: <9143999C-8456-4399-9CF1-4A2DA9DD7711@gmail.com> +Date: Mon, 15 Sep 2014 16:04:57 +0530 +To: Rushabh Mehta +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test mail + + + +@rushabh_mehta +https://xhiveerp.org + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; + charset=us-ascii + +test = +mail
      +



      @rushabh_mehta
      +
      +
      = + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-2.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-2.raw new file mode 100644 index 0000000..49f3a40 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-2.raw @@ -0,0 +1,511 @@ +Delivered-To: test_receiver@example.com +Received: by 10.96.153.227 with SMTP id vj3csp576735qdb; + Mon, 15 Sep 2014 23:28:05 -0700 (PDT) +X-Received: by 10.66.65.202 with SMTP id z10mr41658290pas.20.1410848884944; + Mon, 15 Sep 2014 23:28:04 -0700 (PDT) +Return-Path: +Received: from mail-pa0-x232.google.com (mail-pa0-x232.google.com [2607:f8b0:400e:c03::232]) + by mx.google.com with ESMTPS id he2si27434308pac.73.2014.09.15.23.28.04 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:28:04 -0700 (PDT) +Received-SPF: pass (google.com: domain of test_sender@example.com designates 2607:f8b0:400e:c03::232 as permitted sender) client-ip=2607:f8b0:400e:c03::232; +Authentication-Results: mx.google.com; + spf=pass (google.com: domain of test_sender@example.com designates 2607:f8b0:400e:c03::232 as permitted sender) smtp.mail=test_sender@example.com; + dkim=pass header.i=@gmail.com; + dmarc=pass (p=NONE dis=NONE) header.from=gmail.com +Received: by mail-pa0-f50.google.com with SMTP id bj1so8166191pad.37 + for ; Mon, 15 Sep 2014 23:28:04 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=from:content-type:subject:message-id:date:to:mime-version; + bh=RjaUdjmv0mhS2w8fKWC6N0GWDRnlLZrOgmhtagrz2gY=; + b=rKWW/c0NKT37bbAO8RHBI8JnrXjAZbAP+XPfo7tP58iJ+BKK1UE2J7iDluE5QPeuXg + WhyN0mK8hfAjLF9BDl2OfRR4yDuNIq2nuqVu4wW0NtacDqJzSEW41xdnSDDkXLCBN/c2 + BNikjY9fQkiQ2axEVbHJCCxcMA9ZNZXq3212xnd78eDUIUnVDeIc7XE/PnecwmxclRtc + bJV4zTVKsGbSP3fnyd7VX0w4ezjSX9oUPPRSvVAAjPSfREfRchptsX9LCB2LiVP0rVGO + h4IVIpdeBeMsyiM+4UcRn5raLSeX5WvD/eTyJ28qQz3t0nDZnSSVFboBkP2QcAWJNXcX + ydoQ== +X-Received: by 10.68.171.33 with SMTP id ar1mr12518010pbc.148.1410848884267; + Mon, 15 Sep 2014 23:28:04 -0700 (PDT) +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id sf1sm13174290pbb.0.2014.09.15.23.28.01 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:28:02 -0700 (PDT) +From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_6F6D23B5-0ADE-4347-978A-E0A21B92BB70" +Subject: test +Message-Id: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +Date: Tue, 16 Sep 2014 11:57:58 +0530 +To: Rushabh Mehta +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_6F6D23B5-0ADE-4347-978A-E0A21B92BB70 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +with attachment + + + + + +--Apple-Mail=_6F6D23B5-0ADE-4347-978A-E0A21B92BB70 +Content-Type: multipart/related; + type="text/html"; + boundary="Apple-Mail=_18FE9DD1-B053-4781-ACFD-7E542B418B05" + + +--Apple-Mail=_18FE9DD1-B053-4781-ACFD-7E542B418B05 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +
      with attachment

      +


      +
      +
      +--Apple-Mail=_18FE9DD1-B053-4781-ACFD-7E542B418B05 +Content-Transfer-Encoding: base64 +Content-Disposition: inline; + filename=xhiveerp-conf-14.png +Content-Type: image/png; + x-unix-mode=0644; + name="xhiveerp-conf-14.png" +Content-Id: <9137CC31-70BA-4232-8438-6B9D8B15A1B5@webnotes_sn> + +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 +REPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH +4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN +8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqw +K10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTk +muRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99u +it7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/nd +zPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqv +akfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/ +Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4 +H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HO +FZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9 +jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3R +B6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0 +RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk +03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMA +AAsTAAALEwEAmpwYAAADqGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxu +czp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJE +RiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMi +PgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4 +bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnRpZmY9 +Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgICAgICAgICB4bWxuczpleGlmPSJo +dHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4y +MDE0LTA3LTE4VDE0OjA3OjM3PC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9y +VG9vbD5QaXhlbG1hdG9yIDMuMjwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8dGlmZjpPcmll +bnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpDb21wcmVzc2lvbj41 +PC90aWZmOkNvbXByZXNzaW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4xPC90aWZm +OlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVz +b2x1dGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+ +CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj42ODY8L2V4aWY6UGl4ZWxYRGltZW5zaW9u +PgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAg +PGV4aWY6UGl4ZWxZRGltZW5zaW9uPjEzODwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwv +cmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgqKhMWCAABAAElEQVR4 +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 + +--Apple-Mail=_18FE9DD1-B053-4781-ACFD-7E542B418B05-- + +--Apple-Mail=_6F6D23B5-0ADE-4347-978A-E0A21B92BB70-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-3.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-3.raw new file mode 100644 index 0000000..1571300 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-3.raw @@ -0,0 +1,183 @@ +Return-path: +Envelope-to: test_receiver@example.com +Delivery-date: Wed, 27 Jan 2016 16:24:20 +0800 +Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:62191 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtp (Exim 4.86) + (envelope-from ) + id 1aOLOj-002xFL-CP + for test_receiver@example.com; Wed, 27 Jan 2016 16:24:20 +0800 +From: +To: +References: +In-Reply-To: +Subject: RE: Sales Invoice: SINV-12276 +Date: Wed, 27 Jan 2016 16:24:09 +0800 +Message-ID: <000001d158dc$1b8363a0$528a2ae0$@example.com> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_NextPart_000_0001_01D1591F.29A7DC20" +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AQJZfZxrgcB9KnMqoZ+S4Qq9hcoSeZ3+vGiQ +Content-Language: en-au + +This is a multipart message in MIME format. + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: multipart/alternative; + boundary="----=_NextPart_001_0002_01D1591F.29A7DC20" + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +Test purely for testing with the debugger has email attached + +=20 + +From: Notification [mailto:test_receiver@example.com]=20 +Sent: Wednesday, 27 January 2016 9:30 AM +To: test_receiver@example.com +Subject: Sales Invoice: SINV-12276 + +=20 + +test no 6 sent from bench to outlook to be replied to with messaging + + + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/html; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +hi there

      Test purely for testing with the debugger has email = +attached

       

      From:= + = +Notification [mailto:test_receiver@example.com]
      Sent: Wednesday, 27 = +January 2016 9:30 AM
      To: = +test_receiver@example.com
      Subject: Sales Invoice: = +SINV-12276

       

      test no 3 sent from bench to outlook to be replied to with = +messaging

      fizz buzz

      This email was sent to test_receiver@example.= +com and copied to SuperUser

      Leave this conversation = +

      hi

      +------=_NextPart_001_0002_01D1591F.29A7DC20-- + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: message/rfc822 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment + +Received: from 203-59-223-10.perm.iinet.net.au ([23.59.23.10]:49772 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtpsa (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) + (Exim 4.86) + (envelope-from ) + id 1aOEtO-003tI4-Kv + for test_receiver@example.com; Wed, 27 Jan 2016 09:27:30 +0800 +Return-Path: +From: "Microsoft Outlook" +To: +Subject: Microsoft Outlook Test Message +MIME-Version: 1.0 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AdFYoeN8x8wUI/+QSoCJkp33NKPVmw== + +This is an e-mail message sent automatically by Microsoft Outlook while = +testing the settings for your account. diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-4.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-4.raw new file mode 100644 index 0000000..9937488 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-4.raw @@ -0,0 +1,138 @@ +Return-path: +Envelope-to: test_receiver@example.com +Delivery-date: Tue, 09 Feb 2016 14:53:22 +0800 +Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:56280) + by webcloud85.au.syrahost.com with esmtp (Exim 4.86) + (envelope-from ) + id 1aT2As-003QlT-B0 + for test_receiver@example.com; Tue, 09 Feb 2016 14:53:22 +0800 +From: +To: +Subject: test email +Date: Tue, 9 Feb 2016 14:53:13 +0800 +Message-ID: <000001d16306$8b9e5c60$a2db1520$@ia-group.com.au> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_NextPart_000_0005_01D16349.99C37120" +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AdFjBjqdYnxyziyBQVK9mLTYYu+9Og== +Content-Language: en-au + +This is a multipart message in MIME format. + +------=_NextPart_000_0005_01D16349.99C37120 +Content-Type: multipart/alternative; + boundary="----=_NextPart_001_0006_01D16349.99C37120" + + +------=_NextPart_001_0006_01D16349.99C37120 +Content-Type: text/plain; + charset="us-ascii" +Content-Transfer-Encoding: 7bit + +4th test email + + +------=_NextPart_001_0006_01D16349.99C37120 +Content-Type: text/html; + charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +

      4th test email = +

      +------=_NextPart_001_0006_01D16349.99C37120-- + +------=_NextPart_000_0005_01D16349.99C37120 +Content-Type: message/rfc822 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment + +Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:49772 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtpsa (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) + (Exim 4.86) + (envelope-from ) + id 1aOEtO-003tI4-Kv + for test_receiver@example.com; Wed, 27 Jan 2016 09:27:30 +0800 +Return-Path: +From: "Microsoft Outlook" +To: +Subject: Microsoft Outlook Test Message +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="----=_NextPart_000_0001_01D16349.99C25FB0" +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AdFYoeN8x8wUI/+QSoCJkp33NKPVmw== + +This is a multipart message in MIME format. + +------=_NextPart_000_0001_01D16349.99C25FB0 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +This is an e-mail message sent automatically by Microsoft Outlook while = +testing the settings for your account.=20 + +------=_NextPart_000_0001_01D16349.99C25FB0 +Content-Type: text/html; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + + + +This is an e-mail message sent automatically by Microsoft Outlook while = +testing the settings for your account. + +------=_NextPart_000_0001_01D16349.99C25FB0-- + +------=_NextPart_000_0005_01D16349.99C37120-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-self-sent.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-self-sent.raw new file mode 100644 index 0000000..7744f99 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-self-sent.raw @@ -0,0 +1,91 @@ +Delivered-To: test_receiver@example.com +Received: by 10.96.153.227 with SMTP id vj3csp416144qdb; + Mon, 15 Sep 2014 03:35:07 -0700 (PDT) +X-Received: by 10.66.119.103 with SMTP id kt7mr36981968pab.95.1410777306321; + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Return-Path: +Received: from mail-pa0-x230.google.com (mail-pa0-x230.google.com [2607:f8b0:400e:c03::230]) + by mx.google.com with ESMTPS id dg10si22178346pdb.115.2014.09.15.03.35.06 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +Received-SPF: pass (google.com: domain of test@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) client-ip=2607:f8b0:400e:c03::230; +Authentication-Results: mx.google.com; + spf=pass (google.com: domain of test@example.com designates 2607:f8b0:400e:c03::230 as permitted sender) smtp.mail=test@example.com; + dkim=pass header.i=@gmail.com; + dmarc=pass (p=NONE dis=NONE) header.from=gmail.com +Received: by mail-pa0-f48.google.com with SMTP id hz1so6118714pad.21 + for ; Mon, 15 Sep 2014 03:35:06 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20120113; + h=from:content-type:subject:message-id:date:to:mime-version; + bh=rwiLijtF3lfy9M6cP/7dv2Hm7NJuBwFZn1OFsN8Tlvs=; + b=x7U4Ny3Kz2ULRJ7a04NDBrBTVhP2ImIB9n3LVNGQDnDonPUM5Ro/wZcxPTVnBWZ2L1 + o1bGfP+lhBrvYUlHsd5r4FYC0Uvpad6hbzLr0DGUQgPTxW4cGKbtDEAq+BR2JWd9f803 + vdjSWdGk8w2dt2qbngTqIZkm5U2XWjICDOAYuPIseLUgCFwi9lLyOSARFB7mjAa2YL7Q + Nswk7mbWU1hbnHP6jaBb0m8QanTc7Up944HpNDRxIrB1ZHgKzYhXtx8nhnOx588ZGIAe + E6tyG8IwogR11vLkkrBhtMaOme9PohYx4F1CSTiwspmDCadEzJFGRe//lEXKmZHAYH6g + 90Zg== +X-Received: by 10.70.38.135 with SMTP id g7mr22078275pdk.100.1410777305744; + Mon, 15 Sep 2014 03:35:05 -0700 (PDT) +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id zr6sm11025126pbc.50.2014.09.15.03.35.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 03:35:04 -0700 (PDT) +From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA" +Subject: test mail 🦄🌈😎 +Message-Id: <9143999C-8456-4399-9CF1-4A2DA9DD7711@gmail.com> +Date: Mon, 15 Sep 2014 16:04:57 +0530 +To: Rushabh Mehta +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test mail + + + +@rushabh_mehta +https://xhiveerp.org + + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; + charset=us-ascii + +test = +mail
      +



      @rushabh_mehta
      +
      +
      = + +--Apple-Mail=_57F71261-5C3A-43F6-918B-4438B96F61AA-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw b/xhiveframework/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw new file mode 100644 index 0000000..35ddf06 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/incoming-subject-placeholder.raw @@ -0,0 +1,183 @@ +Return-path: +Envelope-to: test_receiver@example.com +Delivery-date: Wed, 27 Jan 2016 16:24:20 +0800 +Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:62191 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtp (Exim 4.86) + (envelope-from ) + id 1aOLOj-002xFL-CP + for test_receiver@example.com; Wed, 27 Jan 2016 16:24:20 +0800 +From: +To: +References: +In-Reply-To: +Subject: RE: {{ subject }} +Date: Wed, 27 Jan 2016 16:24:09 +0800 +Message-ID: <000001d158dc$1b8363a0$528a2ae0$@example.com> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="----=_NextPart_000_0001_01D1591F.29A7DC20" +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AQJZfZxrgcB9KnMqoZ+S4Qq9hcoSeZ3+vGiQ +Content-Language: en-au + +This is a multipart message in MIME format. + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: multipart/alternative; + boundary="----=_NextPart_001_0002_01D1591F.29A7DC20" + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +Test purely for testing with the debugger has email attached + +=20 + +From: Notification [mailto:test_receiver@example.com]=20 +Sent: Wednesday, 27 January 2016 9:30 AM +To: test_receiver@example.com +Subject: Sales Invoice: SINV-12276 + +=20 + +test no 6 sent from bench to outlook to be replied to with messaging + + + + +------=_NextPart_001_0002_01D1591F.29A7DC20 +Content-Type: text/html; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +hi there

      Test purely for testing with the debugger has email = +attached

       

      From:= + = +Notification [mailto:test_receiver@example.com]
      Sent: Wednesday, 27 = +January 2016 9:30 AM
      To: = +test_receiver@example.com
      Subject: Sales Invoice: = +SINV-12276

       

      test no 3 sent from bench to outlook to be replied to with = +messaging

      fizz buzz

      This email was sent to test_receiver@example.= +com and copied to SuperUser

      Leave this conversation = +

      hi

      +------=_NextPart_001_0002_01D1591F.29A7DC20-- + +------=_NextPart_000_0001_01D1591F.29A7DC20 +Content-Type: message/rfc822 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment + +Received: from 203-59-223-10.perm.iinet.net.au ([23.59.23.10]:49772 helo=DESKTOP7C66I2M) + by webcloud85.au.syrahost.com with esmtpsa (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) + (Exim 4.86) + (envelope-from ) + id 1aOEtO-003tI4-Kv + for test_receiver@example.com; Wed, 27 Jan 2016 09:27:30 +0800 +Return-Path: +From: "Microsoft Outlook" +To: +Subject: Microsoft Outlook Test Message +MIME-Version: 1.0 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable +X-Mailer: Microsoft Outlook 14.0 +Thread-Index: AdFYoeN8x8wUI/+QSoCJkp33NKPVmw== + +This is an e-mail message sent automatically by Microsoft Outlook while = +testing the settings for your account. diff --git a/xhiveframework/email/doctype/email_account/test_mails/reply-1.raw b/xhiveframework/email/doctype/email_account/test_mails/reply-1.raw new file mode 100644 index 0000000..69e5141 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/reply-1.raw @@ -0,0 +1,47 @@ +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id n15sm4041161pdj.34.2014.09.15.23.48.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:48:04 -0700 (PDT) +From: Rushabh Mehta +X-Google-Original-From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040" +Message-Id: <943C954B-A6CE-4E8B-BA0B-1714309AB8BB@xhiveerp.com> +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +Subject: Re: test +Date: Tue, 16 Sep 2014 12:17:58 +0530 +References: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +To: Rushabh Mehta +In-Reply-To: <-- in-reply-to --> +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test-reply + + +On 16-Sep-2014, at 11:57 am, Rushabh Mehta wrote: + +> with attachment +> +> +> +> + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +test-reply
      +

      On 16-Sep-2014, at 11:57 am, Rushabh Mehta <test_sender@example.com> wrote:

      with attachment

      +
      <xhiveerp-conf-14.png>

      +
      +

      +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/reply-2.raw b/xhiveframework/email/doctype/email_account/test_mails/reply-2.raw new file mode 100644 index 0000000..74b3466 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/reply-2.raw @@ -0,0 +1,45 @@ +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id n15sm4041161pdj.34.2014.09.15.23.48.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:48:04 -0700 (PDT) +From: Rushabh Mehta +X-Google-Original-From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040" +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +Subject: weird subject ddwdf23r2 +Date: Tue, 16 Sep 2014 12:17:58 +0530 +References: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +To: Rushabh Mehta +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test-reply-2 + + +On 16-Sep-2014, at 11:57 am, Rushabh Mehta wrote: + +> with attachment +> +> +> +> + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +test-reply
      +

      On 16-Sep-2014, at 11:57 am, Rushabh Mehta <test_sender@example.com> wrote:

      with attachment

      +
      <xhiveerp-conf-14.png>

      +
      +

      +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/reply-3.raw b/xhiveframework/email/doctype/email_account/test_mails/reply-3.raw new file mode 100644 index 0000000..fdea5e7 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/reply-3.raw @@ -0,0 +1,45 @@ +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id n15sm4041161pdj.34.2014.09.15.23.48.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:48:04 -0700 (PDT) +From: Rushabh Mehta +X-Google-Original-From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040" +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +Subject: Re: weird subject ddwdf23r2 +Date: Tue, 16 Sep 2014 12:17:58 +0530 +References: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +To: Rushabh Mehta +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test-reply-3 + + +On 16-Sep-2014, at 11:57 am, Rushabh Mehta wrote: + +> with attachment +> +> +> +> + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +test-reply
      +

      On 16-Sep-2014, at 11:57 am, Rushabh Mehta <test_sender@example.com> wrote:

      with attachment

      +
      <xhiveerp-conf-14.png>

      +
      +

      +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040-- diff --git a/xhiveframework/email/doctype/email_account/test_mails/reply-4.raw b/xhiveframework/email/doctype/email_account/test_mails/reply-4.raw new file mode 100644 index 0000000..be651c8 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_mails/reply-4.raw @@ -0,0 +1,75 @@ +From: +Content-Type: multipart/alternative; + boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361" +Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com> +Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) +X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F +Subject: Re: What did you work on today? +Date: Thu, 10 Nov 2016 16:04:43 +0530 +X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2 +References: {{ message_id }} +To: test_in@iwebnotes.com +In-Reply-To: {{ message_id }} + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; + charset=us-ascii + +Testing another reply! + +> On 10-Nov-2016, at 3:20 PM, XhiveFramework wrote: +>=20 +> Please share what did you do today. If you reply by midnight, your = +response will be recorded! +>=20 +> This email was sent to rmehta@gmail.com +> Unsubscribe from this list = + +> Sent via XhiveERP + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +Testing another reply!

      On 10-Nov-2016, at 3:20 PM, XhiveFramework <test@xhiveerp.com> wrote:

      + + + + +What did you work on today? + +
      + +

      Please share what did you do today. If you reply by midnight, your response will be recorded!

      + +
      + + + + + + +
      +

      +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361-- diff --git a/xhiveframework/email/doctype/email_account/test_records.json b/xhiveframework/email/doctype/email_account/test_records.json new file mode 100644 index 0000000..2e204e5 --- /dev/null +++ b/xhiveframework/email/doctype/email_account/test_records.json @@ -0,0 +1,29 @@ +[ + { + "is_default": 1, + "is_global": 1, + "doctype": "Email Account", + "domain":"example.com", + "email_account_name": "_Test Email Account 1", + "enable_outgoing": 1, + "smtp_server": "test.example.com", + "email_id": "test@example.com", + "password": "password", + "add_signature": 1, + "signature": "\nBest Wishes\nTest Signature", + "enable_auto_reply": 1, + "auto_reply_message": "", + "enable_incoming": 1, + "notify_if_unreplied": 1, + "unreplied_for_mins": 20, + "send_notification_to": "test_unreplied@example.com", + "pop3_server": "pop.test.example.com", + "append_to": "ToDo", + "imap_folder": [{"folder_name": "INBOX", "append_to": "ToDo"}, {"folder_name": "Test Folder", "append_to": "Communication"}], + "track_email_status": 1 + }, + { + "doctype": "ToDo", + "description":"test doctype" + } +] diff --git a/xhiveframework/email/doctype/email_domain/__init__.py b/xhiveframework/email/doctype/email_domain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_domain/email_domain.js b/xhiveframework/email/doctype/email_domain/email_domain.js new file mode 100644 index 0000000..fbf8b84 --- /dev/null +++ b/xhiveframework/email/doctype/email_domain/email_domain.js @@ -0,0 +1,22 @@ +xhiveframework.ui.form.on("Email Domain", { + onload: function (frm) { + if (!frm.doc.__islocal) { + frm.dashboard.clear_headline(); + let msg = __( + "Changing any setting will reflect on all the email accounts associated with this domain." + ); + frm.dashboard.set_headline_alert(msg); + } else { + if (!frm.doc.attachment_limit) { + xhiveframework.call({ + method: "xhiveframework.core.api.file.get_max_file_size", + callback: function (r) { + if (!r.exc) { + frm.set_value("attachment_limit", Number(r.message) / (1024 * 1024)); + } + }, + }); + } + } + }, +}); diff --git a/xhiveframework/email/doctype/email_domain/email_domain.json b/xhiveframework/email/doctype/email_domain/email_domain.json new file mode 100644 index 0000000..5cb4c19 --- /dev/null +++ b/xhiveframework/email/doctype/email_domain/email_domain.json @@ -0,0 +1,157 @@ +{ + "actions": [], + "autoname": "field:domain_name", + "creation": "2016-03-29 10:50:48.848239", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "email_settings", + "domain_name", + "mailbox_settings", + "email_server", + "use_imap", + "use_ssl", + "use_starttls", + "column_break_9", + "incoming_port", + "attachment_limit", + "outgoing_mail_settings", + "smtp_server", + "use_tls", + "use_ssl_for_outgoing", + "column_break_18", + "smtp_port", + "append_emails_to_sent_folder" + ], + "fields": [ + { + "fieldname": "email_settings", + "fieldtype": "Section Break" + }, + { + "fieldname": "domain_name", + "fieldtype": "Data", + "label": "Domain Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "mailbox_settings", + "fieldtype": "Section Break", + "label": "Incoming Settings" + }, + { + "description": "e.g. pop.gmail.com / imap.gmail.com", + "fieldname": "email_server", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Incoming Server", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "use_imap", + "fieldtype": "Check", + "label": "Use IMAP" + }, + { + "default": "0", + "fieldname": "use_ssl", + "fieldtype": "Check", + "label": "Use SSL" + }, + { + "default": "0", + "depends_on": "eval:doc.use_imap && !doc.use_ssl", + "fieldname": "use_starttls", + "fieldtype": "Check", + "label": "Use STARTTLS" + }, + { + "description": "Ignore attachments over this size", + "fieldname": "attachment_limit", + "fieldtype": "Int", + "label": "Attachment Limit (MB)" + }, + { + "fieldname": "outgoing_mail_settings", + "fieldtype": "Section Break", + "label": "Outgoing Settings" + }, + { + "description": "e.g. smtp.gmail.com", + "fieldname": "smtp_server", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Outgoing Server", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "use_tls", + "fieldtype": "Check", + "label": "Use TLS" + }, + { + "description": "If non standard port (e.g. 587)", + "fieldname": "smtp_port", + "fieldtype": "Data", + "label": "Port" + }, + { + "description": "If non-standard port (e.g. POP3: 995/110, IMAP: 993/143)", + "fieldname": "incoming_port", + "fieldtype": "Data", + "label": "Port" + }, + { + "default": "0", + "depends_on": "eval:doc.use_imap", + "fieldname": "append_emails_to_sent_folder", + "fieldtype": "Check", + "label": "Append Emails to Sent Folder" + }, + { + "default": "0", + "fieldname": "use_ssl_for_outgoing", + "fieldtype": "Check", + "label": "Use SSL" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + } + ], + "icon": "icon-inbox", + "links": [ + { + "link_doctype": "Email Account", + "link_fieldname": "domain" + } + ], + "modified": "2023-06-05 12:55:06.434541", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Domain", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/email/doctype/email_domain/email_domain.py b/xhiveframework/email/doctype/email_domain/email_domain.py new file mode 100644 index 0000000..136b9a9 --- /dev/null +++ b/xhiveframework/email/doctype/email_domain/email_domain.py @@ -0,0 +1,124 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import imaplib +import poplib +import smtplib +from functools import wraps + +import xhiveframework +from xhiveframework import _ +from xhiveframework.email.utils import get_port +from xhiveframework.model.document import Document +from xhiveframework.utils import cint + +EMAIL_DOMAIN_FIELDS = [ + "email_server", + "use_imap", + "use_ssl", + "use_starttls", + "use_tls", + "attachment_limit", + "smtp_server", + "smtp_port", + "use_ssl_for_outgoing", + "append_emails_to_sent_folder", + "incoming_port", +] + + +def get_error_message(event): + return { + "incoming": (_("Incoming email account not correct"), _("Error connecting via IMAP/POP3: {e}")), + "outgoing": (_("Outgoing email account not correct"), _("Error connecting via SMTP: {e}")), + }[event] + + +def handle_error(event): + def decorator(fn): + @wraps(fn) + def wrapper(*args, **kwargs): + err_title, err_message = get_error_message(event) + try: + fn(*args, **kwargs) + except Exception as e: + xhiveframework.throw( + title=err_title, + msg=err_message.format(e=e), + ) + + return wrapper + + return decorator + + +class EmailDomain(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + append_emails_to_sent_folder: DF.Check + attachment_limit: DF.Int + domain_name: DF.Data + email_server: DF.Data + incoming_port: DF.Data | None + smtp_port: DF.Data | None + smtp_server: DF.Data + use_imap: DF.Check + use_ssl: DF.Check + use_ssl_for_outgoing: DF.Check + use_starttls: DF.Check + use_tls: DF.Check + + # end: auto-generated types + def validate(self): + """Validate POP3/IMAP and SMTP connections.""" + + if xhiveframework.local.flags.in_patch or xhiveframework.local.flags.in_test or xhiveframework.local.flags.in_install: + return + + self.validate_incoming_server_conn() + self.validate_outgoing_server_conn() + + def on_update(self): + """update all email accounts using this domain""" + for email_account in xhiveframework.get_all("Email Account", filters={"domain": self.name}): + try: + email_account = xhiveframework.get_doc("Email Account", email_account.name) + for attr in EMAIL_DOMAIN_FIELDS: + email_account.set(attr, self.get(attr, default=0)) + email_account.save() + + except Exception as e: + xhiveframework.msgprint( + _("Error has occurred in {0}").format(email_account.name), raise_exception=e.__class__ + ) + + @handle_error("incoming") + def validate_incoming_server_conn(self): + self.incoming_port = get_port(self) + + if self.use_imap: + conn_method = imaplib.IMAP4_SSL if self.use_ssl else imaplib.IMAP4 + else: + conn_method = poplib.POP3_SSL if self.use_ssl else poplib.POP3 + + self.use_starttls = cint(self.use_imap and self.use_starttls and not self.use_ssl) + incoming_conn = conn_method(self.email_server, port=self.incoming_port, timeout=30) + incoming_conn.logout() if self.use_imap else incoming_conn.quit() + + @handle_error("outgoing") + def validate_outgoing_server_conn(self): + conn_method = smtplib.SMTP + + if self.use_ssl_for_outgoing: + self.smtp_port = self.smtp_port or 465 + conn_method = smtplib.SMTP_SSL + elif self.use_tls: + self.smtp_port = self.smtp_port or 587 + + conn_method((self.smtp_server or ""), cint(self.smtp_port), timeout=30).quit() diff --git a/xhiveframework/email/doctype/email_domain/test_email_domain.py b/xhiveframework/email/doctype/email_domain/test_email_domain.py new file mode 100644 index 0000000..1f7ae54 --- /dev/null +++ b/xhiveframework/email/doctype/email_domain/test_email_domain.py @@ -0,0 +1,39 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.test_runner import make_test_objects +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Email Domain") + + +class TestDomain(XhiveFrameworkTestCase): + def setUp(self): + make_test_objects("Email Domain", reset=True) + + def tearDown(self): + xhiveframework.delete_doc("Email Account", "Test") + xhiveframework.delete_doc("Email Domain", "test.com") + + def test_on_update(self): + mail_domain = xhiveframework.get_doc("Email Domain", "test.com") + mail_account = xhiveframework.get_doc("Email Account", "Test") + + # Ensure a different port + mail_account.incoming_port = int(mail_domain.incoming_port) + 5 + mail_account.save() + # Trigger update of accounts using this domain + mail_domain.on_update() + + mail_account.reload() + # After update, incoming_port in account should match the domain + self.assertEqual(mail_account.incoming_port, mail_domain.incoming_port) + + # Also make sure that the other attributes match + self.assertEqual(mail_account.use_imap, mail_domain.use_imap) + self.assertEqual(mail_account.use_ssl, mail_domain.use_ssl) + self.assertEqual(mail_account.use_starttls, mail_domain.use_starttls) + self.assertEqual(mail_account.use_tls, mail_domain.use_tls) + self.assertEqual(mail_account.attachment_limit, mail_domain.attachment_limit) + self.assertEqual(mail_account.smtp_server, mail_domain.smtp_server) + self.assertEqual(mail_account.smtp_port, mail_domain.smtp_port) diff --git a/xhiveframework/email/doctype/email_domain/test_records.json b/xhiveframework/email/doctype/email_domain/test_records.json new file mode 100644 index 0000000..a6ccc99 --- /dev/null +++ b/xhiveframework/email/doctype/email_domain/test_records.json @@ -0,0 +1,32 @@ +[ + { + "doctype": "Email Domain", + "domain_name": "test.com", + "email_id": "_test@test.com", + "email_server": "imap.test.com", + "use_imap": "imap.test.com", + "use_ssl": 1, + "use_tls": 1, + "incoming_port": "993", + "attachment_limit": "1", + "smtp_server": "smtp.test.com", + "smtp_port": "587", + "password": "password" + }, + { + "doctype": "Email Account", + "name": "_Test Email Account 1", + "enable_incoming": 1, + "email_id": "_test@test.com", + "domain": "test.com", + "email_server": "imap.test.com", + "use_imap": 1, + "use_ssl": 0, + "use_tls": 1, + "incoming_port": "143", + "attachment_limit": "1", + "smtp_server": "smtp.test.com", + "smtp_port": "587", + "password": "password" + } +] diff --git a/xhiveframework/email/doctype/email_flag_queue/__init__.py b/xhiveframework/email/doctype/email_flag_queue/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.js b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.js new file mode 100644 index 0000000..9cbf7f6 --- /dev/null +++ b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Flag Queue", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.json b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.json new file mode 100644 index 0000000..14b1ec4 --- /dev/null +++ b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.json @@ -0,0 +1,67 @@ +{ + "actions": [], + "allow_copy": 1, + "creation": "2016-04-20 15:29:39.785172", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "is_completed", + "communication", + "action", + "email_account", + "uid" + ], + "fields": [ + { + "default": "0", + "fieldname": "is_completed", + "fieldtype": "Check", + "label": "Is Completed", + "read_only": 1 + }, + { + "fieldname": "communication", + "fieldtype": "Data", + "label": "Communication" + }, + { + "fieldname": "action", + "fieldtype": "Select", + "label": "Action", + "options": "Read\nUnread" + }, + { + "fieldname": "email_account", + "fieldtype": "Data", + "hidden": 1, + "label": "Email Account" + }, + { + "fieldname": "uid", + "fieldtype": "Data", + "hidden": 1, + "label": "UID" + } + ], + "in_create": 1, + "links": [], + "modified": "2021-11-30 09:51:34.489932", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Flag Queue", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.py b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.py new file mode 100644 index 0000000..3dabf1e --- /dev/null +++ b/xhiveframework/email/doctype/email_flag_queue/email_flag_queue.py @@ -0,0 +1,22 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class EmailFlagQueue(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + action: DF.Literal["Read", "Unread"] + communication: DF.Data | None + email_account: DF.Data | None + is_completed: DF.Check + uid: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/email_flag_queue/test_email_flag_queue.py b/xhiveframework/email/doctype/email_flag_queue/test_email_flag_queue.py new file mode 100644 index 0000000..ea5939d --- /dev/null +++ b/xhiveframework/email/doctype/email_flag_queue/test_email_flag_queue.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Email Flag Queue') + + +class TestEmailFlagQueue(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/email_group/__init__.py b/xhiveframework/email/doctype/email_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_group/email_group.js b/xhiveframework/email/doctype/email_group/email_group.js new file mode 100644 index 0000000..208da0b --- /dev/null +++ b/xhiveframework/email/doctype/email_group/email_group.js @@ -0,0 +1,97 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Group", { + refresh: function (frm) { + if (!frm.is_new()) { + frm.add_custom_button( + __("Import Subscribers"), + function () { + xhiveframework.prompt( + { + fieldtype: "Select", + options: frm.doc.__onload.import_types, + label: __("Import Email From"), + fieldname: "doctype", + reqd: 1, + }, + function (data) { + xhiveframework.call({ + method: "xhiveframework.email.doctype.email_group.email_group.import_from", + args: { + name: frm.doc.name, + doctype: data.doctype, + }, + callback: function (r) { + frm.set_value("total_subscribers", r.message); + }, + }); + }, + __("Import Subscribers"), + __("Import") + ); + }, + __("Action") + ); + + frm.add_custom_button( + __("Add Subscribers"), + function () { + xhiveframework.prompt( + { + fieldtype: "Text", + label: __("Email Addresses"), + fieldname: "email_list", + reqd: 1, + }, + function (data) { + xhiveframework.call({ + method: "xhiveframework.email.doctype.email_group.email_group.add_subscribers", + args: { + name: frm.doc.name, + email_list: data.email_list, + }, + callback: function (r) { + frm.set_value("total_subscribers", r.message); + }, + }); + }, + __("Add Subscribers"), + __("Add") + ); + }, + __("Action") + ); + + frm.add_custom_button( + __("New Newsletter"), + function () { + xhiveframework.route_options = { email_group: frm.doc.name }; + xhiveframework.new_doc("Newsletter"); + }, + __("Action") + ); + } + + frm.trigger("preview_welcome_url"); + }, + welcome_url(frm) { + frm.trigger("preview_welcome_url"); + }, + add_query_parameters: function (frm) { + frm.trigger("preview_welcome_url"); + }, + preview_welcome_url: function (frm) { + if (frm.doc.add_query_parameters && frm.doc.welcome_url) { + frm.call("preview_welcome_url", { email: "mail@example.org" }).then((r) => { + frm.set_df_property( + "add_query_parameters", + "description", + `${__("Preview:")} ${r.message}` + ); + }); + } else { + frm.set_df_property("add_query_parameters", "description", ""); + } + }, +}); diff --git a/xhiveframework/email/doctype/email_group/email_group.json b/xhiveframework/email/doctype/email_group/email_group.json new file mode 100644 index 0000000..1e90bea --- /dev/null +++ b/xhiveframework/email/doctype/email_group/email_group.json @@ -0,0 +1,108 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:title", + "creation": "2015-03-18 06:08:32.729800", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "title", + "column_break_oyyj", + "total_subscribers", + "sign_up_and_confirmation_section", + "confirmation_email_template", + "welcome_email_template", + "welcome_url", + "add_query_parameters" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "no_copy": 1, + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "total_subscribers", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Total Subscribers", + "read_only": 1 + }, + { + "fieldname": "confirmation_email_template", + "fieldtype": "Link", + "label": "Confirmation Email Template", + "options": "Email Template" + }, + { + "fieldname": "welcome_email_template", + "fieldtype": "Link", + "label": "Welcome Email Template", + "options": "Email Template" + }, + { + "fieldname": "column_break_oyyj", + "fieldtype": "Column Break" + }, + { + "fieldname": "sign_up_and_confirmation_section", + "fieldtype": "Section Break", + "label": "Sign Up and Confirmation" + }, + { + "description": "Redirect to this URL after successful confirmation.", + "fieldname": "welcome_url", + "fieldtype": "Data", + "label": "Welcome URL", + "options": "URL" + }, + { + "default": "0", + "depends_on": "welcome_url", + "fieldname": "add_query_parameters", + "fieldtype": "Check", + "label": "Add Query Parameters" + } + ], + "index_web_pages_for_search": 1, + "links": [ + { + "group": "Members", + "link_doctype": "Email Group Member", + "link_fieldname": "email_group" + } + ], + "modified": "2023-11-24 18:35:17.268492", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Group", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Newsletter Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_group/email_group.py b/xhiveframework/email/doctype/email_group/email_group.py new file mode 100755 index 0000000..d8ba044 --- /dev/null +++ b/xhiveframework/email/doctype/email_group/email_group.py @@ -0,0 +1,161 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import contextlib + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import parse_addr, validate_email_address + + +class EmailGroup(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + add_query_parameters: DF.Check + confirmation_email_template: DF.Link | None + title: DF.Data + total_subscribers: DF.Int + welcome_email_template: DF.Link | None + welcome_url: DF.Data | None + + # end: auto-generated types + def onload(self): + singles = [d.name for d in xhiveframework.get_all("DocType", "name", {"issingle": 1})] + self.get("__onload").import_types = [ + {"value": d.parent, "label": f"{d.parent} ({d.label})"} + for d in xhiveframework.get_all("DocField", ("parent", "label"), {"options": "Email"}) + if d.parent not in singles + ] + + def import_from(self, doctype): + """Extract Email Addresses from given doctype and add them to the current list""" + meta = xhiveframework.get_meta(doctype) + email_field = next( + d.fieldname + for d in meta.fields + if d.fieldtype in ("Data", "Small Text", "Text", "Code") and d.options == "Email" + ) + unsubscribed_field = "unsubscribed" if meta.get_field("unsubscribed") else None + added = 0 + + for user in xhiveframework.get_all(doctype, [email_field, unsubscribed_field or "name"]): + with contextlib.suppress(xhiveframework.UniqueValidationError, xhiveframework.InvalidEmailAddressError): + email = parse_addr(user.get(email_field))[1] if user.get(email_field) else None + if email: + xhiveframework.get_doc( + { + "doctype": "Email Group Member", + "email_group": self.name, + "email": email, + "unsubscribed": user.get(unsubscribed_field) if unsubscribed_field else 0, + } + ).insert(ignore_permissions=True) + added += 1 + + xhiveframework.msgprint(_("{0} subscribers added").format(added)) + + return self.update_total_subscribers() + + def update_total_subscribers(self): + self.total_subscribers = self.get_total_subscribers() + self.db_update() + return self.total_subscribers + + def get_total_subscribers(self): + return xhiveframework.db.sql( + """select count(*) from `tabEmail Group Member` + where email_group=%s""", + self.name, + )[0][0] + + @xhiveframework.whitelist() + def preview_welcome_url(self, email: str | None = None) -> str | None: + """Get Welcome URL for the email group.""" + return self.get_welcome_url(email) + + def get_welcome_url(self, email: str | None = None) -> str | None: + """Get Welcome URL for the email group.""" + if not self.welcome_url: + return None + + return ( + add_query_params(self.welcome_url, {"email": email, "email_group": self.name}) + if self.add_query_parameters + else self.welcome_url + ) + + def on_trash(self): + for d in xhiveframework.get_all("Email Group Member", "name", {"email_group": self.name}): + xhiveframework.delete_doc("Email Group Member", d.name) + + +@xhiveframework.whitelist() +def import_from(name, doctype): + nlist = xhiveframework.get_doc("Email Group", name) + if nlist.has_permission("write"): + return nlist.import_from(doctype) + + +@xhiveframework.whitelist() +def add_subscribers(name, email_list): + if not isinstance(email_list, list | tuple): + email_list = email_list.replace(",", "\n").split("\n") + + template = xhiveframework.db.get_value("Email Group", name, "welcome_email_template") + welcome_email = xhiveframework.get_doc("Email Template", template) if template else None + + count = 0 + for email in email_list: + email = email.strip() + parsed_email = validate_email_address(email, False) + + if parsed_email: + if not xhiveframework.db.get_value("Email Group Member", {"email_group": name, "email": parsed_email}): + xhiveframework.get_doc( + {"doctype": "Email Group Member", "email_group": name, "email": parsed_email} + ).insert(ignore_permissions=xhiveframework.flags.ignore_permissions) + + send_welcome_email(welcome_email, parsed_email, name) + + count += 1 + else: + pass + else: + xhiveframework.msgprint(_("{0} is not a valid Email Address").format(email)) + + xhiveframework.msgprint(_("{0} subscribers added").format(count)) + + return xhiveframework.get_doc("Email Group", name).update_total_subscribers() + + +def send_welcome_email(welcome_email, email, email_group): + """Send welcome email for the subscribers of a given email group.""" + if not welcome_email: + return + + args = dict(email=email, email_group=email_group) + message = xhiveframework.render_template(welcome_email.response_, args) + xhiveframework.sendmail(email, subject=welcome_email.subject, message=message) + + +def add_query_params(url: str, params: dict) -> str: + from urllib.parse import urlencode, urlparse, urlunparse + + if not params: + return url + + query_string = urlencode(params) + parsed = list(urlparse(url)) + if parsed[4]: + parsed[4] += f"&{query_string}" + else: + parsed[4] = query_string + + return urlunparse(parsed) diff --git a/xhiveframework/email/doctype/email_group/test_email_group.py b/xhiveframework/email/doctype/email_group/test_email_group.py new file mode 100644 index 0000000..5f6cf19 --- /dev/null +++ b/xhiveframework/email/doctype/email_group/test_email_group.py @@ -0,0 +1,32 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import validate_url + +# test_records = xhiveframework.get_test_records('Email Group') + + +class TestEmailGroup(XhiveFrameworkTestCase): + def test_welcome_url(self): + email_group = xhiveframework.new_doc("Email Group") + email_group.title = "Test" + email_group.welcome_url = "http://example.com/welcome?hello=world" + email_group.add_query_parameters = 1 + email_group.insert() + + welcome_url = email_group.get_welcome_url("mail@example.org") + self.assertTrue(validate_url(welcome_url)) + self.assertIn(email_group.welcome_url, welcome_url) + self.assertIn("email_group=Test", welcome_url) + self.assertIn("email=mail%40example.org", welcome_url) + + email_group.add_query_parameters = 0 + welcome_url = email_group.get_welcome_url("mail@example.org") + self.assertTrue(validate_url(welcome_url)) + self.assertIn(email_group.welcome_url, welcome_url) + self.assertNotIn("email_group=Test", welcome_url) + self.assertNotIn("email=mail%40example.org", welcome_url) + + email_group.welcome_url = "" + self.assertEqual(email_group.get_welcome_url(), None) diff --git a/xhiveframework/email/doctype/email_group/test_records.json b/xhiveframework/email/doctype/email_group/test_records.json new file mode 100644 index 0000000..a55b117 --- /dev/null +++ b/xhiveframework/email/doctype/email_group/test_records.json @@ -0,0 +1,6 @@ +[ + { + "doctype": "Email Group", + "title": "_Test Email Group" + } +] diff --git a/xhiveframework/email/doctype/email_group_member/__init__.py b/xhiveframework/email/doctype/email_group_member/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_group_member/email_group_member.js b/xhiveframework/email/doctype/email_group_member/email_group_member.js new file mode 100644 index 0000000..2bb992e --- /dev/null +++ b/xhiveframework/email/doctype/email_group_member/email_group_member.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Group Member", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/email/doctype/email_group_member/email_group_member.json b/xhiveframework/email/doctype/email_group_member/email_group_member.json new file mode 100644 index 0000000..0d68674 --- /dev/null +++ b/xhiveframework/email/doctype/email_group_member/email_group_member.json @@ -0,0 +1,71 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "hash", + "creation": "2015-03-18 06:15:59.321619", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "email_group", + "email", + "unsubscribed" + ], + "fields": [ + { + "fieldname": "email_group", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Email Group", + "options": "Email Group", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "email", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Email", + "options": "Email", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "unsubscribed", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Unsubscribed", + "search_index": 1 + } + ], + "links": [], + "modified": "2023-11-25 16:54:59.828669", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Group Member", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Newsletter Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "email", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_group_member/email_group_member.py b/xhiveframework/email/doctype/email_group_member/email_group_member.py new file mode 100644 index 0000000..f1d9076 --- /dev/null +++ b/xhiveframework/email/doctype/email_group_member/email_group_member.py @@ -0,0 +1,32 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class EmailGroupMember(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email: DF.Data + email_group: DF.Link + unsubscribed: DF.Check + + # end: auto-generated types + def after_delete(self): + email_group = xhiveframework.get_doc("Email Group", self.email_group) + email_group.update_total_subscribers() + + def after_insert(self): + email_group = xhiveframework.get_doc("Email Group", self.email_group) + email_group.update_total_subscribers() + + +def after_doctype_insert(): + xhiveframework.db.add_unique("Email Group Member", ("email_group", "email")) diff --git a/xhiveframework/email/doctype/email_group_member/test_email_group_member.py b/xhiveframework/email/doctype/email_group_member/test_email_group_member.py new file mode 100644 index 0000000..f4a1c40 --- /dev/null +++ b/xhiveframework/email/doctype/email_group_member/test_email_group_member.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Email Group Member') + + +class TestEmailGroupMember(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/email_queue/__init__.py b/xhiveframework/email/doctype/email_queue/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_queue/email_queue.js b/xhiveframework/email/doctype/email_queue/email_queue.js new file mode 100644 index 0000000..ba8d1ee --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/email_queue.js @@ -0,0 +1,34 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Queue", { + refresh: function (frm) { + if (["Not Sent", "Partially Sent"].includes(frm.doc.status)) { + let button = frm.add_custom_button("Send Now", function () { + xhiveframework.call({ + method: "xhiveframework.email.doctype.email_queue.email_queue.send_now", + args: { + name: frm.doc.name, + }, + btn: button, + callback: function () { + frm.reload_doc(); + }, + }); + }); + } else if (frm.doc.status == "Error") { + frm.add_custom_button("Retry Sending", function () { + frm.call({ + method: "retry_sending", + doc: frm.doc, + args: { + name: frm.doc.name, + }, + callback: function () { + frm.reload_doc(); + }, + }); + }); + } + }, +}); diff --git a/xhiveframework/email/doctype/email_queue/email_queue.json b/xhiveframework/email/doctype/email_queue/email_queue.json new file mode 100644 index 0000000..ea1389d --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/email_queue.json @@ -0,0 +1,177 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2012-08-02 15:17:28", + "description": "Email Queue records.", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "sender", + "recipients", + "show_as_cc", + "message", + "status", + "error", + "message_id", + "reference_doctype", + "reference_name", + "communication", + "send_after", + "priority", + "add_unsubscribe_link", + "unsubscribe_param", + "unsubscribe_method", + "expose_recipients", + "attachments", + "retry", + "email_account" + ], + "fields": [ + { + "fieldname": "sender", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "label": "Sender", + "options": "Email" + }, + { + "fieldname": "recipients", + "fieldtype": "Table", + "label": "Recipient", + "options": "Email Queue Recipient" + }, + { + "fieldname": "show_as_cc", + "fieldtype": "Small Text", + "label": "Show as cc" + }, + { + "fieldname": "message", + "fieldtype": "Code", + "label": "Message" + }, + { + "default": "Not Sent", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Not Sent\nSending\nSent\nPartially Sent\nError" + }, + { + "depends_on": "eval:doc.error", + "fieldname": "error", + "fieldtype": "Code", + "label": "Error" + }, + { + "fieldname": "message_id", + "fieldtype": "Small Text", + "label": "Message ID", + "read_only": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference DocName", + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "communication", + "fieldtype": "Link", + "label": "Communication", + "options": "Communication", + "search_index": 1 + }, + { + "fieldname": "send_after", + "fieldtype": "Datetime", + "label": "Send After", + "no_copy": 1, + "read_only": 1 + }, + { + "default": "1", + "fieldname": "priority", + "fieldtype": "Int", + "label": "Priority", + "read_only": 1 + }, + { + "default": "1", + "fieldname": "add_unsubscribe_link", + "fieldtype": "Check", + "label": "Add Unsubscribe Link" + }, + { + "fieldname": "unsubscribe_param", + "fieldtype": "Data", + "label": "Unsubscribe Param", + "read_only": 1 + }, + { + "fieldname": "unsubscribe_method", + "fieldtype": "Data", + "label": "Unsubscribe Method" + }, + { + "fieldname": "expose_recipients", + "fieldtype": "Data", + "label": "Expose Recipients" + }, + { + "fieldname": "attachments", + "fieldtype": "Code", + "label": "Attachments", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "retry", + "fieldtype": "Int", + "label": "Retry", + "read_only": 1 + }, + { + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account" + } + ], + "icon": "fa fa-envelope", + "idx": 1, + "in_create": 1, + "links": [], + "modified": "2023-06-09 14:31:52.789186", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Queue", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_queue/email_queue.py b/xhiveframework/email/doctype/email_queue/email_queue.py new file mode 100644 index 0000000..45de3e0 --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/email_queue.py @@ -0,0 +1,803 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json +import quopri +import traceback +from contextlib import suppress +from email.parser import Parser +from email.policy import SMTP + +import xhiveframework +from xhiveframework import _, safe_encode, task +from xhiveframework.core.utils import html2text +from xhiveframework.database.database import savepoint +from xhiveframework.email.doctype.email_account.email_account import EmailAccount +from xhiveframework.email.email_body import add_attachment, get_email, get_formatted_html +from xhiveframework.email.queue import get_unsubcribed_url, get_unsubscribe_message +from xhiveframework.email.smtp import SMTPServer +from xhiveframework.model.document import Document +from xhiveframework.query_builder import DocType, Interval +from xhiveframework.query_builder.functions import Now +from xhiveframework.utils import ( + add_days, + cint, + cstr, + get_hook_method, + get_string_between, + get_url, + now, + nowdate, + sbool, + split_emails, +) +from xhiveframework.utils.deprecations import deprecated +from xhiveframework.utils.verified_command import get_signed_params + + +class EmailQueue(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.email.doctype.email_queue_recipient.email_queue_recipient import EmailQueueRecipient + from xhiveframework.types import DF + + add_unsubscribe_link: DF.Check + attachments: DF.Code | None + communication: DF.Link | None + email_account: DF.Link | None + error: DF.Code | None + expose_recipients: DF.Data | None + message: DF.Code | None + message_id: DF.SmallText | None + priority: DF.Int + recipients: DF.Table[EmailQueueRecipient] + reference_doctype: DF.Link | None + reference_name: DF.Data | None + retry: DF.Int + send_after: DF.Datetime | None + sender: DF.Data | None + show_as_cc: DF.SmallText | None + status: DF.Literal["Not Sent", "Sending", "Sent", "Partially Sent", "Error"] + unsubscribe_method: DF.Data | None + unsubscribe_param: DF.Data | None + # end: auto-generated types + DOCTYPE = "Email Queue" + + def set_recipients(self, recipients): + self.set("recipients", []) + for r in recipients: + self.append("recipients", {"recipient": r.strip(), "status": "Not Sent"}) + + def on_trash(self): + self.prevent_email_queue_delete() + + def prevent_email_queue_delete(self): + if xhiveframework.session.user != "Administrator": + xhiveframework.throw(_("Only Administrator can delete Email Queue")) + + def get_duplicate(self, recipients): + values = self.as_dict() + del values["name"] + duplicate = xhiveframework.get_doc(values) + duplicate.set_recipients(recipients) + return duplicate + + @classmethod + def new(cls, doc_data, ignore_permissions=False) -> "EmailQueue": + data = doc_data.copy() + if not data.get("recipients"): + return + + recipients = data.pop("recipients") + doc = xhiveframework.new_doc(cls.DOCTYPE) + doc.update(data) + doc.set_recipients(recipients) + doc.insert(ignore_permissions=ignore_permissions) + return doc + + @classmethod + def find(cls, name) -> "EmailQueue": + return xhiveframework.get_doc(cls.DOCTYPE, name) + + @classmethod + def find_one_by_filters(cls, **kwargs): + name = xhiveframework.db.get_value(cls.DOCTYPE, kwargs) + return cls.find(name) if name else None + + def update_db(self, commit=False, **kwargs): + xhiveframework.db.set_value(self.DOCTYPE, self.name, kwargs) + if commit: + xhiveframework.db.commit() + + def update_status(self, status, commit=False, **kwargs): + self.update_db(status=status, commit=commit, **kwargs) + if self.communication: + communication_doc = xhiveframework.get_doc("Communication", self.communication) + communication_doc.set_delivery_status(commit=commit) + + @property + def cc(self): + return (self.show_as_cc and self.show_as_cc.split(",")) or [] + + @property + def to(self): + return [r.recipient for r in self.recipients if r.recipient not in self.cc] + + @property + def attachments_list(self): + return json.loads(self.attachments) if self.attachments else [] + + def get_email_account(self, raise_error=False): + if self.email_account: + return xhiveframework.get_cached_doc("Email Account", self.email_account) + + return EmailAccount.find_outgoing( + match_by_email=self.sender, match_by_doctype=self.reference_doctype, _raise_error=raise_error + ) + + def is_to_be_sent(self): + return self.status in ["Not Sent", "Partially Sent"] + + def can_send_now(self): + if ( + xhiveframework.are_emails_muted() + or not self.is_to_be_sent() + or cint(xhiveframework.db.get_default("suspend_email_queue")) == 1 + ): + return False + + return True + + def send(self, smtp_server_instance: SMTPServer = None): + """Send emails to recipients.""" + if not self.can_send_now(): + return + + with SendMailContext(self, smtp_server_instance) as ctx: + ctx.fetch_smtp_server() + message = None + for recipient in self.recipients: + if recipient.is_mail_sent(): + continue + + message = ctx.build_message(recipient.recipient) + if method := get_hook_method("override_email_send"): + method(self, self.sender, recipient.recipient, message) + else: + if not xhiveframework.flags.in_test or xhiveframework.flags.testing_email: + ctx.smtp_server.session.sendmail( + from_addr=self.sender, + to_addrs=recipient.recipient, + msg=message.decode("utf-8").encode(), + ) + + ctx.update_recipient_status_to_sent(recipient) + + if xhiveframework.flags.in_test and not xhiveframework.flags.testing_email: + xhiveframework.flags.sent_mail = message + return + + if ctx.email_account_doc.append_emails_to_sent_folder: + ctx.email_account_doc.append_email_to_sent_folder(message) + + @staticmethod + def clear_old_logs(days=30): + """Remove low priority older than 31 days in Outbox or configured in Log Settings. + Note: Used separate query to avoid deadlock + """ + days = days or 31 + email_queue = xhiveframework.qb.DocType("Email Queue") + email_recipient = xhiveframework.qb.DocType("Email Queue Recipient") + + # Delete queue table + ( + xhiveframework.qb.from_(email_queue).delete().where(email_queue.modified < (Now() - Interval(days=days))) + ).run() + + # delete child tables, note that this has potential to leave some orphan + # child table behind if modified time was later than parent doc (rare). + # But it's safe since child table doesn't contain links. + ( + xhiveframework.qb.from_(email_recipient) + .delete() + .where(email_recipient.modified < (Now() - Interval(days=days))) + ).run() + + @xhiveframework.whitelist() + def retry_sending(self): + if self.status == "Error": + self.status = "Not Sent" + self.save(ignore_permissions=True) + + +@task(queue="short") +@deprecated +def send_mail(email_queue_name, smtp_server_instance: SMTPServer = None): + """This is equivalent to EmailQueue.send. + + This provides a way to make sending mail as a background job. + """ + record = EmailQueue.find(email_queue_name) + record.send(smtp_server_instance=smtp_server_instance) + + +class SendMailContext: + def __init__( + self, + queue_doc: Document, + smtp_server_instance: SMTPServer = None, + ): + self.queue_doc: EmailQueue = queue_doc + self.smtp_server: SMTPServer = smtp_server_instance + self.sent_to_atleast_one_recipient = any( + rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent() + ) + + def fetch_smtp_server(self): + self.email_account_doc = self.queue_doc.get_email_account(raise_error=True) + if not self.smtp_server: + self.smtp_server = self.email_account_doc.get_smtp_server() + + def __enter__(self): + self.queue_doc.update_status(status="Sending", commit=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type: + update_fields = {"error": "".join(traceback.format_tb(exc_tb))} + if self.queue_doc.retry < get_email_retry_limit(): + update_fields.update( + { + "status": "Partially Sent" if self.sent_to_atleast_one_recipient else "Not Sent", + "retry": self.queue_doc.retry + 1, + } + ) + else: + update_fields.update({"status": "Error"}) + self.notify_failed_email() + else: + update_fields = {"status": "Sent"} + + self.queue_doc.update_status(**update_fields, commit=True) + + @savepoint(catch=Exception) + def notify_failed_email(self): + # Parse the email body to extract the subject + subject = Parser(policy=SMTP).parsestr(self.queue_doc.message)["Subject"] + + # Construct the notification + notification = xhiveframework.new_doc("Notification Log") + notification.for_user = self.queue_doc.owner + notification.set("type", "Alert") + notification.from_user = self.queue_doc.owner + notification.document_type = self.queue_doc.doctype + notification.document_name = self.queue_doc.name + notification.subject = _("Failed to send email with subject:") + f" {subject}" + notification.insert() + + def update_recipient_status_to_sent(self, recipient): + self.sent_to_atleast_one_recipient = True + recipient.update_db(status="Sent", commit=True) + + def get_message_object(self, message): + return Parser(policy=SMTP).parsestr(message) + + def message_placeholder(self, placeholder_key): + # sourcery skip: avoid-builtin-shadow + map = { + "tracker": "", + "unsubscribe_url": "", + "cc": "", + "recipient": "", + } + return map.get(placeholder_key) + + def build_message(self, recipient_email) -> bytes: + """Build message specific to the recipient.""" + message = self.queue_doc.message + + if not message: + return "" + + message = message.replace(self.message_placeholder("tracker"), self.get_tracker_str(recipient_email)) + message = message.replace( + self.message_placeholder("unsubscribe_url"), self.get_unsubscribe_str(recipient_email) + ) + message = message.replace(self.message_placeholder("cc"), self.get_receivers_str()) + message = message.replace( + self.message_placeholder("recipient"), self.get_recipient_str(recipient_email) + ) + message = self.include_attachments(message) + return message + + def get_tracker_str(self, recipient_email) -> str: + tracker_url = "" + if self.queue_doc.get("email_read_tracker_url"): + email_read_tracker_url = self.queue_doc.email_read_tracker_url + params = { + "recipient_email": recipient_email, + "reference_name": self.queue_doc.reference_name, + "reference_doctype": self.queue_doc.reference_doctype, + } + tracker_url = get_url(f"{email_read_tracker_url}?{get_signed_params(params)}") + + elif xhiveframework.conf.use_ssl and self.email_account_doc.track_email_status: + tracker_url = f"{get_url()}/api/method/xhiveframework.core.doctype.communication.email.mark_email_as_seen?name={self.queue_doc.communication}" + + if tracker_url: + tracker_url_html = f'' + return quopri.encodestring(tracker_url_html.encode()).decode() + + return "" + + def get_unsubscribe_str(self, recipient_email: str) -> str: + unsubscribe_url = "" + + if self.queue_doc.add_unsubscribe_link and self.queue_doc.reference_doctype: + unsubscribe_url = get_unsubcribed_url( + reference_doctype=self.queue_doc.reference_doctype, + reference_name=self.queue_doc.reference_name, + email=recipient_email, + unsubscribe_method=self.queue_doc.unsubscribe_method, + unsubscribe_params=self.queue_doc.unsubscribe_param, + ) + + return quopri.encodestring(unsubscribe_url.encode()).decode() + + def get_receivers_str(self): + message = "" + if self.queue_doc.expose_recipients == "footer": + to_str = ", ".join(self.queue_doc.to) + cc_str = ", ".join(self.queue_doc.cc) + message = f"This email was sent to {to_str}" + message = f"{message} and copied to {cc_str}" if cc_str else message + return message + + def get_recipient_str(self, recipient_email): + return recipient_email if self.queue_doc.expose_recipients != "header" else "" + + def include_attachments(self, message): + message_obj = self.get_message_object(message) + attachments = self.queue_doc.attachments_list + + for attachment in attachments: + if attachment.get("fcontent"): + continue + + file_filters = {} + if attachment.get("fid"): + file_filters["name"] = attachment.get("fid") + elif attachment.get("file_url"): + file_filters["file_url"] = attachment.get("file_url") + + if file_filters: + _file = xhiveframework.get_doc("File", file_filters) + fcontent = _file.get_content() + attachment.update({"fname": _file.file_name, "fcontent": fcontent, "parent": message_obj}) + attachment.pop("fid", None) + attachment.pop("file_url", None) + add_attachment(**attachment) + + elif attachment.get("print_format_attachment") == 1: + attachment.pop("print_format_attachment", None) + print_format_file = xhiveframework.attach_print(**attachment) + self._store_file(print_format_file["fname"], print_format_file["fcontent"]) + print_format_file.update({"parent": message_obj}) + add_attachment(**print_format_file) + + return safe_encode(message_obj.as_string()) + + def _store_file(self, file_name, content): + if not xhiveframework.get_system_settings("store_attached_pdf_document"): + return + + file_data = xhiveframework._dict(file_name=file_name, is_private=1) + + # Store on communication if available, else email queue doc + if self.queue_doc.communication: + file_data.attached_to_doctype = "Communication" + file_data.attached_to_name = self.queue_doc.communication + else: + file_data.attached_to_doctype = self.queue_doc.doctype + file_data.attached_to_name = self.queue_doc.name + + if xhiveframework.db.exists("File", file_data): + return + + file = xhiveframework.new_doc("File", **file_data) + file.content = content + file.insert() + + +@xhiveframework.whitelist() +def bulk_retry(queues): + xhiveframework.only_for("System Manager") + + if isinstance(queues, str): + queues = json.loads(queues) + + if not queues: + return + + xhiveframework.msgprint( + _("Updating Email Queue Statuses. The emails will be picked up in the next scheduled run."), + _("Processing..."), + ) + + email_queue = xhiveframework.qb.DocType("Email Queue") + xhiveframework.qb.update(email_queue).set(email_queue.status, "Not Sent").set(email_queue.modified, now()).set( + email_queue.modified_by, xhiveframework.session.user + ).where(email_queue.name.isin(queues) & email_queue.status == "Error").run() + + +@xhiveframework.whitelist() +def send_now(name): + record = EmailQueue.find(name) + if record: + record.check_permission() + record.send() + + +@xhiveframework.whitelist() +def toggle_sending(enable): + xhiveframework.only_for("System Manager") + xhiveframework.db.set_default("suspend_email_queue", 0 if sbool(enable) else 1) + + +def on_doctype_update(): + """Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" + xhiveframework.db.add_index("Email Queue", ("status", "send_after", "priority", "creation"), "index_bulk_flush") + + xhiveframework.db.add_index("Email Queue", ["message_id(140)"]) + + +def get_email_retry_limit(): + return cint(xhiveframework.db.get_system_setting("email_retry_limit")) or 3 + + +class QueueBuilder: + """Builds Email Queue from the given data""" + + def __init__( + self, + recipients=None, + sender=None, + subject=None, + message=None, + text_content=None, + reference_doctype=None, + reference_name=None, + unsubscribe_method=None, + unsubscribe_params=None, + unsubscribe_message=None, + attachments=None, + reply_to=None, + cc=None, + bcc=None, + message_id=None, + in_reply_to=None, + send_after=None, + expose_recipients=None, + send_priority=1, + communication=None, + read_receipt=None, + queue_separately=False, + is_notification=False, + add_unsubscribe_link=1, + inline_images=None, + header=None, + print_letterhead=False, + with_container=False, + email_read_tracker_url=None, + ): + """Add email to sending queue (Email Queue) + + :param recipients: List of recipients. + :param sender: Email sender. + :param subject: Email subject. + :param message: Email message. + :param text_content: Text version of email message. + :param reference_doctype: Reference DocType of caller document. + :param reference_name: Reference name of caller document. + :param send_priority: Priority for Email Queue, default 1. + :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/xhiveframework.email.queue.unsubscribe`. + :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email + :param attachments: Attachments to be sent. + :param reply_to: Reply to be captured here (default inbox) + :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. + :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. + :param communication: Communication link to be set in Email Queue record + :param queue_separately: Queue each email separately + :param is_notification: Marks email as notification so will not trigger notifications from system + :param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1. + :param inline_images: List of inline images as {"filename", "filecontent"}. All src properties will be replaced with random Content-Id + :param header: Append header in email (boolean) + :param with_container: Wraps email inside styled container + :param email_read_tracker_url: A URL for tracking whether an email is read by the recipient. + """ + + self._unsubscribe_method = unsubscribe_method + self._recipients = recipients + self._cc = cc + self._bcc = bcc + self._send_after = send_after + self._sender = sender + self._text_content = text_content + self._message = message + self._add_unsubscribe_link = add_unsubscribe_link + self._unsubscribe_message = unsubscribe_message + self._attachments = attachments + + self._unsubscribed_user_emails = None + self._email_account = None + + self.unsubscribe_params = unsubscribe_params + self.subject = subject + self.reference_doctype = reference_doctype + self.reference_name = reference_name + self.expose_recipients = expose_recipients + self.with_container = with_container + self.header = header + self.reply_to = reply_to + self.message_id = message_id + self.in_reply_to = in_reply_to + self.send_priority = send_priority + self.communication = communication + self.read_receipt = read_receipt + self.queue_separately = queue_separately + self.is_notification = is_notification + self.inline_images = inline_images + self.print_letterhead = print_letterhead + self.email_read_tracker_url = email_read_tracker_url + + @property + def unsubscribe_method(self): + return self._unsubscribe_method or "/api/method/xhiveframework.email.queue.unsubscribe" + + def _get_emails_list(self, emails=None): + emails = split_emails(emails) if isinstance(emails, str) else (emails or []) + return [each for each in set(emails) if each] + + @property + def recipients(self): + return self._get_emails_list(self._recipients) + + @property + def cc(self): + return self._get_emails_list(self._cc) + + @property + def bcc(self): + return self._get_emails_list(self._bcc) + + @property + def send_after(self): + if isinstance(self._send_after, int): + return add_days(nowdate(), self._send_after) + return self._send_after + + @property + def sender(self): + if not self._sender or self._sender == "Administrator": + email_account = self.get_outgoing_email_account() + return email_account.default_sender + return self._sender + + def email_text_content(self): + unsubscribe_msg = self.unsubscribe_message() + unsubscribe_text_message = (unsubscribe_msg and unsubscribe_msg.text) or "" + + if self._text_content: + return self._text_content + unsubscribe_text_message + + try: + text_content = html2text(self._message) + except Exception: + text_content = "See html attachment" + return text_content + unsubscribe_text_message + + def email_html_content(self): + email_account = self.get_outgoing_email_account() + return get_formatted_html( + self.subject, + self._message, + header=self.header, + email_account=email_account, + unsubscribe_link=self.unsubscribe_message(), + with_container=self.with_container, + ) + + def should_include_unsubscribe_link(self): + return ( + self._add_unsubscribe_link == 1 + and self.reference_doctype + and (self._unsubscribe_message or self.reference_doctype == "Newsletter") + ) + + def unsubscribe_message(self): + if self.should_include_unsubscribe_link(): + return get_unsubscribe_message(self._unsubscribe_message, self.expose_recipients) + + def get_outgoing_email_account(self): + if self._email_account: + return self._email_account + + self._email_account = EmailAccount.find_outgoing( + match_by_doctype=self.reference_doctype, match_by_email=self._sender, _raise_error=True + ) + return self._email_account + + def get_unsubscribed_user_emails(self): + if self._unsubscribed_user_emails is not None: + return self._unsubscribed_user_emails + + all_ids = list(set(self.recipients + self.cc)) + + EmailUnsubscribe = DocType("Email Unsubscribe") + + if len(all_ids) > 0: + unsubscribed = ( + xhiveframework.qb.from_(EmailUnsubscribe) + .select(EmailUnsubscribe.email) + .where( + EmailUnsubscribe.email.isin(all_ids) + & ( + ( + (EmailUnsubscribe.reference_doctype == self.reference_doctype) + & (EmailUnsubscribe.reference_name == self.reference_name) + ) + | (EmailUnsubscribe.global_unsubscribe == 1) + ) + ) + .distinct() + ).run(pluck=True) + else: + unsubscribed = None + + self._unsubscribed_user_emails = unsubscribed or [] + return self._unsubscribed_user_emails + + def final_recipients(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.recipients if mail_id not in unsubscribed_emails] + + def final_cc(self): + unsubscribed_emails = self.get_unsubscribed_user_emails() + return [mail_id for mail_id in self.cc if mail_id not in unsubscribed_emails] + + def get_attachments(self): + attachments = [] + if self._attachments: + # store attachments with fid or print format details, to be attached on-demand later + for att in self._attachments: + if att.get("fid") or att.get("file_url"): + attachments.append(att) + elif att.get("print_format_attachment") == 1: + if not att.get("lang", None): + att["lang"] = xhiveframework.local.lang + att["print_letterhead"] = self.print_letterhead + attachments.append(att) + return attachments + + def prepare_email_content(self): + mail = get_email( + recipients=self.final_recipients(), + sender=self.sender, + subject=self.subject, + formatted=self.email_html_content(), + text_content=self.email_text_content(), + attachments=self._attachments, + reply_to=self.reply_to, + cc=self.final_cc(), + bcc=self.bcc, + email_account=self.get_outgoing_email_account(), + expose_recipients=self.expose_recipients, + inline_images=self.inline_images, + header=self.header, + ) + + mail.set_message_id(self.message_id, self.is_notification) + if self.read_receipt: + mail.msg_root["Disposition-Notification-To"] = self.sender + if self.in_reply_to: + mail.set_in_reply_to(self.in_reply_to) + return mail + + def process(self, send_now=False) -> EmailQueue | None: + """Build and return the email queues those are created. + + Sends email incase if it is requested to send now. + """ + final_recipients = self.final_recipients() + queue_separately = (final_recipients and self.queue_separately) or len(final_recipients) > 100 + if not (final_recipients + self.final_cc()): + return [] + + queue_data = self.as_dict(include_recipients=False) + if not queue_data: + return [] + + if not queue_separately: + recipients = list(set(final_recipients + self.final_cc() + self.bcc)) + q = EmailQueue.new({**queue_data, **{"recipients": recipients}}, ignore_permissions=True) + send_now and q.send() + return q + else: + if send_now and len(final_recipients) >= 1000: + # force queueing if there are too many recipients to avoid timeouts + send_now = False + for recipients in xhiveframework.utils.create_batch(final_recipients, 1000): + xhiveframework.enqueue( + self.send_emails, + queue_data=queue_data, + final_recipients=recipients, + job_name=xhiveframework.utils.get_job_name( + "send_bulk_emails_for", self.reference_doctype, self.reference_name + ), + now=xhiveframework.flags.in_test or send_now, + queue="long", + ) + + def send_emails(self, queue_data, final_recipients): + # This is used to bulk send emails from same sender to multiple recipients separately + # This re-uses smtp server instance to minimize the cost of new session creation + smtp_server_instance = None + for r in final_recipients: + recipients = list(set([r, *self.final_cc(), *self.bcc])) + q = EmailQueue.new({**queue_data, **{"recipients": recipients}}, ignore_permissions=True) + if not smtp_server_instance: + email_account = q.get_email_account(raise_error=True) + smtp_server_instance = email_account.get_smtp_server() + + with suppress(Exception): + q.send(smtp_server_instance=smtp_server_instance) + + smtp_server_instance.quit() + + def as_dict(self, include_recipients=True): + email_account = self.get_outgoing_email_account() + email_account_name = email_account and email_account.is_exists_in_db() and email_account.name + + mail = self.prepare_email_content() + try: + mail_to_string = cstr(mail.as_string()) + except xhiveframework.InvalidEmailAddressError: + # bad Email Address - don't add to queue + xhiveframework.log_error( + title="Invalid email address", + message="Invalid email address Sender: {}, Recipients: {}, \nTraceback: {} ".format( + self.sender, ", ".join(self.final_recipients()), traceback.format_exc() + ), + reference_doctype=self.reference_doctype, + reference_name=self.reference_name, + ) + return + + d = { + "priority": self.send_priority, + "attachments": json.dumps(self.get_attachments()), + "message_id": get_string_between("<", mail.msg_root["Message-Id"], ">"), + "message": mail_to_string, + "sender": mail.sender, + "reference_doctype": self.reference_doctype, + "reference_name": self.reference_name, + "add_unsubscribe_link": self._add_unsubscribe_link, + "unsubscribe_method": self.unsubscribe_method, + "unsubscribe_params": self.unsubscribe_params, + "expose_recipients": self.expose_recipients, + "communication": self.communication, + "send_after": self.send_after, + "show_as_cc": ",".join(self.final_cc()), + "show_as_bcc": ",".join(self.bcc), + "email_account": email_account_name or None, + "email_read_tracker_url": self.email_read_tracker_url, + } + + if include_recipients: + d["recipients"] = self.final_recipients() + + return d diff --git a/xhiveframework/email/doctype/email_queue/email_queue_list.js b/xhiveframework/email/doctype/email_queue/email_queue_list.js new file mode 100644 index 0000000..b62498a --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/email_queue_list.js @@ -0,0 +1,60 @@ +xhiveframework.listview_settings["Email Queue"] = { + get_indicator: function (doc) { + var colour = { + Sent: "green", + Sending: "blue", + "Not Sent": "grey", + Error: "red", + Expired: "orange", + }; + return [__(doc.status), colour[doc.status], "status,=," + doc.status]; + }, + refresh: function (listview) { + show_toggle_sending_button(listview); + add_bulk_retry_button_to_actions(listview); + }, + onload: function (list_view) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(list_view.doctype); + }); + }, +}; + +function show_toggle_sending_button(list_view) { + if (!has_common(xhiveframework.user_roles, ["Administrator", "System Manager"])) return; + + const sending_disabled = cint(xhiveframework.sys_defaults.suspend_email_queue); + const label = sending_disabled ? __("Resume Sending") : __("Suspend Sending"); + + list_view.page.add_inner_button(label, async () => { + await xhiveframework.xcall( + "xhiveframework.email.doctype.email_queue.email_queue.toggle_sending", + + // enable if disabled + { enable: sending_disabled } + ); + + // set new value for suspend_email_queue in sys_defaults + xhiveframework.sys_defaults.suspend_email_queue = sending_disabled ? 0 : 1; + + // clear the button and show one with the opposite label + list_view.page.remove_inner_button(label); + show_toggle_sending_button(list_view); + }); +} + +function add_bulk_retry_button_to_actions(list_view) { + list_view.page.add_actions_menu_item(__("Retry Sending"), () => { + xhiveframework.call({ + method: "xhiveframework.email.doctype.email_queue.email_queue.bulk_retry", + args: { + queues: list_view.get_checked_items(true), + }, + callback: (r) => { + if (!r.exc) { + list_view.refresh(); + } + }, + }); + }); +} diff --git a/xhiveframework/email/doctype/email_queue/patches/__init__.py b/xhiveframework/email/doctype/email_queue/patches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_queue/patches/drop_search_index_on_message_id.py b/xhiveframework/email/doctype/email_queue/patches/drop_search_index_on_message_id.py new file mode 100644 index 0000000..e4abb83 --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/patches/drop_search_index_on_message_id.py @@ -0,0 +1,11 @@ +import xhiveframework + + +def execute(): + """Drop search index on message_id""" + + if xhiveframework.db.get_column_type("Email Queue", "message_id") == "text": + return + + if index := xhiveframework.db.get_column_index("tabEmail Queue", "message_id", unique=False): + xhiveframework.db.sql(f"ALTER TABLE `tabEmail Queue` DROP INDEX `{index.Key_name}`") diff --git a/xhiveframework/email/doctype/email_queue/test_email_queue.py b/xhiveframework/email/doctype/email_queue/test_email_queue.py new file mode 100644 index 0000000..ce88975 --- /dev/null +++ b/xhiveframework/email/doctype/email_queue/test_email_queue.py @@ -0,0 +1,95 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import textwrap + +import xhiveframework +from xhiveframework.email.doctype.email_queue.email_queue import SendMailContext, get_email_retry_limit +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestEmailQueue(XhiveFrameworkTestCase): + def test_email_queue_deletion_based_on_modified_date(self): + from xhiveframework.email.doctype.email_queue.email_queue import EmailQueue + + old_record = xhiveframework.get_doc( + { + "doctype": "Email Queue", + "sender": "Test ", + "show_as_cc": "", + "message": "Test message", + "status": "Sent", + "priority": 1, + "recipients": [ + { + "recipient": "test_auth@test.com", + } + ], + } + ).insert() + + old_record.modified = "2010-01-01 00:00:01" + old_record.recipients[0].modified = old_record.modified + old_record.db_update_all() + + new_record = xhiveframework.copy_doc(old_record) + new_record.insert() + + EmailQueue.clear_old_logs() + + self.assertFalse(xhiveframework.db.exists("Email Queue", old_record.name)) + self.assertFalse(xhiveframework.db.exists("Email Queue Recipient", {"parent": old_record.name})) + + self.assertTrue(xhiveframework.db.exists("Email Queue", new_record.name)) + self.assertTrue(xhiveframework.db.exists("Email Queue Recipient", {"parent": new_record.name})) + + def test_failed_email_notification(self): + subject = xhiveframework.generate_hash() + email_record = xhiveframework.new_doc("Email Queue") + email_record.sender = "Test " + email_record.message = textwrap.dedent( + f"""\ + MIME-Version: 1.0 + Message-Id: {xhiveframework.generate_hash()} + X-Original-From: Test + Subject: {subject} + From: Test + To: + Date: {xhiveframework.utils.now_datetime().strftime('%a, %d %b %Y %H:%M:%S %z')} + Reply-To: test@example.com + X-XhiveFramework-Site: {xhiveframework.local.site} + """ + ) + email_record.status = "Error" + email_record.retry = get_email_retry_limit() + email_record.priority = 1 + email_record.reference_doctype = "User" + email_record.reference_name = "Administrator" + email_record.insert() + + # Simulate an exception so that we get a notification + try: + with SendMailContext(queue_doc=email_record): + raise Exception("Test Exception") + except Exception: + pass + + notification_log = xhiveframework.db.get_value( + "Notification Log", + {"subject": f"Failed to send email with subject: {subject}"}, + ) + self.assertTrue(notification_log) + + def test_perf_reusing_smtp_server(self): + """Ensure that same smtpserver instance is being returned when retrieved multiple times.""" + + self.assertTrue(xhiveframework.new_doc("Email Queue").get_email_account()._from_site_config) + + def get_server(q): + return q.get_email_account().get_smtp_server() + + self.assertIs(get_server(xhiveframework.new_doc("Email Queue")), get_server(xhiveframework.new_doc("Email Queue"))) + + q1 = xhiveframework.new_doc("Email Queue", email_account="_Test Email Account 1") + q2 = xhiveframework.new_doc("Email Queue", email_account="_Test Email Account 1") + self.assertIsNot(get_server(xhiveframework.new_doc("Email Queue")), get_server(q1)) + self.assertIs(get_server(q1), get_server(q2)) diff --git a/xhiveframework/email/doctype/email_queue_recipient/__init__.py b/xhiveframework/email/doctype/email_queue_recipient/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.json b/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.json new file mode 100644 index 0000000..406449e --- /dev/null +++ b/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.json @@ -0,0 +1,46 @@ +{ + "actions": [], + "creation": "2016-12-08 12:01:07.993900", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "recipient", + "status", + "error" + ], + "fields": [ + { + "fieldname": "recipient", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Recipient", + "options": "Email" + }, + { + "default": "Not Sent", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "\nNot Sent\nSent", + "search_index": 1 + }, + { + "fieldname": "error", + "fieldtype": "Code", + "label": "Error" + } + ], + "istable": 1, + "links": [], + "modified": "2022-09-06 13:38:10.644417", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Queue Recipient", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.py b/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.py new file mode 100644 index 0000000..79ca0e7 --- /dev/null +++ b/xhiveframework/email/doctype/email_queue_recipient/email_queue_recipient.py @@ -0,0 +1,40 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class EmailQueueRecipient(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + error: DF.Code | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + recipient: DF.Data | None + status: DF.Literal["", "Not Sent", "Sent"] + # end: auto-generated types + DOCTYPE = "Email Queue Recipient" + + def is_mail_to_be_sent(self): + return self.status == "Not Sent" + + def is_mail_sent(self): + return self.status == "Sent" + + def update_db(self, commit=False, **kwargs): + xhiveframework.db.set_value(self.DOCTYPE, self.name, kwargs) + if commit: + xhiveframework.db.commit() + + +def on_doctype_update(): + """Index required for log clearing, modified is not indexed on child table by default""" + xhiveframework.db.add_index("Email Queue Recipient", ["modified"]) diff --git a/xhiveframework/email/doctype/email_rule/__init__.py b/xhiveframework/email/doctype/email_rule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_rule/email_rule.js b/xhiveframework/email/doctype/email_rule/email_rule.js new file mode 100644 index 0000000..7fd2f79 --- /dev/null +++ b/xhiveframework/email/doctype/email_rule/email_rule.js @@ -0,0 +1,6 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Rule", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/email/doctype/email_rule/email_rule.json b/xhiveframework/email/doctype/email_rule/email_rule.json new file mode 100644 index 0000000..20e2962 --- /dev/null +++ b/xhiveframework/email/doctype/email_rule/email_rule.json @@ -0,0 +1,49 @@ +{ + "actions": [], + "allow_copy": 1, + "autoname": "field:email_id", + "creation": "2017-03-13 09:20:56.387135", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email_id", + "is_spam" + ], + "fields": [ + { + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email ID", + "options": "Email", + "unique": 1 + }, + { + "default": "0", + "fieldname": "is_spam", + "fieldtype": "Check", + "label": "Is Spam" + } + ], + "links": [], + "modified": "2022-08-03 12:20:51.443237", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Rule", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_rule/email_rule.py b/xhiveframework/email/doctype/email_rule/email_rule.py new file mode 100644 index 0000000..823f82c --- /dev/null +++ b/xhiveframework/email/doctype/email_rule/email_rule.py @@ -0,0 +1,19 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class EmailRule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email_id: DF.Data | None + is_spam: DF.Check + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/email_rule/test_email_rule.py b/xhiveframework/email/doctype/email_rule/test_email_rule.py new file mode 100644 index 0000000..27b761e --- /dev/null +++ b/xhiveframework/email/doctype/email_rule/test_email_rule.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestEmailRule(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/email_template/__init__.py b/xhiveframework/email/doctype/email_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_template/email_template.js b/xhiveframework/email/doctype/email_template/email_template.js new file mode 100644 index 0000000..b551156 --- /dev/null +++ b/xhiveframework/email/doctype/email_template/email_template.js @@ -0,0 +1,6 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Template", { + refresh: function () {}, +}); diff --git a/xhiveframework/email/doctype/email_template/email_template.json b/xhiveframework/email/doctype/email_template/email_template.json new file mode 100644 index 0000000..6bf08cd --- /dev/null +++ b/xhiveframework/email/doctype/email_template/email_template.json @@ -0,0 +1,88 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2014-06-19 05:20:26.331041", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "subject", + "use_html", + "response_html", + "response", + "section_break_4", + "email_reply_help" + ], + "fields": [ + { + "fieldname": "subject", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Subject", + "reqd": 1 + }, + { + "depends_on": "eval:!doc.use_html", + "fieldname": "response", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Response", + "mandatory_depends_on": "eval:!doc.use_html" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "fieldname": "email_reply_help", + "fieldtype": "HTML", + "label": "Email Reply Help", + "options": "

      Email Reply Example

      \n\n
      Order Overdue\n\nTransaction {{ name }} has exceeded Due Date. Please take necessary action.\n\nDetails\n\n- Customer: {{ customer }}\n- Amount: {{ grand_total }}\n
      \n\n

      How to get fieldnames

      \n\n

      The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

      \n\n

      Templating

      \n\n

      Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

      \n" + }, + { + "default": "0", + "fieldname": "use_html", + "fieldtype": "Check", + "label": "Use HTML" + }, + { + "depends_on": "eval:doc.use_html", + "fieldname": "response_html", + "fieldtype": "Code", + "label": "Response ", + "options": "Jinja" + } + ], + "icon": "fa fa-comment", + "links": [], + "modified": "2023-12-12 20:01:07.080625", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Template", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "read": 1, + "role": "Desk User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_template/email_template.py b/xhiveframework/email/doctype/email_template/email_template.py new file mode 100644 index 0000000..8e14d26 --- /dev/null +++ b/xhiveframework/email/doctype/email_template/email_template.py @@ -0,0 +1,56 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.model.document import Document +from xhiveframework.utils.jinja import validate_template + + +class EmailTemplate(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + response: DF.TextEditor | None + response_html: DF.Code | None + subject: DF.Data + use_html: DF.Check + + # end: auto-generated types + + @property + def response_(self): + return self.response_html if self.use_html else self.response + + def validate(self): + validate_template(self.subject) + validate_template(self.response_) + + def get_formatted_subject(self, doc): + return xhiveframework.render_template(self.subject, doc) + + def get_formatted_response(self, doc): + return xhiveframework.render_template(self.response_, doc) + + def get_formatted_email(self, doc): + if isinstance(doc, str): + doc = json.loads(doc) + + return { + "subject": self.get_formatted_subject(doc), + "message": self.get_formatted_response(doc), + } + + +@xhiveframework.whitelist() +def get_email_template(template_name, doc): + """Returns the processed HTML of a email template with the given doc""" + + email_template = xhiveframework.get_doc("Email Template", template_name) + return email_template.get_formatted_email(doc) diff --git a/xhiveframework/email/doctype/email_template/test_email_template.py b/xhiveframework/email/doctype/email_template/test_email_template.py new file mode 100644 index 0000000..b722d4a --- /dev/null +++ b/xhiveframework/email/doctype/email_template/test_email_template.py @@ -0,0 +1,7 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestEmailTemplate(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/email_unsubscribe/__init__.py b/xhiveframework/email/doctype/email_unsubscribe/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.js b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.js new file mode 100644 index 0000000..8d880f2 --- /dev/null +++ b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Email Unsubscribe", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.json b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.json new file mode 100644 index 0000000..38df531 --- /dev/null +++ b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.json @@ -0,0 +1,70 @@ +{ + "actions": [], + "creation": "2015-03-18 09:41:20.216320", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "email", + "reference_doctype", + "reference_name", + "global_unsubscribe" + ], + "fields": [ + { + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Document Type", + "options": "DocType" + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "options": "reference_doctype" + }, + { + "default": "0", + "fieldname": "global_unsubscribe", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Global Unsubscribe" + } + ], + "links": [], + "modified": "2022-08-03 12:20:51.694626", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Unsubscribe", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.py b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.py new file mode 100644 index 0000000..f2d90de --- /dev/null +++ b/xhiveframework/email/doctype/email_unsubscribe/email_unsubscribe.py @@ -0,0 +1,58 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class EmailUnsubscribe(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email: DF.Data + global_unsubscribe: DF.Check + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + + # end: auto-generated types + def validate(self): + if not self.global_unsubscribe and not (self.reference_doctype and self.reference_name): + xhiveframework.throw(_("Reference DocType and Reference Name are required"), xhiveframework.MandatoryError) + + if not self.global_unsubscribe and xhiveframework.db.get_value(self.doctype, self.name, "global_unsubscribe"): + xhiveframework.throw(_("Delete this record to allow sending to this email address")) + + if self.global_unsubscribe: + if xhiveframework.get_all( + "Email Unsubscribe", + filters={"email": self.email, "global_unsubscribe": 1, "name": ["!=", self.name]}, + ): + xhiveframework.throw(_("{0} already unsubscribed").format(self.email), xhiveframework.DuplicateEntryError) + + else: + if xhiveframework.get_all( + "Email Unsubscribe", + filters={ + "email": self.email, + "reference_doctype": self.reference_doctype, + "reference_name": self.reference_name, + "name": ["!=", self.name], + }, + ): + xhiveframework.throw( + _("{0} already unsubscribed for {1} {2}").format( + self.email, self.reference_doctype, self.reference_name + ), + xhiveframework.DuplicateEntryError, + ) + + def on_update(self): + if self.reference_doctype and self.reference_name: + doc = xhiveframework.get_doc(self.reference_doctype, self.reference_name) + doc.add_comment("Label", _("Left this conversation"), comment_email=self.email) diff --git a/xhiveframework/email/doctype/email_unsubscribe/test_email_unsubscribe.py b/xhiveframework/email/doctype/email_unsubscribe/test_email_unsubscribe.py new file mode 100644 index 0000000..0484bf0 --- /dev/null +++ b/xhiveframework/email/doctype/email_unsubscribe/test_email_unsubscribe.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Email Unsubscribe') + + +class TestEmailUnsubscribe(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/imap_folder/__init__.py b/xhiveframework/email/doctype/imap_folder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/imap_folder/imap_folder.json b/xhiveframework/email/doctype/imap_folder/imap_folder.json new file mode 100644 index 0000000..bab50de --- /dev/null +++ b/xhiveframework/email/doctype/imap_folder/imap_folder.json @@ -0,0 +1,53 @@ +{ + "actions": [], + "creation": "2021-09-21 11:38:13.521979", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "folder_name", + "append_to", + "uidvalidity", + "uidnext" + ], + "fields": [ + { + "fieldname": "folder_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Folder Name", + "reqd": 1 + }, + { + "fieldname": "append_to", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Append To", + "options": "DocType" + }, + { + "fieldname": "uidvalidity", + "fieldtype": "Data", + "hidden": 1, + "label": "UIDVALIDITY" + }, + { + "fieldname": "uidnext", + "fieldtype": "Data", + "hidden": 1, + "label": "UIDNEXT" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-21 11:53:00.811236", + "modified_by": "Administrator", + "module": "Email", + "name": "IMAP Folder", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} diff --git a/xhiveframework/email/doctype/imap_folder/imap_folder.py b/xhiveframework/email/doctype/imap_folder/imap_folder.py new file mode 100644 index 0000000..42d8292 --- /dev/null +++ b/xhiveframework/email/doctype/imap_folder/imap_folder.py @@ -0,0 +1,25 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class IMAPFolder(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + append_to: DF.Link | None + folder_name: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + uidnext: DF.Data | None + uidvalidity: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/newsletter/__init__.py b/xhiveframework/email/doctype/newsletter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/newsletter/exceptions.py b/xhiveframework/email/doctype/newsletter/exceptions.py new file mode 100644 index 0000000..5dc291f --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/exceptions.py @@ -0,0 +1,16 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE + +from xhiveframework.exceptions import ValidationError + + +class NewsletterAlreadySentError(ValidationError): + pass + + +class NoRecipientFoundError(ValidationError): + pass + + +class NewsletterNotSavedError(ValidationError): + pass diff --git a/xhiveframework/email/doctype/newsletter/newsletter.js b/xhiveframework/email/doctype/newsletter/newsletter.js new file mode 100644 index 0000000..0bcd347 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/newsletter.js @@ -0,0 +1,229 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +xhiveframework.ui.form.on("Newsletter", { + refresh(frm) { + let doc = frm.doc; + let can_write = xhiveframework.boot.user.can_write.includes(doc.doctype); + if (!frm.is_new() && !frm.is_dirty() && !doc.email_sent && can_write) { + frm.add_custom_button( + __("Send a test email"), + () => { + frm.events.send_test_email(frm); + }, + __("Preview") + ); + + frm.add_custom_button( + __("Check broken links"), + () => { + frm.dashboard.set_headline(__("Checking broken links...")); + frm.call("find_broken_links").then((r) => { + frm.dashboard.set_headline(""); + let links = r.message; + if (links && links.length) { + let html = + "
        " + + links.map((link) => `
      • ${link}
      • `).join("") + + "
      "; + frm.dashboard.set_headline( + __("Following links are broken in the email content: {0}", [html]) + ); + } else { + frm.dashboard.set_headline( + __("No broken links found in the email content") + ); + setTimeout(() => { + frm.dashboard.set_headline(""); + }, 3000); + } + }); + }, + __("Preview") + ); + + frm.add_custom_button( + __("Send now"), + () => { + if (frm.doc.schedule_send) { + xhiveframework.confirm( + __( + "This newsletter was scheduled to send on a later date. Are you sure you want to send it now?" + ), + function () { + frm.events.send_emails(frm); + } + ); + return; + } + xhiveframework.confirm( + __("Are you sure you want to send this newsletter now?"), + () => { + frm.events.send_emails(frm); + } + ); + }, + __("Send") + ); + + frm.add_custom_button( + __("Schedule sending"), + () => { + frm.events.schedule_send_dialog(frm); + }, + __("Send") + ); + } + + frm.events.update_sending_status(frm); + + if (frm.is_new() && !doc.sender_email) { + let { fullname, email } = xhiveframework.user_info(doc.owner); + frm.set_value("sender_email", email); + frm.set_value("sender_name", fullname); + } + + frm.trigger("update_schedule_message"); + }, + + send_emails(frm) { + xhiveframework.dom.freeze(__("Queuing emails...")); + frm.call("send_emails").then(() => { + frm.refresh(); + xhiveframework.dom.unfreeze(); + xhiveframework.show_alert( + __("Queued {0} emails", [xhiveframework.utils.shorten_number(frm.doc.total_recipients)]) + ); + }); + }, + + schedule_send_dialog(frm) { + let hours = xhiveframework.utils.range(24); + let time_slots = hours.map((hour) => { + return `${(hour + "").padStart(2, "0")}:00`; + }); + let d = new xhiveframework.ui.Dialog({ + title: __("Schedule Newsletter"), + fields: [ + { + label: __("Date"), + fieldname: "date", + fieldtype: "Date", + options: { + minDate: new Date(), + }, + reqd: true, + }, + { + label: __("Time"), + fieldname: "time", + fieldtype: "Select", + options: time_slots, + reqd: true, + }, + ], + primary_action_label: __("Schedule"), + primary_action({ date, time }) { + frm.set_value("schedule_sending", 1); + frm.set_value("schedule_send", `${date} ${time}:00`); + d.hide(); + frm.save(); + }, + secondary_action_label: __("Cancel Scheduling"), + secondary_action() { + frm.set_value("schedule_sending", 0); + frm.set_value("schedule_send", ""); + d.hide(); + frm.save(); + }, + }); + if (frm.doc.schedule_sending) { + let parts = frm.doc.schedule_send.split(" "); + if (parts.length === 2) { + let [date, time] = parts; + d.set_value("date", date); + d.set_value("time", time.slice(0, 5)); + } + } + d.show(); + }, + + send_test_email(frm) { + let d = new xhiveframework.ui.Dialog({ + title: __("Send Test Email"), + fields: [ + { + label: __("Email"), + fieldname: "email", + fieldtype: "Data", + options: "Email", + }, + ], + primary_action_label: __("Send"), + primary_action({ email }) { + d.get_primary_btn().text(__("Sending...")).prop("disabled", true); + frm.call("send_test_email", { email }).then(() => { + d.get_primary_btn().text(__("Send again")).prop("disabled", false); + }); + }, + }); + d.show(); + }, + + async update_sending_status(frm) { + if (frm.doc.email_sent && frm.$wrapper.is(":visible") && !frm.waiting_for_request) { + frm.waiting_for_request = true; + let res = await frm.call("get_sending_status"); + frm.waiting_for_request = false; + let stats = res.message; + stats && frm.events.update_sending_progress(frm, stats); + if ( + stats.sent + stats.error >= frm.doc.total_recipients || + (!stats.total && !stats.emails_queued) + ) { + frm.sending_status && clearInterval(frm.sending_status); + frm.sending_status = null; + return; + } + } + + if (frm.sending_status) return; + frm.sending_status = setInterval(() => frm.events.update_sending_status(frm), 5000); + }, + + update_sending_progress(frm, stats) { + if (stats.sent + stats.error >= frm.doc.total_recipients || !frm.doc.email_sent) { + frm.doc.email_sent && frm.page.set_indicator(__("Sent"), "green"); + frm.dashboard.hide_progress(); + return; + } + if (stats.total) { + frm.page.set_indicator(__("Sending"), "blue"); + frm.dashboard.show_progress( + __("Sending emails"), + (stats.sent * 100) / frm.doc.total_recipients, + __("{0} of {1} sent", [stats.sent, frm.doc.total_recipients]) + ); + } else if (stats.emails_queued) { + frm.page.set_indicator(__("Queued"), "blue"); + } + }, + + on_hide(frm) { + if (frm.sending_status) { + clearInterval(frm.sending_status); + frm.sending_status = null; + } + }, + + update_schedule_message(frm) { + if (!frm.doc.email_sent && frm.doc.schedule_send) { + let datetime = xhiveframework.datetime.global_date_format(frm.doc.schedule_send); + frm.dashboard.set_headline_alert( + __("This newsletter is scheduled to be sent on {0}", [datetime.bold()]) + ); + } else { + frm.dashboard.clear_headline(); + } + }, +}); diff --git a/xhiveframework/email/doctype/newsletter/newsletter.json b/xhiveframework/email/doctype/newsletter/newsletter.json new file mode 100644 index 0000000..d3a43f8 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/newsletter.json @@ -0,0 +1,280 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2013-01-10 16:34:31", + "description": "Create and send emails to a specific group of subscribers periodically.", + "doctype": "DocType", + "document_type": "Other", + "engine": "InnoDB", + "field_order": [ + "status_section", + "email_sent_at", + "column_break_3", + "total_recipients", + "column_break_12", + "total_views", + "email_sent", + "from_section", + "sender_name", + "column_break_5", + "sender_email", + "column_break_7", + "send_from", + "recipients", + "email_group", + "subject_section", + "subject", + "newsletter_content", + "content_type", + "message", + "message_md", + "message_html", + "campaign", + "attachments", + "send_unsubscribe_link", + "send_webview_link", + "schedule_settings_section", + "scheduled_to_send", + "schedule_sending", + "schedule_send", + "publish_as_a_web_page_section", + "published", + "route" + ], + "fields": [ + { + "fieldname": "email_group", + "fieldtype": "Table", + "in_standard_filter": 1, + "label": "Audience", + "options": "Newsletter Email Group", + "reqd": 1 + }, + { + "fieldname": "send_from", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "label": "Sender", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "email_sent", + "fieldtype": "Check", + "hidden": 1, + "label": "Email Sent", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "newsletter_content", + "fieldtype": "Section Break", + "label": "Content" + }, + { + "fieldname": "subject", + "fieldtype": "Small Text", + "in_global_search": 1, + "in_list_view": 1, + "label": "Subject", + "reqd": 1 + }, + { + "depends_on": "eval: doc.content_type === 'Rich Text'", + "fieldname": "message", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Message", + "mandatory_depends_on": "eval: doc.content_type === 'Rich Text'" + }, + { + "default": "1", + "fieldname": "send_unsubscribe_link", + "fieldtype": "Check", + "label": "Send Unsubscribe Link" + }, + { + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Published" + }, + { + "depends_on": "published", + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "read_only": 1 + }, + { + "fieldname": "scheduled_to_send", + "fieldtype": "Int", + "hidden": 1, + "label": "Scheduled To Send" + }, + { + "fieldname": "recipients", + "fieldtype": "Section Break", + "label": "To" + }, + { + "depends_on": "eval: doc.schedule_sending", + "fieldname": "schedule_send", + "fieldtype": "Datetime", + "label": "Send Email At", + "read_only": 1, + "read_only_depends_on": "eval: doc.email_sent" + }, + { + "fieldname": "content_type", + "fieldtype": "Select", + "label": "Content Type", + "options": "Rich Text\nMarkdown\nHTML" + }, + { + "depends_on": "eval:doc.content_type === 'Markdown'", + "fieldname": "message_md", + "fieldtype": "Markdown Editor", + "label": "Message (Markdown)", + "mandatory_depends_on": "eval:doc.content_type === 'Markdown'" + }, + { + "depends_on": "eval:doc.content_type === 'HTML'", + "fieldname": "message_html", + "fieldtype": "HTML Editor", + "label": "Message (HTML)", + "mandatory_depends_on": "eval:doc.content_type === 'HTML'" + }, + { + "default": "0", + "fieldname": "schedule_sending", + "fieldtype": "Check", + "label": "Schedule sending at a later time", + "read_only_depends_on": "eval: doc.email_sent" + }, + { + "default": "0", + "fieldname": "send_webview_link", + "fieldtype": "Check", + "label": "Send Web View Link" + }, + { + "fieldname": "from_section", + "fieldtype": "Section Break", + "label": "From" + }, + { + "fieldname": "sender_name", + "fieldtype": "Data", + "label": "Sender Name" + }, + { + "fieldname": "sender_email", + "fieldtype": "Data", + "label": "Sender Email", + "options": "Email", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "subject_section", + "fieldtype": "Section Break", + "label": "Subject" + }, + { + "fieldname": "publish_as_a_web_page_section", + "fieldtype": "Section Break", + "label": "Publish as a web page" + }, + { + "depends_on": "schedule_sending", + "fieldname": "schedule_settings_section", + "fieldtype": "Section Break", + "label": "Scheduled Sending" + }, + { + "fieldname": "attachments", + "fieldtype": "Table", + "label": "Attachments", + "options": "Newsletter Attachment" + }, + { + "fieldname": "email_sent_at", + "fieldtype": "Datetime", + "label": "Email Sent At", + "read_only": 1 + }, + { + "fieldname": "total_recipients", + "fieldtype": "Int", + "label": "Total Recipients", + "read_only": 1 + }, + { + "depends_on": "email_sent", + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "total_views", + "fieldtype": "Int", + "label": "Total Views", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "campaign", + "fieldtype": "Link", + "label": "Campaign", + "options": "Marketing Campaign" + } + ], + "has_web_view": 1, + "icon": "fa fa-envelope", + "idx": 1, + "index_web_pages_for_search": 1, + "is_published_field": "published", + "links": [], + "modified": "2024-01-30 14:05:50.645802", + "modified_by": "Administrator", + "module": "Email", + "name": "Newsletter", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Newsletter Manager", + "share": 1, + "write": 1 + } + ], + "route": "newsletters", + "sort_field": "modified", + "sort_order": "ASC", + "states": [], + "title_field": "subject", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/newsletter/newsletter.py b/xhiveframework/email/doctype/newsletter/newsletter.py new file mode 100644 index 0000000..40c90ad --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/newsletter.py @@ -0,0 +1,449 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE + + +import xhiveframework +import xhiveframework.utils +from xhiveframework import _ +from xhiveframework.email.doctype.email_group.email_group import add_subscribers +from xhiveframework.rate_limiter import rate_limit +from xhiveframework.utils.safe_exec import is_job_queued +from xhiveframework.utils.verified_command import get_signed_params, verify_request +from xhiveframework.website.website_generator import WebsiteGenerator + +from .exceptions import NewsletterAlreadySentError, NewsletterNotSavedError, NoRecipientFoundError + + +class Newsletter(WebsiteGenerator): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.email.doctype.newsletter_attachment.newsletter_attachment import NewsletterAttachment + from xhiveframework.email.doctype.newsletter_email_group.newsletter_email_group import ( + NewsletterEmailGroup, + ) + from xhiveframework.types import DF + + attachments: DF.Table[NewsletterAttachment] + campaign: DF.Link | None + content_type: DF.Literal["Rich Text", "Markdown", "HTML"] + email_group: DF.Table[NewsletterEmailGroup] + email_sent: DF.Check + email_sent_at: DF.Datetime | None + message: DF.TextEditor | None + message_html: DF.HTMLEditor | None + message_md: DF.MarkdownEditor | None + published: DF.Check + route: DF.Data | None + schedule_send: DF.Datetime | None + schedule_sending: DF.Check + scheduled_to_send: DF.Int + send_from: DF.Data | None + send_unsubscribe_link: DF.Check + send_webview_link: DF.Check + sender_email: DF.Data + sender_name: DF.Data | None + subject: DF.SmallText + total_recipients: DF.Int + total_views: DF.Int + # end: auto-generated types + + def validate(self): + self.route = f"newsletters/{self.name}" + self.validate_sender_address() + self.validate_publishing() + self.validate_scheduling_date() + + @property + def newsletter_recipients(self) -> list[str]: + if getattr(self, "_recipients", None) is None: + self._recipients = self.get_recipients() + return self._recipients + + @xhiveframework.whitelist() + def get_sending_status(self): + count_by_status = xhiveframework.get_all( + "Email Queue", + filters={"reference_doctype": self.doctype, "reference_name": self.name}, + fields=["status", "count(name) as count"], + group_by="status", + order_by="status", + ) + sent = 0 + error = 0 + total = 0 + for row in count_by_status: + if row.status == "Sent": + sent = row.count + elif row.status == "Error": + error = row.count + total += row.count + emails_queued = is_job_queued( + job_name=xhiveframework.utils.get_job_name("send_bulk_emails_for", self.doctype, self.name), + queue="long", + ) + return {"sent": sent, "error": error, "total": total, "emails_queued": emails_queued} + + @xhiveframework.whitelist() + def send_test_email(self, email): + test_emails = xhiveframework.utils.validate_email_address(email, throw=True) + self.send_newsletter(emails=test_emails, test_email=True) + xhiveframework.msgprint(_("Test email sent to {0}").format(email), alert=True) + + @xhiveframework.whitelist() + def find_broken_links(self): + import requests + from bs4 import BeautifulSoup + + html = self.get_message() + soup = BeautifulSoup(html, "html.parser") + links = soup.find_all("a") + images = soup.find_all("img") + broken_links = [] + for el in links + images: + url = el.attrs.get("href") or el.attrs.get("src") + try: + response = requests.head(url, verify=False, timeout=5) + if response.status_code >= 400: + broken_links.append(url) + except Exception: + broken_links.append(url) + return broken_links + + @xhiveframework.whitelist() + def send_emails(self): + """queue sending emails to recipients""" + self.schedule_sending = False + self.schedule_send = None + self.queue_all() + + def validate_send(self): + """Validate if Newsletter can be sent.""" + self.validate_newsletter_status() + self.validate_newsletter_recipients() + + def validate_newsletter_status(self): + if self.email_sent: + xhiveframework.throw(_("Newsletter has already been sent"), exc=NewsletterAlreadySentError) + + if self.get("__islocal"): + xhiveframework.throw(_("Please save the Newsletter before sending"), exc=NewsletterNotSavedError) + + def validate_newsletter_recipients(self): + if not self.newsletter_recipients: + xhiveframework.throw(_("Newsletter should have atleast one recipient"), exc=NoRecipientFoundError) + + def validate_sender_address(self): + """Validate self.send_from is a valid email address or not.""" + if self.sender_email: + xhiveframework.utils.validate_email_address(self.sender_email, throw=True) + self.send_from = ( + f"{self.sender_name} <{self.sender_email}>" if self.sender_name else self.sender_email + ) + + def validate_publishing(self): + if self.send_webview_link and not self.published: + xhiveframework.throw(_("Newsletter must be published to send webview link in email")) + + def validate_scheduling_date(self): + if ( + self.schedule_sending + and xhiveframework.utils.get_datetime(self.schedule_send) < xhiveframework.utils.now_datetime() + ): + xhiveframework.throw(_("Past dates are not allowed for Scheduling.")) + + def get_linked_email_queue(self) -> list[str]: + """Get list of email queue linked to this newsletter.""" + return xhiveframework.get_all( + "Email Queue", + filters={ + "reference_doctype": self.doctype, + "reference_name": self.name, + }, + pluck="name", + ) + + def get_queued_recipients(self) -> list[str]: + """Recipients who have already been queued for receiving the newsletter.""" + return xhiveframework.get_all( + "Email Queue Recipient", + filters={ + "parent": ("in", self.get_linked_email_queue()), + }, + pluck="recipient", + ) + + def get_pending_recipients(self) -> list[str]: + """Get list of pending recipients of the newsletter. These + recipients may not have receive the newsletter in the previous iteration. + """ + + queued_recipients = set(self.get_queued_recipients()) + return [x for x in self.newsletter_recipients if x not in queued_recipients] + + def queue_all(self): + """Queue Newsletter to all the recipients generated from the `Email Group` table""" + self.validate() + self.validate_send() + + recipients = self.get_pending_recipients() + self.send_newsletter(emails=recipients) + + self.email_sent = True + self.email_sent_at = xhiveframework.utils.now() + self.total_recipients = len(recipients) + self.save() + + def get_newsletter_attachments(self) -> list[dict[str, str]]: + """Get list of attachments on current Newsletter""" + return [{"file_url": row.attachment} for row in self.attachments] + + def send_newsletter(self, emails: list[str], test_email: bool = False): + """Trigger email generation for `emails` and add it in Email Queue.""" + attachments = self.get_newsletter_attachments() + sender = self.send_from or xhiveframework.utils.get_formatted_email(self.owner) + args = self.as_dict() + args["message"] = self.get_message(medium="email") + + is_auto_commit_set = bool(xhiveframework.db.auto_commit_on_many_writes) + xhiveframework.db.auto_commit_on_many_writes = not xhiveframework.flags.in_test + + xhiveframework.sendmail( + subject=self.subject, + sender=sender, + recipients=emails, + attachments=attachments, + template="newsletter", + add_unsubscribe_link=self.send_unsubscribe_link, + unsubscribe_method="/unsubscribe", + unsubscribe_params={"name": self.name}, + reference_doctype=self.doctype, + reference_name=self.name, + queue_separately=True, + send_priority=0, + args=args, + email_read_tracker_url=None + if test_email + else "/api/method/xhiveframework.email.doctype.newsletter.newsletter.newsletter_email_read", + ) + + xhiveframework.db.auto_commit_on_many_writes = is_auto_commit_set + + def get_message(self, medium=None) -> str: + message = self.message + if self.content_type == "Markdown": + message = xhiveframework.utils.md_to_html(self.message_md) + if self.content_type == "HTML": + message = self.message_html + + html = xhiveframework.render_template(message, {"doc": self.as_dict()}) + + return self.add_source(html, medium=medium) + + def add_source(self, html: str, medium="None") -> str: + """Add source to the site links in the newsletter content.""" + from bs4 import BeautifulSoup + + soup = BeautifulSoup(html, "html.parser") + + links = soup.find_all("a") + for link in links: + href = link.get("href") + if href and not href.startswith("#"): + if not xhiveframework.utils.is_site_link(href): + continue + new_href = xhiveframework.utils.add_trackers_to_url( + href, source="Newsletter", campaign=self.campaign, medium=medium + ) + link["href"] = new_href + + return str(soup) + + def get_recipients(self) -> list[str]: + """Get recipients from Email Group""" + emails = xhiveframework.get_all( + "Email Group Member", + filters={"unsubscribed": 0, "email_group": ("in", self.get_email_groups())}, + pluck="email", + ) + return list(set(emails)) + + def get_email_groups(self) -> list[str]: + # wondering why the 'or'? i can't figure out why both aren't equivalent - @gavin + return [x.email_group for x in self.email_group] or xhiveframework.get_all( + "Newsletter Email Group", + filters={"parent": self.name, "parenttype": "Newsletter"}, + pluck="email_group", + ) + + def get_attachments(self) -> list[dict[str, str]]: + return xhiveframework.get_all( + "File", + fields=["name", "file_name", "file_url", "is_private"], + filters={ + "attached_to_name": self.name, + "attached_to_doctype": "Newsletter", + "is_private": 0, + }, + ) + + +def confirmed_unsubscribe(email, group): + """unsubscribe the email(user) from the mailing list(email_group)""" + xhiveframework.flags.ignore_permissions = True + doc = xhiveframework.get_doc("Email Group Member", {"email": email, "email_group": group}) + if not doc.unsubscribed: + doc.unsubscribed = 1 + doc.save(ignore_permissions=True) + + +@xhiveframework.whitelist(allow_guest=True) +@rate_limit(limit=10, seconds=60 * 60) +def subscribe(email, email_group=None): + """API endpoint to subscribe an email to a particular email group. Triggers a confirmation email.""" + + if email_group is None: + email_group = get_default_email_group() + + # build subscription confirmation URL + api_endpoint = xhiveframework.utils.get_url( + "/api/method/xhiveframework.email.doctype.newsletter.newsletter.confirm_subscription" + ) + signed_params = get_signed_params({"email": email, "email_group": email_group}) + confirm_subscription_url = f"{api_endpoint}?{signed_params}" + + # fetch custom template if available + email_confirmation_template = xhiveframework.db.get_value( + "Email Group", email_group, "confirmation_email_template" + ) + + # build email and send + if email_confirmation_template: + args = {"email": email, "confirmation_url": confirm_subscription_url, "email_group": email_group} + email_template = xhiveframework.get_doc("Email Template", email_confirmation_template) + email_subject = email_template.subject + content = xhiveframework.render_template(email_template.response, args) + else: + email_subject = _("Confirm Your Email") + translatable_content = ( + _("Thank you for your interest in subscribing to our updates"), + _("Please verify your Email Address"), + confirm_subscription_url, + _("Click here to verify"), + ) + content = """ +

      {}. {}.

      +

      {}

      + """.format(*translatable_content) + + xhiveframework.sendmail( + email, + subject=email_subject, + content=content, + ) + + +@xhiveframework.whitelist(allow_guest=True) +def confirm_subscription(email, email_group=None): + """API endpoint to confirm email subscription. + This endpoint is called when user clicks on the link sent to their mail. + """ + if not verify_request(): + return + + if email_group is None: + email_group = get_default_email_group() + + try: + group = xhiveframework.get_doc("Email Group", email_group) + except xhiveframework.DoesNotExistError: + group = xhiveframework.get_doc({"doctype": "Email Group", "title": email_group}).insert( + ignore_permissions=True + ) + + xhiveframework.flags.ignore_permissions = True + + add_subscribers(email_group, email) + xhiveframework.db.commit() + + welcome_url = group.get_welcome_url(email) + + if welcome_url: + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = welcome_url + else: + xhiveframework.respond_as_web_page( + _("Confirmed"), + _("{0} has been successfully added to the Email Group.").format(email), + indicator_color="green", + ) + + +def get_list_context(context=None): + context.update( + { + "show_search": True, + "no_breadcrumbs": True, + "title": _("Newsletters"), + "filters": {"published": 1}, + "row_template": "email/doctype/newsletter/templates/newsletter_row.html", + } + ) + + +def send_scheduled_email(): + """Send scheduled newsletter to the recipients.""" + scheduled_newsletter = xhiveframework.get_all( + "Newsletter", + filters={ + "schedule_send": ("<=", xhiveframework.utils.now_datetime()), + "email_sent": False, + "schedule_sending": True, + }, + ignore_ifnull=True, + pluck="name", + ) + + for newsletter_name in scheduled_newsletter: + try: + newsletter = xhiveframework.get_doc("Newsletter", newsletter_name) + newsletter.queue_all() + + except Exception: + xhiveframework.db.rollback() + + # wasn't able to send emails :( + xhiveframework.db.set_value("Newsletter", newsletter_name, "email_sent", 0) + newsletter.log_error("Failed to send newsletter") + + if not xhiveframework.flags.in_test: + xhiveframework.db.commit() + + +@xhiveframework.whitelist(allow_guest=True) +def newsletter_email_read(recipient_email=None, reference_doctype=None, reference_name=None): + if not (recipient_email and reference_name): + return + verify_request() + try: + doc = xhiveframework.get_cached_doc("Newsletter", reference_name) + if doc.add_viewed(recipient_email, force=True, unique_views=True): + newsletter = xhiveframework.qb.DocType("Newsletter") + ( + xhiveframework.qb.update(newsletter) + .set(newsletter.total_views, newsletter.total_views + 1) + .where(newsletter.name == doc.name) + ).run() + + except Exception: + doc.log_error(f"Unable to mark as viewed for {recipient_email}") + + finally: + xhiveframework.response.update(xhiveframework.utils.get_imaginary_pixel_response()) + + +def get_default_email_group(): + return _("Website", lang=xhiveframework.db.get_default("language")) diff --git a/xhiveframework/email/doctype/newsletter/newsletter_list.js b/xhiveframework/email/doctype/newsletter/newsletter_list.js new file mode 100644 index 0000000..c0320d7 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/newsletter_list.js @@ -0,0 +1,12 @@ +xhiveframework.listview_settings["Newsletter"] = { + add_fields: ["subject", "email_sent", "schedule_sending"], + get_indicator: function (doc) { + if (doc.email_sent) { + return [__("Sent"), "green", "email_sent,=,Yes"]; + } else if (doc.schedule_sending) { + return [__("Scheduled"), "purple", "email_sent,=,No|schedule_sending,=,Yes"]; + } else { + return [__("Not Sent"), "gray", "email_sent,=,No"]; + } + }, +}; diff --git a/xhiveframework/email/doctype/newsletter/templates/newsletter.html b/xhiveframework/email/doctype/newsletter/templates/newsletter.html new file mode 100644 index 0000000..78ef967 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/templates/newsletter.html @@ -0,0 +1,65 @@ +{% extends "templates/web.html" %} + +{% block title %} {{ doc.subject }} {% endblock %} + +{% block page_content %} + + +
      +
      +
      +

      {{ doc.subject }}

      +

      + {{ xhiveframework.format_date(doc.modified) }} +

      +
      +
      + {{ doc.get_message(medium="web_page") }} +
      +
      + + {% if doc.attachments %} +
      +
      +
      + {{ _("Attachments") }} +
      +
      +
      +
      + {% for attachment in doc.attachments %} +

      + + {{ attachment.attachment }} + +

      + {% endfor %} +
      +
      +
      + {% endif %} + +
      +{% endblock %} diff --git a/xhiveframework/email/doctype/newsletter/templates/newsletter_row.html b/xhiveframework/email/doctype/newsletter/templates/newsletter_row.html new file mode 100644 index 0000000..6e1e134 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/templates/newsletter_row.html @@ -0,0 +1,15 @@ + diff --git a/xhiveframework/email/doctype/newsletter/test_newsletter.py b/xhiveframework/email/doctype/newsletter/test_newsletter.py new file mode 100644 index 0000000..8cd5025 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter/test_newsletter.py @@ -0,0 +1,252 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See LICENSE + +from random import choice +from unittest.mock import MagicMock, PropertyMock, patch + +import xhiveframework +from xhiveframework.email.doctype.newsletter.exceptions import ( + NewsletterAlreadySentError, + NoRecipientFoundError, +) +from xhiveframework.email.doctype.newsletter.newsletter import ( + Newsletter, + confirmed_unsubscribe, + send_scheduled_email, +) +from xhiveframework.email.queue import flush +from xhiveframework.tests.utils import XhiveFrameworkTestCase +from xhiveframework.utils import add_days, getdate + +emails = [ + "test_subscriber1@example.com", + "test_subscriber2@example.com", + "test_subscriber3@example.com", + "test1@example.com", +] +newsletters = [] + + +def get_dotted_path(obj: type) -> str: + klass = obj.__class__ + module = klass.__module__ + if module == "builtins": + return klass.__qualname__ # avoid outputs like 'builtins.str' + return f"{module}.{klass.__qualname__}" + + +class TestNewsletterMixin: + def setUp(self): + xhiveframework.set_user("Administrator") + self.setup_email_group() + + def tearDown(self): + xhiveframework.set_user("Administrator") + for newsletter in newsletters: + xhiveframework.db.delete( + "Email Queue", + { + "reference_doctype": "Newsletter", + "reference_name": newsletter, + }, + ) + xhiveframework.delete_doc("Newsletter", newsletter) + xhiveframework.db.delete("Newsletter Email Group", {"parent": newsletter}) + newsletters.remove(newsletter) + + def setup_email_group(self): + if not xhiveframework.db.exists("Email Group", "_Test Email Group"): + xhiveframework.get_doc({"doctype": "Email Group", "title": "_Test Email Group"}).insert() + + for email in emails: + doctype = "Email Group Member" + email_filters = {"email": email, "email_group": "_Test Email Group"} + + savepoint = "setup_email_group" + xhiveframework.db.savepoint(savepoint) + + try: + xhiveframework.get_doc( + { + "doctype": doctype, + **email_filters, + } + ).insert(ignore_if_duplicate=True) + except Exception: + xhiveframework.db.rollback(save_point=savepoint) + xhiveframework.db.set_value(doctype, email_filters, "unsubscribed", 0) + + xhiveframework.db.release_savepoint(savepoint) + + def send_newsletter(self, published=0, schedule_send=None) -> str | None: + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Email Queue Recipient") + xhiveframework.db.delete("Newsletter") + + newsletter_options = { + "published": published, + "schedule_sending": bool(schedule_send), + "schedule_send": schedule_send, + } + newsletter = self.get_newsletter(**newsletter_options) + + if schedule_send: + send_scheduled_email() + else: + newsletter.send_emails() + return newsletter.name + + return newsletter + + @staticmethod + def get_newsletter(**kwargs) -> "Newsletter": + """Generate and return Newsletter object""" + doctype = "Newsletter" + newsletter_content = { + "subject": "_Test Newsletter", + "sender_name": "Test Sender", + "sender_email": "test_sender@example.com", + "content_type": "Rich Text", + "message": "Testing my news.", + } + similar_newsletters = xhiveframework.get_all(doctype, newsletter_content, pluck="name") + + for similar_newsletter in similar_newsletters: + xhiveframework.delete_doc(doctype, similar_newsletter) + + newsletter = xhiveframework.get_doc({"doctype": doctype, **newsletter_content, **kwargs}) + newsletter.append("email_group", {"email_group": "_Test Email Group"}) + newsletter.save(ignore_permissions=True) + newsletter.reload() + newsletters.append(newsletter.name) + + attached_files = xhiveframework.get_all( + "File", + { + "attached_to_doctype": newsletter.doctype, + "attached_to_name": newsletter.name, + }, + pluck="name", + ) + for file in attached_files: + xhiveframework.delete_doc("File", file) + + return newsletter + + +class TestNewsletter(TestNewsletterMixin, XhiveFrameworkTestCase): + def test_send(self): + self.send_newsletter() + + email_queue_list = [xhiveframework.get_doc("Email Queue", e.name) for e in xhiveframework.get_all("Email Queue")] + self.assertEqual(len(email_queue_list), 4) + + recipients = {e.recipients[0].recipient for e in email_queue_list} + self.assertTrue(set(emails).issubset(recipients)) + + def test_unsubscribe(self): + name = self.send_newsletter() + to_unsubscribe = choice(emails) + group = xhiveframework.get_all("Newsletter Email Group", filters={"parent": name}, fields=["email_group"]) + + flush() + confirmed_unsubscribe(to_unsubscribe, group[0].email_group) + + name = self.send_newsletter() + email_queue_list = [xhiveframework.get_doc("Email Queue", e.name) for e in xhiveframework.get_all("Email Queue")] + self.assertEqual(len(email_queue_list), 3) + recipients = [e.recipients[0].recipient for e in email_queue_list] + + for email in emails: + if email != to_unsubscribe: + self.assertTrue(email in recipients) + + def test_schedule_send(self): + newsletter = self.send_newsletter(schedule_send=add_days(getdate(), 1)) + newsletter.db_set("schedule_send", add_days(getdate(), -1)) # Set date in past + send_scheduled_email() + + email_queue_list = [xhiveframework.get_doc("Email Queue", e.name) for e in xhiveframework.get_all("Email Queue")] + self.assertEqual(len(email_queue_list), 4) + recipients = [e.recipients[0].recipient for e in email_queue_list] + for email in emails: + self.assertTrue(email in recipients) + + def test_newsletter_send_test_email(self): + """Test "Send Test Email" functionality of Newsletter""" + newsletter = self.get_newsletter() + test_email = choice(emails) + newsletter.send_test_email(test_email) + + self.assertFalse(newsletter.email_sent) + newsletter.save = MagicMock() + self.assertFalse(newsletter.save.called) + # check if the test email is in the queue + email_queue = xhiveframework.get_all( + "Email Queue", + filters=[ + ["reference_doctype", "=", "Newsletter"], + ["reference_name", "=", newsletter.name], + ["Email Queue Recipient", "recipient", "=", test_email], + ], + ) + self.assertTrue(email_queue) + + def test_newsletter_status(self): + """Test for Newsletter's stats on onload event""" + newsletter = self.get_newsletter() + newsletter.email_sent = True + result = newsletter.get_sending_status() + self.assertTrue("total" in result) + self.assertTrue("sent" in result) + + def test_already_sent_newsletter(self): + newsletter = self.get_newsletter() + newsletter.send_emails() + + with self.assertRaises(NewsletterAlreadySentError): + newsletter.send_emails() + + def test_newsletter_with_no_recipient(self): + newsletter = self.get_newsletter() + property_path = f"{get_dotted_path(newsletter)}.newsletter_recipients" + + with patch(property_path, new_callable=PropertyMock) as mock_newsletter_recipients: + mock_newsletter_recipients.return_value = [] + with self.assertRaises(NoRecipientFoundError): + newsletter.send_emails() + + def test_send_scheduled_email_error_handling(self): + newsletter = self.get_newsletter(schedule_send=add_days(getdate(), -1)) + job_path = "xhiveframework.email.doctype.newsletter.newsletter.Newsletter.queue_all" + m = MagicMock(side_effect=xhiveframework.OutgoingEmailError) + + with self.assertRaises(xhiveframework.OutgoingEmailError): + with patch(job_path, new_callable=m): + send_scheduled_email() + + newsletter.reload() + self.assertEqual(newsletter.email_sent, 0) + + def test_retry_partially_sent_newsletter(self): + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Email Queue Recipient") + xhiveframework.db.delete("Newsletter") + + newsletter = self.get_newsletter() + newsletter.send_emails() + email_queue_list = [xhiveframework.get_doc("Email Queue", e.name) for e in xhiveframework.get_all("Email Queue")] + self.assertEqual(len(email_queue_list), 4) + + # delete a queue document to emulate partial send + queue_recipient_name = email_queue_list[0].recipients[0].recipient + email_queue_list[0].delete() + newsletter.email_sent = False + + # make sure the pending recipient is only the one which has been deleted + self.assertEqual(newsletter.get_pending_recipients(), [queue_recipient_name]) + + # retry + newsletter.send_emails() + self.assertEqual(xhiveframework.db.count("Email Queue"), 4) + self.assertTrue(newsletter.email_sent) diff --git a/xhiveframework/email/doctype/newsletter_attachment/__init__.py b/xhiveframework/email/doctype/newsletter_attachment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.json b/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.json new file mode 100644 index 0000000..e2add0e --- /dev/null +++ b/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2021-12-06 16:37:40.652468", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "attachment" + ], + "fields": [ + { + "fieldname": "attachment", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Attachment", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-12-06 16:37:47.481057", + "modified_by": "Administrator", + "module": "Email", + "name": "Newsletter Attachment", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.py b/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.py new file mode 100644 index 0000000..c82dce2 --- /dev/null +++ b/xhiveframework/email/doctype/newsletter_attachment/newsletter_attachment.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +# import xhiveframework +from xhiveframework.model.document import Document + + +class NewsletterAttachment(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + attachment: DF.Attach + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/newsletter_email_group/__init__.py b/xhiveframework/email/doctype/newsletter_email_group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.json b/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.json new file mode 100644 index 0000000..b8c1afe --- /dev/null +++ b/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.json @@ -0,0 +1,42 @@ +{ + "actions": [], + "creation": "2017-02-26 16:20:52.654136", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "email_group", + "total_subscribers" + ], + "fields": [ + { + "columns": 7, + "fieldname": "email_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Email Group", + "options": "Email Group", + "reqd": 1 + }, + { + "columns": 3, + "fetch_from": "email_group.total_subscribers", + "fieldname": "total_subscribers", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Total Subscribers" + } + ], + "istable": 1, + "links": [], + "modified": "2021-12-06 20:12:08.420240", + "modified_by": "Administrator", + "module": "Email", + "name": "Newsletter Email Group", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.py b/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.py new file mode 100644 index 0000000..631170e --- /dev/null +++ b/xhiveframework/email/doctype/newsletter_email_group/newsletter_email_group.py @@ -0,0 +1,22 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class NewsletterEmailGroup(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email_group: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + total_subscribers: DF.ReadOnly | None + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/notification/__init__.py b/xhiveframework/email/doctype/notification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/notification/notification.js b/xhiveframework/email/doctype/notification/notification.js new file mode 100644 index 0000000..93429bb --- /dev/null +++ b/xhiveframework/email/doctype/notification/notification.js @@ -0,0 +1,201 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.notification = { + setup_fieldname_select: function (frm) { + // get the doctype to update fields + if (!frm.doc.document_type) { + return; + } + + xhiveframework.model.with_doctype(frm.doc.document_type, function () { + let get_select_options = function (df, parent_field) { + // Append parent_field name along with fieldname for child table fields + let select_value = parent_field ? df.fieldname + "," + parent_field : df.fieldname; + + return { + value: select_value, + label: df.fieldname + " (" + __(df.label, null, df.parent) + ")", + }; + }; + + let get_date_change_options = function () { + let date_options = $.map(fields, function (d) { + return d.fieldtype == "Date" || d.fieldtype == "Datetime" + ? get_select_options(d) + : null; + }); + // append creation and modified date to Date Change field + return date_options.concat([ + { value: "creation", label: `creation (${__("Created On")})` }, + { value: "modified", label: `modified (${__("Last Modified Date")})` }, + ]); + }; + + let fields = xhiveframework.get_doc("DocType", frm.doc.document_type).fields; + let options = $.map(fields, function (d) { + return xhiveframework.model.no_value_type.includes(d.fieldtype) + ? null + : get_select_options(d); + }); + + // set value changed options + frm.set_df_property("value_changed", "options", [""].concat(options)); + frm.set_df_property("set_property_after_alert", "options", [""].concat(options)); + + // set date changed options + frm.set_df_property("date_changed", "options", get_date_change_options()); + + let receiver_fields = []; + if (frm.doc.channel === "Email") { + receiver_fields = $.map(fields, function (d) { + // Add User and Email fields from child into select dropdown + if (xhiveframework.model.table_fields.includes(d.fieldtype)) { + let child_fields = xhiveframework.get_doc("DocType", d.options).fields; + return $.map(child_fields, function (df) { + return df.options == "Email" || + (df.options == "User" && df.fieldtype == "Link") + ? get_select_options(df, d.fieldname) + : null; + }); + // Add User and Email fields from parent into select dropdown + } else { + return d.options == "Email" || + (d.options == "User" && d.fieldtype == "Link") + ? get_select_options(d) + : null; + } + }); + } else if (["WhatsApp", "SMS"].includes(frm.doc.channel)) { + receiver_fields = $.map(fields, function (d) { + return d.options == "Phone" ? get_select_options(d) : null; + }); + } + + // set email recipient options + frm.fields_dict.recipients.grid.update_docfield_property( + "receiver_by_document_field", + "options", + [""].concat(["owner"]).concat(receiver_fields) + ); + }); + }, + setup_example_message: function (frm) { + let template = ""; + if (frm.doc.channel === "Email") { + template = `
      Message Example
      + +
      <h3>Order Overdue</h3>
      +
      +<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>
      +
      +<!-- show last comment -->
      +{% if comments %}
      +Last comment: {{ comments[-1].comment }} by {{ comments[-1].by }}
      +{% endif %}
      +
      +<h4>Details</h4>
      +
      +<ul>
      +<li>Customer: {{ doc.customer }}
      +<li>Amount: {{ doc.grand_total }}
      +</ul>
      +
      + `; + } else if (["Slack", "System Notification", "SMS"].includes(frm.doc.channel)) { + template = `
      Message Example
      + +
      *Order Overdue*
      +
      +Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.
      +
      +
      +{% if comments %}
      +Last comment: {{ comments[-1].comment }} by {{ comments[-1].by }}
      +{% endif %}
      +
      +*Details*
      +
      +• Customer: {{ doc.customer }}
      +• Amount: {{ doc.grand_total }}
      +
      `; + } + if (template) { + frm.set_df_property("message_examples", "options", template); + } + }, +}; + +xhiveframework.ui.form.on("Notification", { + onload: function (frm) { + frm.set_query("document_type", function () { + return { + filters: { + istable: 0, + }, + }; + }); + frm.set_query("print_format", function () { + return { + filters: { + doc_type: frm.doc.document_type, + }, + }; + }); + }, + refresh: function (frm) { + xhiveframework.notification.setup_fieldname_select(frm); + xhiveframework.notification.setup_example_message(frm); + + frm.add_fetch("sender", "email_id", "sender_email"); + frm.set_query("sender", () => { + return { + filters: { + enable_outgoing: 1, + }, + }; + }); + frm.get_field("is_standard").toggle(xhiveframework.boot.developer_mode); + frm.trigger("event"); + }, + document_type: function (frm) { + xhiveframework.notification.setup_fieldname_select(frm); + }, + view_properties: function (frm) { + xhiveframework.route_options = { doc_type: frm.doc.document_type }; + xhiveframework.set_route("Form", "Customize Form"); + }, + event: function (frm) { + if (["Days Before", "Days After"].includes(frm.doc.event)) { + frm.add_custom_button(__("Get Alerts for Today"), function () { + xhiveframework.call({ + method: "xhiveframework.email.doctype.notification.notification.get_documents_for_today", + args: { + notification: frm.doc.name, + }, + callback: function (r) { + if (r.message && r.message.length > 0) { + xhiveframework.msgprint(r.message.toString()); + } else { + xhiveframework.msgprint(__("No alerts for today")); + } + }, + }); + }); + } + }, + channel: function (frm) { + frm.toggle_reqd("recipients", frm.doc.channel == "Email"); + xhiveframework.notification.setup_fieldname_select(frm); + xhiveframework.notification.setup_example_message(frm); + if (frm.doc.channel === "SMS" && frm.doc.__islocal) { + frm.set_df_property( + "channel", + "description", + `To use SMS Channel, initialize SMS Settings.` + ); + } else { + frm.set_df_property("channel", "description", ` `); + } + }, +}); diff --git a/xhiveframework/email/doctype/notification/notification.json b/xhiveframework/email/doctype/notification/notification.json new file mode 100644 index 0000000..8b6900a --- /dev/null +++ b/xhiveframework/email/doctype/notification/notification.json @@ -0,0 +1,306 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2014-07-11 17:18:09.923399", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "enabled", + "column_break_2", + "channel", + "slack_webhook_url", + "filters", + "subject", + "document_type", + "is_standard", + "module", + "col_break_1", + "event", + "method", + "date_changed", + "days_in_advance", + "value_changed", + "sender", + "send_system_notification", + "sender_email", + "section_break_9", + "condition", + "column_break_6", + "html_7", + "property_section", + "set_property_after_alert", + "property_value", + "column_break_5", + "send_to_all_assignees", + "recipients", + "message_sb", + "message", + "message_examples", + "view_properties", + "column_break_25", + "attach_print", + "print_format" + ], + "fields": [ + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Email", + "depends_on": "eval: !doc.disable_channel", + "fieldname": "channel", + "fieldtype": "Select", + "label": "Channel", + "options": "Email\nSlack\nSystem Notification\nSMS", + "reqd": 1, + "set_only_once": 1 + }, + { + "depends_on": "eval:doc.channel=='Slack'", + "description": "To use Slack Channel, add a Slack Webhook URL.", + "fieldname": "slack_webhook_url", + "fieldtype": "Link", + "label": "Slack Channel", + "mandatory_depends_on": "eval:doc.channel=='Slack'", + "options": "Slack Webhook URL" + }, + { + "fieldname": "filters", + "fieldtype": "Section Break", + "label": "Filters" + }, + { + "depends_on": "eval: in_list(['Email', 'Slack', 'System Notification'], doc.channel)", + "description": "To add dynamic subject, use jinja tags like\n\n
      {{ doc.name }} Delivered
      ", + "fieldname": "subject", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Subject", + "mandatory_depends_on": "eval: in_list(['Email', 'Slack', 'System Notification'], doc.channel)" + }, + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "options": "DocType", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "no_copy": 1 + }, + { + "depends_on": "is_standard", + "fieldname": "module", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Module", + "options": "Module Def" + }, + { + "fieldname": "col_break_1", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.document_type", + "fieldname": "event", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Send Alert On", + "options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change\nMethod\nCustom", + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:doc.event=='Method'", + "description": "Trigger on valid methods like \"before_insert\", \"after_update\", etc (will depend on the DocType selected)", + "fieldname": "method", + "fieldtype": "Data", + "label": "Trigger Method" + }, + { + "depends_on": "eval:doc.document_type && (doc.event==\"Days After\" || doc.event==\"Days Before\")", + "description": "Send alert if date matches this field's value", + "fieldname": "date_changed", + "fieldtype": "Select", + "label": "Reference Date" + }, + { + "default": "0", + "depends_on": "eval:doc.document_type && (doc.event==\"Days After\" || doc.event==\"Days Before\")", + "description": "Send days before or after the reference date", + "fieldname": "days_in_advance", + "fieldtype": "Int", + "label": "Days Before or After" + }, + { + "depends_on": "eval:doc.document_type && doc.event==\"Value Change\"", + "description": "Send alert if this field's value changes", + "fieldname": "value_changed", + "fieldtype": "Select", + "label": "Value Changed" + }, + { + "depends_on": "eval: doc.channel == 'Email'", + "fieldname": "sender", + "fieldtype": "Link", + "label": "Sender", + "options": "Email Account" + }, + { + "fieldname": "sender_email", + "fieldtype": "Data", + "label": "Sender Email", + "options": "Email", + "read_only": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "description": "Optional: The alert will be sent if this expression is true", + "fieldname": "condition", + "fieldtype": "Code", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Condition" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "html_7", + "fieldtype": "HTML", + "options": "

      Condition Examples:

      \n
      doc.status==\"Open\"
      doc.due_date==nowdate()
      doc.total > 40000\n
      \n" + }, + { + "collapsible": 1, + "fieldname": "property_section", + "fieldtype": "Section Break", + "label": "Set Property After Alert" + }, + { + "fieldname": "set_property_after_alert", + "fieldtype": "Select", + "label": "Set Property After Alert" + }, + { + "fieldname": "property_value", + "fieldtype": "Data", + "label": "Value To Be Set" + }, + { + "depends_on": "eval:doc.channel !=\"Slack\"", + "fieldname": "column_break_5", + "fieldtype": "Section Break", + "label": "Recipients" + }, + { + "fieldname": "recipients", + "fieldtype": "Table", + "label": "Recipients", + "mandatory_depends_on": "eval:doc.channel!=='Slack' && !doc.send_to_all_assignees", + "options": "Notification Recipient" + }, + { + "fieldname": "message_sb", + "fieldtype": "Section Break", + "label": "Message" + }, + { + "default": "Add your message here", + "fieldname": "message", + "fieldtype": "Code", + "ignore_xss_filter": 1, + "label": "Message" + }, + { + "fieldname": "message_examples", + "fieldtype": "HTML", + "label": "Message Examples", + "options": "
      Message Example
      \n\n
      <h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.grand_total }}\n</ul>\n
      " + }, + { + "fieldname": "view_properties", + "fieldtype": "Button", + "label": "View Properties (via Customize Form)" + }, + { + "collapsible": 1, + "collapsible_depends_on": "attach_print", + "fieldname": "column_break_25", + "fieldtype": "Section Break", + "label": "Print Settings" + }, + { + "default": "0", + "fieldname": "attach_print", + "fieldtype": "Check", + "label": "Attach Print" + }, + { + "depends_on": "attach_print", + "fieldname": "print_format", + "fieldtype": "Link", + "label": "Print Format", + "options": "Print Format" + }, + { + "default": "0", + "depends_on": "eval: doc.channel !== 'System Notification'", + "description": "If enabled, the notification will show up in the notifications dropdown on the top right corner of the navigation bar.", + "fieldname": "send_system_notification", + "fieldtype": "Check", + "label": "Send System Notification" + }, + { + "default": "0", + "depends_on": "eval:doc.channel == 'Email'", + "fieldname": "send_to_all_assignees", + "fieldtype": "Check", + "label": "Send To All Assignees" + } + ], + "icon": "fa fa-envelope", + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-05-04 11:17:11.882314", + "modified_by": "Administrator", + "module": "Email", + "name": "Notification", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "export": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "subject", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/notification/notification.py b/xhiveframework/email/doctype/notification/notification.py new file mode 100644 index 0000000..ff3cb18 --- /dev/null +++ b/xhiveframework/email/doctype/notification/notification.py @@ -0,0 +1,532 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json +import os + +import xhiveframework +from xhiveframework import _ +from xhiveframework.core.doctype.role.role import get_info_based_on_role, get_user_info +from xhiveframework.core.doctype.sms_settings.sms_settings import send_sms +from xhiveframework.desk.doctype.notification_log.notification_log import enqueue_create_notification +from xhiveframework.integrations.doctype.slack_webhook_url.slack_webhook_url import send_slack_message +from xhiveframework.model.document import Document +from xhiveframework.modules.utils import export_module_json, get_doc_module +from xhiveframework.utils import add_to_date, cast, is_html, nowdate, validate_email_address +from xhiveframework.utils.jinja import validate_template +from xhiveframework.utils.safe_exec import get_safe_globals + + +class Notification(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.email.doctype.notification_recipient.notification_recipient import ( + NotificationRecipient, + ) + from xhiveframework.types import DF + + attach_print: DF.Check + channel: DF.Literal["Email", "Slack", "System Notification", "SMS"] + condition: DF.Code | None + date_changed: DF.Literal[None] + days_in_advance: DF.Int + document_type: DF.Link + enabled: DF.Check + event: DF.Literal[ + "", + "New", + "Save", + "Submit", + "Cancel", + "Days After", + "Days Before", + "Value Change", + "Method", + "Custom", + ] + is_standard: DF.Check + message: DF.Code | None + method: DF.Data | None + module: DF.Link | None + print_format: DF.Link | None + property_value: DF.Data | None + recipients: DF.Table[NotificationRecipient] + send_system_notification: DF.Check + send_to_all_assignees: DF.Check + sender: DF.Link | None + sender_email: DF.Data | None + set_property_after_alert: DF.Literal[None] + slack_webhook_url: DF.Link | None + subject: DF.Data | None + value_changed: DF.Literal[None] + # end: auto-generated types + + def onload(self): + """load message""" + if self.is_standard: + self.message = self.get_template() + + def autoname(self): + if not self.name: + self.name = self.subject + + def validate(self): + if self.channel in ("Email", "Slack", "System Notification"): + validate_template(self.subject) + + validate_template(self.message) + + if self.event in ("Days Before", "Days After") and not self.date_changed: + xhiveframework.throw(_("Please specify which date field must be checked")) + + if self.event == "Value Change" and not self.value_changed: + xhiveframework.throw(_("Please specify which value field must be checked")) + + self.validate_forbidden_types() + self.validate_condition() + self.validate_standard() + xhiveframework.cache.hdel("notifications", self.document_type) + + def on_update(self): + xhiveframework.cache.hdel("notifications", self.document_type) + path = export_module_json(self, self.is_standard, self.module) + if path: + # js + if not os.path.exists(path + ".md") and not os.path.exists(path + ".html"): + with open(path + ".md", "w") as f: + f.write(self.message) + + # py + if not os.path.exists(path + ".py"): + with open(path + ".py", "w") as f: + f.write( + """import xhiveframework + +def get_context(context): + # do your magic here + pass +""" + ) + + def validate_standard(self): + if self.is_standard and self.enabled and not xhiveframework.conf.developer_mode: + xhiveframework.throw( + _("Cannot edit Standard Notification. To edit, please disable this and duplicate it") + ) + + def validate_condition(self): + temp_doc = xhiveframework.new_doc(self.document_type) + if self.condition: + try: + xhiveframework.safe_eval(self.condition, None, get_context(temp_doc.as_dict())) + except Exception: + xhiveframework.throw(_("The Condition '{0}' is invalid").format(self.condition)) + + def validate_forbidden_types(self): + forbidden_document_types = ("Email Queue",) + if self.document_type in forbidden_document_types or xhiveframework.get_meta(self.document_type).istable: + # currently notifications don't work on child tables as events are not fired for each record of child table + + xhiveframework.throw(_("Cannot set Notification on Document Type {0}").format(self.document_type)) + + def get_documents_for_today(self): + """get list of documents that will be triggered today""" + docs = [] + + diff_days = self.days_in_advance + if self.event == "Days After": + diff_days = -diff_days + + reference_date = add_to_date(nowdate(), days=diff_days) + reference_date_start = reference_date + " 00:00:00.000000" + reference_date_end = reference_date + " 23:59:59.000000" + + doc_list = xhiveframework.get_all( + self.document_type, + fields="name", + filters=[ + {self.date_changed: (">=", reference_date_start)}, + {self.date_changed: ("<=", reference_date_end)}, + ], + ) + + for d in doc_list: + doc = xhiveframework.get_doc(self.document_type, d.name) + + if self.condition and not xhiveframework.safe_eval(self.condition, None, get_context(doc)): + continue + + docs.append(doc) + + return docs + + def send(self, doc): + """Build recipients and send Notification""" + + context = get_context(doc) + context = {"doc": doc, "alert": self, "comments": None} + if doc.get("_comments"): + context["comments"] = json.loads(doc.get("_comments")) + + if self.is_standard: + self.load_standard_properties(context) + try: + if self.channel == "Email": + self.send_an_email(doc, context) + + if self.channel == "Slack": + self.send_a_slack_msg(doc, context) + + if self.channel == "SMS": + self.send_sms(doc, context) + + if self.channel == "System Notification" or self.send_system_notification: + self.create_system_notification(doc, context) + + except Exception: + self.log_error("Failed to send Notification") + + if self.set_property_after_alert: + allow_update = True + if ( + doc.docstatus.is_submitted() + and not doc.meta.get_field(self.set_property_after_alert).allow_on_submit + ): + allow_update = False + try: + if allow_update and not doc.flags.in_notification_update: + fieldname = self.set_property_after_alert + value = self.property_value + if doc.meta.get_field(fieldname).fieldtype in xhiveframework.model.numeric_fieldtypes: + value = xhiveframework.utils.cint(value) + + doc.reload() + doc.set(fieldname, value) + doc.flags.updater_reference = { + "doctype": self.doctype, + "docname": self.name, + "label": _("via Notification"), + } + doc.flags.in_notification_update = True + doc.save(ignore_permissions=True) + doc.flags.in_notification_update = False + except Exception: + self.log_error("Document update failed") + + def create_system_notification(self, doc, context): + subject = self.subject + if "{" in subject: + subject = xhiveframework.render_template(self.subject, context) + + attachments = self.get_attachment(doc) + + recipients, cc, bcc = self.get_list_of_recipients(doc, context) + + users = recipients + cc + bcc + + if not users: + return + + notification_doc = { + "type": "Alert", + "document_type": doc.doctype, + "document_name": doc.name, + "subject": subject, + "from_user": doc.modified_by or doc.owner, + "email_content": xhiveframework.render_template(self.message, context), + "attached_file": attachments and json.dumps(attachments[0]), + } + enqueue_create_notification(users, notification_doc) + + def send_an_email(self, doc, context): + from email.utils import formataddr + + from xhiveframework.core.doctype.communication.email import _make as make_communication + + subject = self.subject + if "{" in subject: + subject = xhiveframework.render_template(self.subject, context) + + attachments = self.get_attachment(doc) + recipients, cc, bcc = self.get_list_of_recipients(doc, context) + if not (recipients or cc or bcc): + return + + sender = None + message = xhiveframework.render_template(self.message, context) + if self.sender and self.sender_email: + sender = formataddr((self.sender, self.sender_email)) + + communication = None + # Add mail notification to communication list + # No need to add if it is already a communication. + if doc.doctype != "Communication": + communication = make_communication( + doctype=doc.doctype, + name=doc.name, + content=message, + subject=subject, + sender=sender, + recipients=recipients, + communication_medium="Email", + send_email=False, + attachments=attachments, + cc=cc, + bcc=bcc, + communication_type="Automated Message", + ).get("name") + + xhiveframework.sendmail( + recipients=recipients, + subject=subject, + sender=sender, + cc=cc, + bcc=bcc, + message=message, + reference_doctype=doc.doctype, + reference_name=doc.name, + attachments=attachments, + expose_recipients="header", + print_letterhead=((attachments and attachments[0].get("print_letterhead")) or False), + communication=communication, + ) + + def send_a_slack_msg(self, doc, context): + send_slack_message( + webhook_url=self.slack_webhook_url, + message=xhiveframework.render_template(self.message, context), + reference_doctype=doc.doctype, + reference_name=doc.name, + ) + + def send_sms(self, doc, context): + send_sms( + receiver_list=self.get_receiver_list(doc, context), + msg=xhiveframework.render_template(self.message, context), + ) + + def get_list_of_recipients(self, doc, context): + recipients = [] + cc = [] + bcc = [] + for recipient in self.recipients: + if recipient.condition: + if not xhiveframework.safe_eval(recipient.condition, None, context): + continue + if recipient.receiver_by_document_field: + fields = recipient.receiver_by_document_field.split(",") + # fields from child table + if len(fields) > 1: + for d in doc.get(fields[1]): + email_id = d.get(fields[0]) + if validate_email_address(email_id): + recipients.append(email_id) + # field from parent doc + else: + email_ids_value = doc.get(fields[0]) + if validate_email_address(email_ids_value): + email_ids = email_ids_value.replace(",", "\n") + recipients = recipients + email_ids.split("\n") + + cc.extend(get_emails_from_template(recipient.cc, context)) + bcc.extend(get_emails_from_template(recipient.bcc, context)) + + # For sending emails to specified role + if recipient.receiver_by_role: + emails = get_info_based_on_role(recipient.receiver_by_role, "email", ignore_permissions=True) + + for email in emails: + recipients = recipients + email.split("\n") + + if self.send_to_all_assignees: + recipients = recipients + get_assignees(doc) + + return list(set(recipients)), list(set(cc)), list(set(bcc)) + + def get_receiver_list(self, doc, context): + """return receiver list based on the doc field and role specified""" + receiver_list = [] + for recipient in self.recipients: + if recipient.condition: + if not xhiveframework.safe_eval(recipient.condition, None, context): + continue + + # For sending messages to the owner's mobile phone number + if recipient.receiver_by_document_field == "owner": + receiver_list += get_user_info([dict(user_name=doc.get("owner"))], "mobile_no") + # For sending messages to the number specified in the receiver field + elif recipient.receiver_by_document_field: + receiver_list.append(doc.get(recipient.receiver_by_document_field)) + + # For sending messages to specified role + if recipient.receiver_by_role: + receiver_list += get_info_based_on_role( + recipient.receiver_by_role, "mobile_no", ignore_permissions=True + ) + + return receiver_list + + def get_attachment(self, doc): + """check print settings are attach the pdf""" + if not self.attach_print: + return None + + print_settings = xhiveframework.get_doc("Print Settings", "Print Settings") + if (doc.docstatus == 0 and not print_settings.allow_print_for_draft) or ( + doc.docstatus == 2 and not print_settings.allow_print_for_cancelled + ): + # ignoring attachment as draft and cancelled documents are not allowed to print + status = "Draft" if doc.docstatus == 0 else "Cancelled" + xhiveframework.throw( + _( + """Not allowed to attach {0} document, please enable Allow Print For {0} in Print Settings""" + ).format(status), + title=_("Error in Notification"), + ) + else: + return [ + { + "print_format_attachment": 1, + "doctype": doc.doctype, + "name": doc.name, + "print_format": self.print_format, + "print_letterhead": print_settings.with_letterhead, + "lang": xhiveframework.db.get_value("Print Format", self.print_format, "default_print_language") + if self.print_format + else "en", + } + ] + + def get_template(self): + module = get_doc_module(self.module, self.doctype, self.name) + + def load_template(extn): + template = "" + template_path = os.path.join(os.path.dirname(module.__file__), xhiveframework.scrub(self.name) + extn) + if os.path.exists(template_path): + with open(template_path) as f: + template = f.read() + return template + + return load_template(".html") or load_template(".md") + + def load_standard_properties(self, context): + """load templates and run get_context""" + module = get_doc_module(self.module, self.doctype, self.name) + if module: + if hasattr(module, "get_context"): + out = module.get_context(context) + if out: + context.update(out) + + self.message = self.get_template() + + if not is_html(self.message): + self.message = xhiveframework.utils.md_to_html(self.message) + + def on_trash(self): + xhiveframework.cache.hdel("notifications", self.document_type) + + +@xhiveframework.whitelist() +def get_documents_for_today(notification): + notification = xhiveframework.get_doc("Notification", notification) + notification.check_permission("read") + return [d.name for d in notification.get_documents_for_today()] + + +def trigger_daily_alerts(): + trigger_notifications(None, "daily") + + +def trigger_notifications(doc, method=None): + if xhiveframework.flags.in_import or xhiveframework.flags.in_patch: + # don't send notifications while syncing or patching + return + + if method == "daily": + doc_list = xhiveframework.get_all( + "Notification", filters={"event": ("in", ("Days Before", "Days After")), "enabled": 1} + ) + for d in doc_list: + alert = xhiveframework.get_doc("Notification", d.name) + + for doc in alert.get_documents_for_today(): + evaluate_alert(doc, alert, alert.event) + xhiveframework.db.commit() + + +def evaluate_alert(doc: Document, alert, event): + from jinja2 import TemplateError + + try: + if isinstance(alert, str): + alert = xhiveframework.get_doc("Notification", alert) + + context = get_context(doc) + + if alert.condition: + if not xhiveframework.safe_eval(alert.condition, None, context): + return + + if event == "Value Change" and not doc.is_new(): + if not xhiveframework.db.has_column(doc.doctype, alert.value_changed): + alert.db_set("enabled", 0) + alert.log_error(f"Notification {alert.name} has been disabled due to missing field") + return + + doc_before_save = doc.get_doc_before_save() + field_value_before_save = doc_before_save.get(alert.value_changed) if doc_before_save else None + + fieldtype = doc.meta.get_field(alert.value_changed).fieldtype + if cast(fieldtype, doc.get(alert.value_changed)) == cast(fieldtype, field_value_before_save): + # value not changed + return + + if event != "Value Change" and not doc.is_new(): + # reload the doc for the latest values & comments, + # except for validate type event. + doc.reload() + alert.send(doc) + except TemplateError: + message = _("Error while evaluating Notification {0}. Please fix your template.").format( + xhiveframework.utils.get_link_to_form("Notification", alert.name) + ) + xhiveframework.throw(message, title=_("Error in Notification")) + except Exception as e: + title = str(e) + xhiveframework.log_error(title=title) + + msg = f"
      {title}{message}
      " + xhiveframework.throw(msg, title=_("Error in Notification")) + + +def get_context(doc): + return { + "doc": doc, + "nowdate": nowdate, + "xhiveframework": xhiveframework._dict(utils=get_safe_globals().get("xhiveframework").get("utils")), + } + + +def get_assignees(doc): + assignees = [] + assignees = xhiveframework.get_all( + "ToDo", + filters={"status": "Open", "reference_name": doc.name, "reference_type": doc.doctype}, + fields=["allocated_to"], + ) + + return [d.allocated_to for d in assignees] + + +def get_emails_from_template(template, context): + if not template: + return () + + emails = xhiveframework.render_template(template, context) if "{" in template else template + return filter(None, emails.replace(",", "\n").split("\n")) diff --git a/xhiveframework/email/doctype/notification/test_notification.py b/xhiveframework/email/doctype/notification/test_notification.py new file mode 100644 index 0000000..db659df --- /dev/null +++ b/xhiveframework/email/doctype/notification/test_notification.py @@ -0,0 +1,380 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +from contextlib import contextmanager + +import xhiveframework +import xhiveframework.utils +import xhiveframework.utils.scheduler +from xhiveframework.desk.form import assign_to +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["User", "Notification"] + + +@contextmanager +def get_test_notification(config): + try: + notification = xhiveframework.get_doc(doctype="Notification", **config).insert() + yield notification + finally: + notification.delete() + + +class TestNotification(XhiveFrameworkTestCase): + def setUp(self): + xhiveframework.db.delete("Email Queue") + xhiveframework.set_user("test@example.com") + + if not xhiveframework.db.exists("Notification", {"name": "ToDo Status Update"}, "name"): + notification = xhiveframework.new_doc("Notification") + notification.name = "ToDo Status Update" + notification.subject = "ToDo Status Update" + notification.document_type = "ToDo" + notification.event = "Value Change" + notification.value_changed = "status" + notification.send_to_all_assignees = 1 + notification.set_property_after_alert = "description" + notification.property_value = "Changed by Notification" + notification.save() + + if not xhiveframework.db.exists("Notification", {"name": "Contact Status Update"}, "name"): + notification = xhiveframework.new_doc("Notification") + notification.name = "Contact Status Update" + notification.subject = "Contact Status Update" + notification.document_type = "Contact" + notification.event = "Value Change" + notification.value_changed = "status" + notification.message = "Test Contact Update" + notification.append("recipients", {"receiver_by_document_field": "email_id,email_ids"}) + notification.save() + + def tearDown(self): + xhiveframework.set_user("Administrator") + + def test_new_and_save(self): + """Check creating a new communication triggers a notification.""" + communication = xhiveframework.new_doc("Communication") + communication.communication_type = "Comment" + communication.subject = "test" + communication.content = "test" + communication.insert(ignore_permissions=True) + + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + { + "reference_doctype": "Communication", + "reference_name": communication.name, + "status": "Not Sent", + }, + ) + ) + xhiveframework.db.delete("Email Queue") + + communication.reload() + communication.content = "test 2" + communication.save() + + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + { + "reference_doctype": "Communication", + "reference_name": communication.name, + "status": "Not Sent", + }, + ) + ) + + self.assertEqual(xhiveframework.db.get_value("Communication", communication.name, "subject"), "__testing__") + + def test_condition(self): + """Check notification is triggered based on a condition.""" + event = xhiveframework.new_doc("Event") + event.subject = "test" + event.event_type = "Private" + event.starts_on = "2014-06-06 12:00:00" + event.insert() + + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + event.event_type = "Public" + event.save() + + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + # Make sure that we track the triggered notifications in communication doctype. + self.assertTrue( + xhiveframework.db.get_value( + "Communication", + { + "reference_doctype": "Event", + "reference_name": event.name, + "communication_type": "Automated Message", + }, + ) + ) + + def test_invalid_condition(self): + xhiveframework.set_user("Administrator") + notification = xhiveframework.new_doc("Notification") + notification.subject = "test" + notification.document_type = "ToDo" + notification.send_alert_on = "New" + notification.message = "test" + + recipent = xhiveframework.new_doc("Notification Recipient") + recipent.receiver_by_document_field = "owner" + + notification.recipents = recipent + notification.condition = "test" + + self.assertRaises(xhiveframework.ValidationError, notification.save) + notification.delete() + + def test_value_changed(self): + event = xhiveframework.new_doc("Event") + event.subject = "test" + event.event_type = "Private" + event.starts_on = "2014-06-06 12:00:00" + event.insert() + + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + event.subject = "test 1" + event.save() + + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + event.description = "test" + event.save() + + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + def test_alert_disabled_on_wrong_field(self): + xhiveframework.set_user("Administrator") + notification = xhiveframework.get_doc( + { + "doctype": "Notification", + "subject": "_Test Notification for wrong field", + "document_type": "Event", + "event": "Value Change", + "attach_print": 0, + "value_changed": "description1", + "message": "Description changed", + "recipients": [{"receiver_by_document_field": "owner"}], + } + ).insert() + xhiveframework.db.commit() + + event = xhiveframework.new_doc("Event") + event.subject = "test-2" + event.event_type = "Private" + event.starts_on = "2014-06-06 12:00:00" + event.insert() + event.subject = "test 1" + event.save() + + # verify that notification is disabled + notification.reload() + self.assertEqual(notification.enabled, 0) + notification.delete() + event.delete() + + def test_date_changed(self): + event = xhiveframework.new_doc("Event") + event.subject = "test" + event.event_type = "Private" + event.starts_on = "2014-01-01 12:00:00" + event.insert() + + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + xhiveframework.set_user("Administrator") + xhiveframework.get_doc( + "Scheduled Job Type", + dict(method="xhiveframework.email.doctype.notification.notification.trigger_daily_alerts"), + ).execute() + + # not today, so no alert + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + event.starts_on = xhiveframework.utils.add_days(xhiveframework.utils.nowdate(), 2) + " 12:00:00" + event.save() + + # Value Change notification alert will be trigger as description is not changed + # mail will not be sent + self.assertFalse( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + xhiveframework.get_doc( + "Scheduled Job Type", + dict(method="xhiveframework.email.doctype.notification.notification.trigger_daily_alerts"), + ).execute() + + # today so show alert + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "Event", "reference_name": event.name, "status": "Not Sent"}, + ) + ) + + def test_cc_jinja(self): + xhiveframework.db.delete("User", {"email": "test_jinja@example.com"}) + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Email Queue Recipient") + + test_user = xhiveframework.new_doc("User") + test_user.name = "test_jinja" + test_user.first_name = "test_jinja" + test_user.email = "test_jinja@example.com" + + test_user.insert(ignore_permissions=True) + + self.assertTrue( + xhiveframework.db.get_value( + "Email Queue", + {"reference_doctype": "User", "reference_name": test_user.name, "status": "Not Sent"}, + ) + ) + + self.assertTrue(xhiveframework.db.get_value("Email Queue Recipient", {"recipient": "test_jinja@example.com"})) + + xhiveframework.db.delete("User", {"email": "test_jinja@example.com"}) + xhiveframework.db.delete("Email Queue") + xhiveframework.db.delete("Email Queue Recipient") + + def test_notification_to_assignee(self): + todo = xhiveframework.new_doc("ToDo") + todo.description = "Test Notification" + todo.save() + + assign_to.add( + { + "assign_to": ["test2@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo", + } + ) + + assign_to.add( + { + "assign_to": ["test1@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo", + } + ) + + # change status of todo + todo.status = "Closed" + todo.save() + + email_queue = xhiveframework.get_doc( + "Email Queue", {"reference_doctype": "ToDo", "reference_name": todo.name} + ) + + self.assertTrue(email_queue) + + # check if description is changed after alert since set_property_after_alert is set + self.assertEqual(todo.description, "Changed by Notification") + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue("test2@example.com" in recipients) + self.assertTrue("test1@example.com" in recipients) + + def test_notification_by_child_table_field(self): + contact = xhiveframework.new_doc("Contact") + contact.first_name = "John Doe" + contact.status = "Open" + contact.append("email_ids", {"email_id": "test2@example.com", "is_primary": 1}) + + contact.append("email_ids", {"email_id": "test1@example.com"}) + + contact.save() + + # change status of contact + contact.status = "Replied" + contact.save() + + email_queue = xhiveframework.get_doc( + "Email Queue", {"reference_doctype": "Contact", "reference_name": contact.name} + ) + + self.assertTrue(email_queue) + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue("test2@example.com" in recipients) + self.assertTrue("test1@example.com" in recipients) + + def test_notification_value_change_casted_types(self): + """Make sure value change event dont fire because of incorrect type comparisons.""" + xhiveframework.set_user("Administrator") + + notification = { + "document_type": "User", + "subject": "User changed birthdate", + "event": "Value Change", + "channel": "System Notification", + "value_changed": "birth_date", + "recipients": [{"receiver_by_document_field": "email"}], + } + + with get_test_notification(notification) as n: + xhiveframework.db.delete("Notification Log", {"subject": n.subject}) + + user = xhiveframework.get_doc("User", "test@example.com") + user.birth_date = xhiveframework.utils.add_days(user.birth_date, 1) + user.save() + + user.reload() + user.birth_date = xhiveframework.utils.getdate(user.birth_date) + user.save() + self.assertEqual(1, xhiveframework.db.count("Notification Log", {"subject": n.subject})) + + @classmethod + def tearDownClass(cls): + xhiveframework.delete_doc_if_exists("Notification", "ToDo Status Update") + xhiveframework.delete_doc_if_exists("Notification", "Contact Status Update") diff --git a/xhiveframework/email/doctype/notification/test_records.json b/xhiveframework/email/doctype/notification/test_records.json new file mode 100644 index 0000000..665f800 --- /dev/null +++ b/xhiveframework/email/doctype/notification/test_records.json @@ -0,0 +1,76 @@ +[ + { + "doctype": "Notification", + "subject":"_Test Notification 1", + "document_type": "Communication", + "event": "New", + "attach_print": 0, + "message": "New comment {{ doc.content }} created", + "condition": "doc.communication_type=='Comment'", + "recipients": [ + { "receiver_by_document_field": "owner" } + ] + }, + { + "doctype": "Notification", + "subject":"_Test Notification 2", + "document_type": "Communication", + "event": "Save", + "attach_print": 0, + "message": "New comment {{ doc.content }} saved", + "condition": "doc.communication_type=='Comment'", + "recipients": [ + { "receiver_by_document_field": "owner" } + ], + "set_property_after_alert": "subject", + "property_value": "__testing__" + }, + { + "doctype": "Notification", + "subject":"_Test Notification 3", + "document_type": "Event", + "event": "Save", + "attach_print": 0, + "condition": "doc.event_type=='Public'", + "message": "A new public event {{ doc.subject }} on {{ doc.starts_on }} is created", + "recipients": [ + { "receiver_by_document_field": "owner" } + ] + }, + { + "doctype": "Notification", + "subject":"_Test Notification 4", + "document_type": "Event", + "event": "Value Change", + "attach_print": 0, + "value_changed": "description", + "message": "Description changed", + "recipients": [ + { "receiver_by_document_field": "owner" } + ] + }, + { + "doctype": "Notification", + "subject":"_Test Notification 5", + "document_type": "Event", + "event": "Days Before", + "attach_print": 0, + "date_changed": "starts_on", + "days_in_advance": 2, + "message": "Description changed", + "recipients": [ + { "receiver_by_document_field": "owner" } + ] + }, + { + "doctype": "Notification", + "subject":"_Test Notification 6", + "document_type": "User", + "event": "New", + "attach_print": 0, + "message": "New user {{ doc.name }} created", + "recipients": [ + { "receiver_by_document_field": "owner", "cc": "{{ doc.email }}" } + ] + } +] diff --git a/xhiveframework/email/doctype/notification_recipient/__init__.py b/xhiveframework/email/doctype/notification_recipient/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/notification_recipient/notification_recipient.json b/xhiveframework/email/doctype/notification_recipient/notification_recipient.json new file mode 100644 index 0000000..0670320 --- /dev/null +++ b/xhiveframework/email/doctype/notification_recipient/notification_recipient.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "creation": "2014-07-11 17:19:37.037109", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "receiver_by_document_field", + "receiver_by_role", + "cc", + "bcc", + "condition" + ], + "fields": [ + { + "depends_on": "eval:parent.channel=='Email'", + "description": "Optional: Always send to these ids. Each Email Address on a new row", + "fieldname": "cc", + "fieldtype": "Code", + "label": "CC" + }, + { + "depends_on": "eval:parent.channel=='Email'", + "fieldname": "bcc", + "fieldtype": "Code", + "label": "BCC" + }, + { + "description": "Expression, Optional", + "fieldname": "condition", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Condition" + }, + { + "fieldname": "receiver_by_document_field", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Receiver By Document Field" + }, + { + "fieldname": "receiver_by_role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Receiver By Role", + "options": "Role" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-01 17:40:27.289105", + "modified_by": "Administrator", + "module": "Email", + "name": "Notification Recipient", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/notification_recipient/notification_recipient.py b/xhiveframework/email/doctype/notification_recipient/notification_recipient.py new file mode 100644 index 0000000..ffc8500 --- /dev/null +++ b/xhiveframework/email/doctype/notification_recipient/notification_recipient.py @@ -0,0 +1,25 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from xhiveframework.model.document import Document + + +class NotificationRecipient(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + bcc: DF.Code | None + cc: DF.Code | None + condition: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + receiver_by_document_field: DF.Literal[None] + receiver_by_role: DF.Link | None + # end: auto-generated types + pass diff --git a/xhiveframework/email/doctype/unhandled_email/__init__.py b/xhiveframework/email/doctype/unhandled_email/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/doctype/unhandled_email/test_unhandled_email.py b/xhiveframework/email/doctype/unhandled_email/test_unhandled_email.py new file mode 100644 index 0000000..edaf8d0 --- /dev/null +++ b/xhiveframework/email/doctype/unhandled_email/test_unhandled_email.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Unhandled Emails') + + +class TestUnhandledEmail(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/email/doctype/unhandled_email/unhandled_email.json b/xhiveframework/email/doctype/unhandled_email/unhandled_email.json new file mode 100644 index 0000000..d904536 --- /dev/null +++ b/xhiveframework/email/doctype/unhandled_email/unhandled_email.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "creation": "2016-04-14 09:41:45.892975", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "email_account", + "uid", + "reason", + "message_id", + "raw" + ], + "fields": [ + { + "fieldname": "email_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Email Account", + "options": "Email Account" + }, + { + "fieldname": "uid", + "fieldtype": "Data", + "label": "UID" + }, + { + "fieldname": "reason", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Reason" + }, + { + "fieldname": "message_id", + "fieldtype": "Code", + "label": "Message-id" + }, + { + "fieldname": "raw", + "fieldtype": "Code", + "label": "Raw Email" + } + ], + "in_create": 1, + "links": [], + "modified": "2022-08-03 12:20:51.822287", + "modified_by": "Administrator", + "module": "Email", + "name": "Unhandled Email", + "owner": "Administrator", + "permissions": [ + { + "read": 1, + "role": "System Manager" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/email/doctype/unhandled_email/unhandled_email.py b/xhiveframework/email/doctype/unhandled_email/unhandled_email.py new file mode 100644 index 0000000..a5cb589 --- /dev/null +++ b/xhiveframework/email/doctype/unhandled_email/unhandled_email.py @@ -0,0 +1,31 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class UnhandledEmail(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + email_account: DF.Link | None + message_id: DF.Code | None + raw: DF.Code | None + reason: DF.LongText | None + uid: DF.Data | None + # end: auto-generated types + + @staticmethod + def clear_old_logs(days=30): + xhiveframework.db.delete( + "Unhandled Email", + { + "modified": ("<", xhiveframework.utils.add_days(xhiveframework.utils.nowdate(), -1 * days)), + }, + ) diff --git a/xhiveframework/email/email.md b/xhiveframework/email/email.md new file mode 100644 index 0000000..dbd1952 --- /dev/null +++ b/xhiveframework/email/email.md @@ -0,0 +1,65 @@ +# Email Architecture + +This document describes the high-level architecture of how emails work in xhiveframework. + +> NOTE: There are mentions of different types of workers to execute jobs from different types of queues in the explanations below, however only a single type of worker can also be used to do the same. + +### High-Level View + +#### Pulling/Sending: + +![email-pull-flow](assets/images/email-pull-flow.png) + +The same follows for email sending with the only difference being in the jobs queued. + +The scheduler schedules/pushes the `email.pull`/`queue.flush` job into the default queue which is picked up by the default worker and upon execution enqueues `pull_from_email_account`/`send_mail` job into the short queue and the job is then picked up by the short worker and the email is pulled in/sent out of the system. + + +### Code Map + +This section talks briefly about various important directories/files. + +#### `doctype/email_queue` + +Contains all logic about email sending. Every email to be sent is stored in `Email Queue` DocType (Newsletter(s) included). + +#### `receive.py` + +Contains all the logic and classes about email receiving. + +#### `doctype/email_account` + +Contains logic for validating connection/auth for email account(s). + +Also, contains the abstraction for facilitating email receiving. + +#### `doctype/newsletter` + +Contains all the logic about newsletter scheduling, subscribing and unsubscribing. + +Newsletter has a similar design as to how normal emails are sent except for the initial scheduled job (`newsletter.send_scheduled_email`) and the fact that newsletter recipients are batched, primarily for SMTP connection reusing & mitigating any potential timeouts (available as a general feature as well). + +Each batch is queued as a separate job (`QueueBuilder.send_emails`) into the long queue by the default worker which is then picked up by the long worker. + +#### `doctype/notification` + +Contains all logic about handling various types of notifications by the system. + +#### `email_body.py` + +Contains logic about creating the email body. We use Python's email std lib for body generation. + +The email message format/syntax is described in [RFC5322](https://datatracker.ietf.org/doc/html/rfc5322) + + +### Observability + +At the application level, + +`RQ Job` DocType is the most useful in knowing the status of any type of "recent" email job (pull/send). + +`Email Queue` DocType is self-contained and stores all the errored out (along with traceback) as well as the sent/partially sent mails from the system. + +`Communication` DocType stores all the successfully received emails and `Unhandled Email` DocType stores all the "unhandled" ones during receiving. + +Apart from these, the `Error Log` DocType also comes into help from time to time. diff --git a/xhiveframework/email/email_body.py b/xhiveframework/email/email_body.py new file mode 100755 index 0000000..9a6048c --- /dev/null +++ b/xhiveframework/email/email_body.py @@ -0,0 +1,591 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import email.utils +import os +import re +from email import policy +from email.header import Header +from email.mime.multipart import MIMEMultipart + +import xhiveframework +from xhiveframework.email.doctype.email_account.email_account import EmailAccount +from xhiveframework.utils import ( + cint, + expand_relative_urls, + get_url, + markdown, + parse_addr, + random_string, + scrub_urls, + split_emails, + strip, + to_markdown, +) +from xhiveframework.utils.pdf import get_pdf + +EMBED_PATTERN = re.compile("""embed=["'](.*?)["']""") + + +def get_email( + recipients, + sender="", + msg="", + subject="[No Subject]", + text_content=None, + footer=None, + print_html=None, + formatted=None, + attachments=None, + content=None, + reply_to=None, + cc=None, + bcc=None, + email_account=None, + expose_recipients=None, + inline_images=None, + header=None, +): + """Prepare an email with the following format: + - multipart/mixed + - multipart/alternative + - text/plain + - multipart/related + - text/html + - inline image + - attachment + """ + content = content or msg + + if cc is None: + cc = [] + if bcc is None: + bcc = [] + if inline_images is None: + inline_images = [] + + emailobj = EMail( + sender, + recipients, + subject, + reply_to=reply_to, + cc=cc, + bcc=bcc, + email_account=email_account, + expose_recipients=expose_recipients, + ) + + if not content.strip().startswith("<"): + content = markdown(content) + + emailobj.set_html( + content, + text_content, + footer=footer, + header=header, + print_html=print_html, + formatted=formatted, + inline_images=inline_images, + ) + + if isinstance(attachments, dict): + attachments = [attachments] + + for attach in attachments or []: + # cannot attach if no filecontent + if attach.get("fcontent") is None: + continue + emailobj.add_attachment(**attach) + + return emailobj + + +class EMail: + """ + Wrapper on the email module. Email object represents emails to be sent to the client. + Also provides a clean way to add binary `FileData` attachments + Also sets all messages as multipart/alternative for cleaner reading in text-only clients + """ + + def __init__( + self, + sender="", + recipients=(), + subject="", + alternative=0, + reply_to=None, + cc=(), + bcc=(), + email_account=None, + expose_recipients=None, + ): + from email import charset as Charset + + Charset.add_charset("utf-8", Charset.QP, Charset.QP, "utf-8") + + if isinstance(recipients, str): + recipients = recipients.replace(";", ",").replace("\n", "") + recipients = split_emails(recipients) + + # remove null + recipients = filter(None, (strip(r) for r in recipients)) + + self.sender = sender + self.reply_to = reply_to or sender + self.recipients = recipients + self.subject = subject + self.expose_recipients = expose_recipients + + self.msg_root = MIMEMultipart("mixed", policy=policy.SMTP) + self.msg_alternative = MIMEMultipart("alternative", policy=policy.SMTP) + self.msg_root.attach(self.msg_alternative) + self.cc = cc or [] + self.bcc = bcc or [] + self.html_set = False + + self.email_account = email_account or EmailAccount.find_outgoing( + match_by_email=sender, _raise_error=True + ) + + def set_html( + self, + message, + text_content=None, + footer=None, + print_html=None, + formatted=None, + inline_images=None, + header=None, + ): + """Attach message in the html portion of multipart/alternative""" + if not formatted: + formatted = get_formatted_html( + self.subject, + message, + footer, + print_html, + email_account=self.email_account, + header=header, + sender=self.sender, + ) + + # this is the first html part of a multi-part message, + # convert to text well + if not self.html_set: + if text_content: + self.set_text(expand_relative_urls(text_content)) + else: + self.set_html_as_text(expand_relative_urls(formatted)) + + self.set_part_html(formatted, inline_images) + self.html_set = True + + def set_text(self, message): + """ + Attach message in the text portion of multipart/alternative + """ + from email.mime.text import MIMEText + + part = MIMEText(message, "plain", "utf-8", policy=policy.SMTP) + self.msg_alternative.attach(part) + + def set_part_html(self, message, inline_images): + from email.mime.text import MIMEText + + has_inline_images = EMBED_PATTERN.search(message) + + if has_inline_images: + # process inline images + message, _inline_images = replace_filename_with_cid(message) + + # prepare parts + msg_related = MIMEMultipart("related", policy=policy.SMTP) + + html_part = MIMEText(message, "html", "utf-8", policy=policy.SMTP) + msg_related.attach(html_part) + + for image in _inline_images: + self.add_attachment( + image.get("filename"), + image.get("filecontent"), + content_id=image.get("content_id"), + parent=msg_related, + inline=True, + ) + + self.msg_alternative.attach(msg_related) + else: + self.msg_alternative.attach(MIMEText(message, "html", "utf-8", policy=policy.SMTP)) + + def set_html_as_text(self, html): + """Set plain text from HTML""" + self.set_text(to_markdown(html)) + + def set_message(self, message, mime_type="text/html", as_attachment=0, filename="attachment.html"): + """Append the message with MIME content to the root node (as attachment)""" + from email.mime.text import MIMEText + + maintype, subtype = mime_type.split("/") + part = MIMEText(message, _subtype=subtype, policy=policy.SMTP) + + if as_attachment: + part.add_header("Content-Disposition", "attachment", filename=filename) + + self.msg_root.attach(part) + + def attach_file(self, n): + """attach a file from the `FileData` table""" + _file = xhiveframework.get_doc("File", {"file_name": n}) + content = _file.get_content() + if not content: + return + + self.add_attachment(_file.file_name, content) + + def add_attachment(self, fname, fcontent, content_type=None, parent=None, content_id=None, inline=False): + """add attachment""" + + if not parent: + parent = self.msg_root + + add_attachment(fname, fcontent, content_type, parent, content_id, inline) + + def add_pdf_attachment(self, name, html, options=None): + self.add_attachment(name, get_pdf(html, options), "application/octet-stream") + + def validate(self): + """validate the Email Addresses""" + from xhiveframework.utils import validate_email_address + + if not self.sender: + self.sender = self.email_account.default_sender + + validate_email_address(strip(self.sender), True) + self.reply_to = validate_email_address(strip(self.reply_to) or self.sender, True) + + self.set_header("X-Original-From", self.sender) + self.replace_sender() + self.replace_sender_name() + + self.recipients = [strip(r) for r in self.recipients if r not in xhiveframework.STANDARD_USERS] + self.cc = [strip(r) for r in self.cc if r not in xhiveframework.STANDARD_USERS] + self.bcc = [strip(r) for r in self.bcc if r not in xhiveframework.STANDARD_USERS] + + for e in self.recipients + (self.cc or []) + (self.bcc or []): + validate_email_address(e, True) + + def replace_sender(self): + if cint(self.email_account.always_use_account_email_id_as_sender): + sender_name, _ = parse_addr(self.sender) + self.sender = email.utils.formataddr( + (str(Header(sender_name or self.email_account.name, "utf-8")), self.email_account.email_id) + ) + + def replace_sender_name(self): + if cint(self.email_account.always_use_account_name_as_sender_name): + _, sender_email = parse_addr(self.sender) + self.sender = email.utils.formataddr( + (str(Header(self.email_account.name, "utf-8")), sender_email) + ) + + def set_message_id(self, message_id, is_notification=False): + if message_id: + message_id = "<" + message_id + ">" + else: + message_id = get_message_id() + self.set_header("isnotification", "") + + if is_notification: + self.set_header("isnotification", "") + + self.set_header("Message-Id", message_id) + + def set_in_reply_to(self, in_reply_to): + """Used to send the Message-Id of a received email back as In-Reply-To""" + self.set_header("In-Reply-To", in_reply_to) + + def make(self): + """build into msg_root""" + headers = { + "Subject": strip(self.subject), + "From": self.sender, + "To": ", ".join(self.recipients) if self.expose_recipients == "header" else "", + "Date": email.utils.formatdate(), + "Reply-To": self.reply_to if self.reply_to else None, + "CC": ", ".join(self.cc) if self.cc and self.expose_recipients == "header" else None, + "X-XhiveFramework-Site": get_url(), + } + + # reset headers as values may be changed. + for key, val in headers.items(): + if val: + self.set_header(key, val) + + # call hook to enable apps to modify msg_root before sending + for hook in xhiveframework.get_hooks("make_email_body_message"): + xhiveframework.get_attr(hook)(self) + + def set_header(self, key, value): + if key in self.msg_root: + # delete key if found + # this is done because adding the same key doesn't override + # the existing key, rather appends another header with same key. + del self.msg_root[key] + + self.msg_root[key] = sanitize_email_header(value) + + def as_string(self): + """validate, build message and convert to string""" + self.validate() + self.make() + return self.msg_root.as_string(policy=policy.SMTP) + + +def get_formatted_html( + subject, + message, + footer=None, + print_html=None, + email_account=None, + header=None, + unsubscribe_link: xhiveframework._dict | None = None, + sender=None, + with_container=False, +): + email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender) + + rendered_email = xhiveframework.get_template("templates/emails/standard.html").render( + { + "brand_logo": get_brand_logo(email_account) if with_container or header else None, + "with_container": with_container, + "site_url": get_url(), + "header": get_header(header), + "content": message, + "footer": get_footer(email_account, footer), + "title": subject, + "print_html": print_html, + "subject": subject, + } + ) + + html = scrub_urls(rendered_email) + + if unsubscribe_link: + html = html.replace("", unsubscribe_link.html) + + return inline_style_in_html(html) + + +@xhiveframework.whitelist() +def get_email_html(template, args, subject, header=None, with_container=False): + import json + + with_container = cint(with_container) + args = json.loads(args) + if header and header.startswith("["): + header = json.loads(header) + email = xhiveframework.utils.jinja.get_email_from_template(template, args) + return get_formatted_html(subject, email[0], header=header, with_container=with_container) + + +def inline_style_in_html(html): + """Convert email.css and html to inline-styled html""" + from premailer import Premailer + + from xhiveframework.utils.jinja_globals import bundled_asset + + # get email css files from hooks + css_files = xhiveframework.get_hooks("email_css") + css_files = [bundled_asset(path) for path in css_files] + css_files = [path.lstrip("/") for path in css_files] + css_files = [css_file for css_file in css_files if os.path.exists(os.path.abspath(css_file))] + + p = Premailer( + html=html, external_styles=css_files, strip_important=False, allow_loading_external_files=True + ) + + return p.transform() + + +def add_attachment(fname, fcontent, content_type=None, parent=None, content_id=None, inline=False): + """Add attachment to parent which must an email object""" + import mimetypes + from email.mime.audio import MIMEAudio + from email.mime.base import MIMEBase + from email.mime.image import MIMEImage + from email.mime.text import MIMEText + + if not content_type: + content_type, encoding = mimetypes.guess_type(fname) + + if not parent: + return + + if content_type is None: + # No guess could be made, or the file is encoded (compressed), so + # use a generic bag-of-bits type. + content_type = "application/octet-stream" + + maintype, subtype = content_type.split("/", 1) + if maintype == "text": + # Note: we should handle calculating the charset + if isinstance(fcontent, str): + fcontent = fcontent.encode("utf-8") + part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8") + elif maintype == "image": + part = MIMEImage(fcontent, _subtype=subtype) + elif maintype == "audio": + part = MIMEAudio(fcontent, _subtype=subtype) + else: + part = MIMEBase(maintype, subtype) + part.set_payload(fcontent) + # Encode the payload using Base64 + from email import encoders + + encoders.encode_base64(part) + + # Set the filename parameter + if fname: + attachment_type = "inline" if inline else "attachment" + part.add_header("Content-Disposition", attachment_type, filename=str(fname)) + if content_id: + part.add_header("Content-ID", f"<{content_id}>") + + parent.attach(part) + + +def get_message_id(): + """Returns Message ID created from doctype and name""" + return email.utils.make_msgid(domain=xhiveframework.local.site) + + +def get_signature(email_account): + if email_account and email_account.add_signature and email_account.signature: + return "
      " + email_account.signature + else: + return "" + + +def get_footer(email_account, footer=None): + """append a footer (signature)""" + footer = footer or "" + + args = {} + + if email_account and email_account.footer: + args.update({"email_account_footer": email_account.footer}) + + sender_address = xhiveframework.db.get_default("email_footer_address") + + if sender_address: + args.update({"sender_address": sender_address}) + + if not cint(xhiveframework.db.get_default("disable_standard_email_footer")): + args.update({"default_mail_footer": xhiveframework.get_hooks("default_mail_footer")}) + + footer += xhiveframework.utils.jinja.get_email_from_template("email_footer", args)[0] + + return footer + + +def replace_filename_with_cid(message): + """Replaces with + and return the modified message and + a list of inline_images with {filename, filecontent, content_id} + """ + + inline_images = [] + + while True: + matches = EMBED_PATTERN.search(message) + if not matches: + break + groups = matches.groups() + + # found match + img_path = groups[0] + img_path_escaped = xhiveframework.utils.html_utils.unescape_html(img_path) + filename = img_path_escaped.rsplit("/")[-1] + + filecontent = get_filecontent_from_path(img_path_escaped) + if not filecontent: + message = re.sub(f"""embed=['"]{re.escape(img_path)}['"]""", "", message) + continue + + content_id = random_string(10) + + inline_images.append({"filename": filename, "filecontent": filecontent, "content_id": content_id}) + + message = re.sub(f"""embed=['"]{re.escape(img_path)}['"]""", f'src="cid:{content_id}"', message) + + return (message, inline_images) + + +def get_filecontent_from_path(path): + if not path: + return + + if path.startswith("/"): + path = path[1:] + + if path.startswith("assets/"): + # from public folder + full_path = os.path.abspath(path) + elif path.startswith("files/"): + # public file + full_path = xhiveframework.get_site_path("public", path) + elif path.startswith("private/files/"): + # private file + full_path = xhiveframework.get_site_path(path) + else: + full_path = path + + if os.path.exists(full_path): + with open(full_path, "rb") as f: + filecontent = f.read() + + return filecontent + else: + return None + + +def get_header(header=None): + """Build header from template""" + from xhiveframework.utils.jinja import get_email_from_template + + if not header: + return None + + if isinstance(header, str): + # header = 'My Title' + header = [header, None] + if len(header) == 1: + # header = ['My Title'] + header.append(None) + # header = ['My Title', 'orange'] + title, indicator = header + + if not title: + title = xhiveframework.get_hooks("app_title")[-1] + + email_header, text = get_email_from_template( + "email_header", {"header_title": title, "indicator": indicator} + ) + + return email_header + + +def sanitize_email_header(header: str): + """ + Removes all line boundaries in the headers. + + Email Policy (python's std) has some bugs in it which uses splitlines + and raises ValueError (ref: https://github.com/python/cpython/blob/main/Lib/email/policy.py#L143). + Hence removing all line boundaries while sanitization of headers to prevent such faliures. + The line boundaries which are removed can be found here: https://docs.python.org/3/library/stdtypes.html#str.splitlines + """ + + return "".join(header.splitlines()) + + +def get_brand_logo(email_account): + return email_account.get("brand_logo") diff --git a/xhiveframework/email/inbox.py b/xhiveframework/email/inbox.py new file mode 100644 index 0000000..e9b501b --- /dev/null +++ b/xhiveframework/email/inbox.py @@ -0,0 +1,132 @@ +import json + +import xhiveframework +from xhiveframework.client import set_value + + +def get_email_accounts(user=None): + if not user: + user = xhiveframework.session.user + + email_accounts = [] + + accounts = xhiveframework.get_all( + "User Email", + filters={"parent": user}, + fields=["email_account", "email_id", "enable_outgoing"], + distinct=True, + order_by="idx", + ) + + if not accounts: + return {"email_accounts": [], "all_accounts": ""} + + all_accounts = ",".join(account.get("email_account") for account in accounts) + if len(accounts) > 1: + email_accounts.append({"email_account": all_accounts, "email_id": "All Accounts"}) + email_accounts.extend(accounts) + + email_accounts.extend( + [ + {"email_account": "Sent", "email_id": "Sent Mail"}, + {"email_account": "Spam", "email_id": "Spam"}, + {"email_account": "Trash", "email_id": "Trash"}, + ] + ) + + return {"email_accounts": email_accounts, "all_accounts": all_accounts} + + +@xhiveframework.whitelist() +def create_email_flag_queue(names, action): + """create email flag queue to mark email either as read or unread""" + + def mark_as_seen_unseen(name, action): + doc = xhiveframework.get_doc("Communication", name) + if action == "Read": + doc.add_seen() + else: + _seen = json.loads(doc._seen or "[]") + _seen = [user for user in _seen if xhiveframework.session.user != user] + doc.db_set("_seen", json.dumps(_seen), update_modified=False) + + if not all([names, action]): + return + + for name in json.loads(names or []): + uid, seen_status, email_account = xhiveframework.db.get_value( + "Communication", name, ["ifnull(uid, -1)", "ifnull(seen, 0)", "email_account"] + ) + + # can not mark email SEEN or UNSEEN without uid + if not uid or uid == -1: + continue + + seen = 1 if action == "Read" else 0 + # check if states are correct + if (action == "Read" and seen_status == 0) or (action == "Unread" and seen_status == 1): + create_new = True + email_flag_queue = xhiveframework.db.sql( + """select name, action from `tabEmail Flag Queue` + where communication = %(name)s and is_completed=0""", + {"name": name}, + as_dict=True, + ) + + for queue in email_flag_queue: + if queue.action != action: + xhiveframework.delete_doc("Email Flag Queue", queue.name, ignore_permissions=True) + elif queue.action == action: + # Read or Unread request for email is already available + create_new = False + + if create_new: + flag_queue = xhiveframework.get_doc( + { + "uid": uid, + "action": action, + "communication": name, + "doctype": "Email Flag Queue", + "email_account": email_account, + } + ) + flag_queue.save(ignore_permissions=True) + xhiveframework.db.set_value("Communication", name, "seen", seen, update_modified=False) + mark_as_seen_unseen(name, action) + + +@xhiveframework.whitelist() +def mark_as_closed_open(communication: str, status: str): + """Set status to open or close""" + set_value("Communication", communication, "status", status) + + +@xhiveframework.whitelist() +def move_email(communication: str, email_account: str): + """Move email to another email account.""" + set_value("Communication", communication, "email_account", email_account) + + +@xhiveframework.whitelist() +def mark_as_trash(communication: str): + """Set email status to trash.""" + set_value("Communication", communication, "email_status", "Trash") + + +@xhiveframework.whitelist() +def mark_as_spam(communication: str, sender: str): + """Set email status to spam.""" + email_rule = xhiveframework.db.get_value("Email Rule", {"email_id": sender}) + if not email_rule: + xhiveframework.get_doc({"doctype": "Email Rule", "email_id": sender, "is_spam": 1}).insert( + ignore_permissions=True + ) + set_value("Communication", communication, "email_status", "Spam") + + +def link_communication_to_document(doc, reference_doctype, reference_name, ignore_communication_links): + if not ignore_communication_links: + doc.reference_doctype = reference_doctype + doc.reference_name = reference_name + doc.status = "Linked" + doc.save(ignore_permissions=True) diff --git a/xhiveframework/email/oauth.py b/xhiveframework/email/oauth.py new file mode 100644 index 0000000..c6b45f5 --- /dev/null +++ b/xhiveframework/email/oauth.py @@ -0,0 +1,74 @@ +import base64 +from imaplib import IMAP4 +from poplib import POP3 +from smtplib import SMTP + +import xhiveframework + + +class Oauth: + def __init__( + self, + conn: IMAP4 | POP3 | SMTP, + email_account: str, + email: str, + access_token: str, + mechanism: str = "XOAUTH2", + ) -> None: + self.email_account = email_account + self.email = email + self._mechanism = mechanism + self._conn = conn + self._access_token = access_token + + self._validate() + + def _validate(self) -> None: + if not self._access_token: + xhiveframework.throw( + xhiveframework._("Please Authorize OAuth for Email Account {}").format(self.email_account), + title=xhiveframework._("OAuth Error"), + ) + + @property + def _auth_string(self) -> str: + return f"user={self.email}\1auth=Bearer {self._access_token}\1\1" + + def connect(self) -> None: + try: + if isinstance(self._conn, POP3): + self._connect_pop() + + elif isinstance(self._conn, IMAP4): + self._connect_imap() + + else: + # SMTP + self._connect_smtp() + + except Exception: + xhiveframework.log_error( + "Email Connection Error - Authentication Failed", + reference_doctype="Email Account", + reference_name=self.email_account, + ) + # raising a bare exception here as we have a lot of exception handling present + # where the connect method is called from - hence just logging and raising. + raise + + def _connect_pop(self) -> None: + # NOTE: poplib doesn't have AUTH command implementation + res = self._conn._shortcmd( + "AUTH {} {}".format( + self._mechanism, base64.b64encode(bytes(self._auth_string, "utf-8")).decode("utf-8") + ) + ) + + if not res.startswith(b"+OK"): + raise + + def _connect_imap(self) -> None: + self._conn.authenticate(self._mechanism, lambda x: self._auth_string) + + def _connect_smtp(self) -> None: + self._conn.auth(self._mechanism, lambda x: self._auth_string, initial_response_ok=False) diff --git a/xhiveframework/email/page/__init__.py b/xhiveframework/email/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/email/queue.py b/xhiveframework/email/queue.py new file mode 100755 index 0000000..5c89648 --- /dev/null +++ b/xhiveframework/email/queue.py @@ -0,0 +1,179 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _, msgprint +from xhiveframework.utils import cint, cstr, get_url, now_datetime +from xhiveframework.utils.data import getdate +from xhiveframework.utils.verified_command import get_signed_params, verify_request + +# After this percent of failures in every batch, entire batch is aborted. +# This usually indicates a systemic failure so we shouldn't keep trying to send emails. +EMAIL_QUEUE_BATCH_FAILURE_THRESHOLD_PERCENT = 0.33 +EMAIL_QUEUE_BATCH_FAILURE_THRESHOLD_COUNT = 10 + + +def get_emails_sent_this_month(email_account=None): + """Get count of emails sent from a specific email account. + + :param email_account: name of the email account used to send mail + + if email_account=None, email account filter is not applied while counting + """ + today = getdate() + month_start = today.replace(day=1) + + filters = { + "status": "Sent", + "creation": [">=", str(month_start)], + } + if email_account: + filters["email_account"] = email_account + + return xhiveframework.db.count("Email Queue", filters=filters) + + +def get_emails_sent_today(email_account=None): + """Get count of emails sent from a specific email account. + + :param email_account: name of the email account used to send mail + + if email_account=None, email account filter is not applied while counting + """ + q = """ + SELECT + COUNT(`name`) + FROM + `tabEmail Queue` + WHERE + `status` in ('Sent', 'Not Sent', 'Sending') + AND + `creation` > (NOW() - INTERVAL '24' HOUR) + """ + + q_args = {} + if email_account is not None: + if email_account: + q += " AND email_account = %(email_account)s" + q_args["email_account"] = email_account + else: + q += " AND (email_account is null OR email_account='')" + + return xhiveframework.db.sql(q, q_args)[0][0] + + +def get_unsubscribe_message(unsubscribe_message: str, expose_recipients: str) -> "xhiveframework._dict[str, str]": + unsubscribe_message = unsubscribe_message or _("Unsubscribe") + unsubscribe_link = f'{unsubscribe_message}' + unsubscribe_html = _("{0} to stop receiving emails of this type").format(unsubscribe_link) + html = f"""""" + + text = f"\n\n{unsubscribe_message}: \n" + if expose_recipients == "footer": + text = f"\n{text}" + + return xhiveframework._dict(html=html, text=text) + + +def get_unsubcribed_url(reference_doctype, reference_name, email, unsubscribe_method, unsubscribe_params): + params = { + "email": cstr(email), + "doctype": cstr(reference_doctype), + "name": cstr(reference_name), + } + if unsubscribe_params: + params.update(unsubscribe_params) + + return get_url(unsubscribe_method + "?" + get_signed_params(params)) + + +@xhiveframework.whitelist(allow_guest=True) +def unsubscribe(doctype, name, email): + # unsubsribe from comments and communications + if not xhiveframework.flags.in_test and not verify_request(): + return + + try: + xhiveframework.get_doc( + { + "doctype": "Email Unsubscribe", + "email": email, + "reference_doctype": doctype, + "reference_name": name, + } + ).insert(ignore_permissions=True) + + except xhiveframework.DuplicateEntryError: + xhiveframework.db.rollback() + + else: + xhiveframework.db.commit() + + return_unsubscribed_page(email, doctype, name) + + +def return_unsubscribed_page(email, doctype, name): + xhiveframework.respond_as_web_page( + _("Unsubscribed"), + _("{0} has left the conversation in {1} {2}").format(email, _(doctype), name), + indicator_color="green", + ) + + +def flush(): + """flush email queue, every time: called from scheduler. + + This should not be called outside of background jobs. + """ + from xhiveframework.email.doctype.email_queue.email_queue import EmailQueue + + # To avoid running jobs inside unit tests + if xhiveframework.are_emails_muted(): + msgprint(_("Emails are muted")) + + if cint(xhiveframework.db.get_default("suspend_email_queue")) == 1: + return + + email_queue_batch = get_queue() + if not email_queue_batch: + return + + failed_email_queues = [] + for row in email_queue_batch: + try: + email_queue: EmailQueue = xhiveframework.get_doc("Email Queue", row.name) + email_queue.send() + except Exception: + xhiveframework.get_doc("Email Queue", row.name).log_error() + failed_email_queues.append(row.name) + + if ( + len(failed_email_queues) / len(email_queue_batch) + > EMAIL_QUEUE_BATCH_FAILURE_THRESHOLD_PERCENT + and len(failed_email_queues) > EMAIL_QUEUE_BATCH_FAILURE_THRESHOLD_COUNT + ): + xhiveframework.throw(_("Email Queue flushing aborted due to too many failures.")) + + +def get_queue(): + batch_size = cint(xhiveframework.conf.email_queue_batch_size) or 500 + + return xhiveframework.db.sql( + f"""select + name, sender + from + `tabEmail Queue` + where + (status='Not Sent' or status='Partially Sent') and + (send_after is null or send_after < %(now)s) + order + by priority desc, retry asc, creation asc + limit {batch_size}""", + {"now": now_datetime()}, + as_dict=True, + ) diff --git a/xhiveframework/email/receive.py b/xhiveframework/email/receive.py new file mode 100644 index 0000000..13ee3cb --- /dev/null +++ b/xhiveframework/email/receive.py @@ -0,0 +1,898 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import datetime +import email +import email.utils +import imaplib +import json +import poplib +import re +import ssl +import time +from contextlib import suppress +from email.header import decode_header + +import _socket +import chardet +from email_reply_parser import EmailReplyParser + +import xhiveframework +from xhiveframework import _, safe_decode, safe_encode +from xhiveframework.core.doctype.file.exceptions import MaxFileSizeReachedError +from xhiveframework.core.doctype.file.utils import get_random_filename +from xhiveframework.email.oauth import Oauth +from xhiveframework.utils import ( + add_days, + cint, + convert_utc_to_system_timezone, + cstr, + extract_email_id, + get_datetime, + get_string_between, + markdown, + now, + parse_addr, + sanitize_html, + strip, +) +from xhiveframework.utils.html_utils import clean_email_html +from xhiveframework.utils.user import is_system_user + +# fix due to a python bug in poplib that limits it to 2048 +poplib._MAXLINE = 1_00_000 + +THREAD_ID_PATTERN = re.compile(r"(?<=\[)[\w/-]+") +WORDS_PATTERN = re.compile(r"\w+") + + +class EmailSizeExceededError(xhiveframework.ValidationError): + pass + + +class LoginLimitExceeded(xhiveframework.ValidationError): + pass + + +class SentEmailInInboxError(Exception): + pass + + +class EmailServer: + """Wrapper for POP server to pull emails.""" + + def __init__(self, args=None): + self.settings = args or xhiveframework._dict() + + def connect(self): + """Connect to **Email Account**.""" + return self.connect_imap() if cint(self.settings.use_imap) else self.connect_pop() + + def connect_imap(self): + """Connect to IMAP""" + try: + if cint(self.settings.use_ssl): + self.imap = imaplib.IMAP4_SSL( + self.settings.host, + self.settings.incoming_port, + timeout=xhiveframework.conf.pop_timeout, + ssl_context=ssl.create_default_context(), + ) + else: + self.imap = imaplib.IMAP4( + self.settings.host, self.settings.incoming_port, timeout=xhiveframework.conf.pop_timeout + ) + + if cint(self.settings.use_starttls): + self.imap.starttls() + + if self.settings.use_oauth: + Oauth( + self.imap, + self.settings.email_account, + self.settings.username, + self.settings.access_token, + ).connect() + + else: + self.imap.login(self.settings.username, self.settings.password) + + # connection established! + return True + + except _socket.error: + # Invalid mail server -- due to refusing connection + xhiveframework.msgprint(_("Invalid Mail Server. Please rectify and try again.")) + raise + + def connect_pop(self): + # this method return pop connection + try: + if cint(self.settings.use_ssl): + self.pop = poplib.POP3_SSL( + self.settings.host, + self.settings.incoming_port, + timeout=xhiveframework.conf.pop_timeout, + context=ssl.create_default_context(), + ) + else: + self.pop = poplib.POP3( + self.settings.host, self.settings.incoming_port, timeout=xhiveframework.conf.pop_timeout + ) + + if self.settings.use_oauth: + Oauth( + self.pop, + self.settings.email_account, + self.settings.username, + self.settings.access_token, + ).connect() + + else: + self.pop.user(self.settings.username) + self.pop.pass_(self.settings.password) + + # connection established! + return True + + except _socket.error: + xhiveframework.log_error("POP: Unable to connect") + + # Invalid mail server -- due to refusing connection + xhiveframework.msgprint(_("Invalid Mail Server. Please rectify and try again.")) + raise + + except poplib.error_proto as e: + if self.is_temporary_system_problem(e): + return False + + else: + xhiveframework.msgprint(_("Invalid User Name or Support Password. Please rectify and try again.")) + raise + + def select_imap_folder(self, folder): + res = self.imap.select(f'"{folder}"') + return res[0] == "OK" # The folder exsits TODO: handle other resoponses too + + def logout(self): + if cint(self.settings.use_imap): + self.imap.logout() + else: + self.pop.quit() + return + + def get_messages(self, folder="INBOX"): + """Returns new email messages.""" + + self.latest_messages = [] + self.seen_status = {} + self.uid_reindexed = False + + email_list = self.get_new_mails(folder) + + for i, uid in enumerate(email_list[:100]): + try: + self.retrieve_message(uid, i + 1) + except (_socket.timeout, LoginLimitExceeded): + # get whatever messages were retrieved + break + + out = {"latest_messages": self.latest_messages} + if self.settings.use_imap: + out.update( + {"uid_list": email_list, "seen_status": self.seen_status, "uid_reindexed": self.uid_reindexed} + ) + + return out + + def get_new_mails(self, folder): + """Return list of new mails""" + email_list = [] + if cint(self.settings.use_imap): + self.check_imap_uidvalidity(folder) + + readonly = False if self.settings.email_sync_rule == "UNSEEN" else True + + self.imap.select(folder, readonly=readonly) + response, message = self.imap.uid("search", None, self.settings.email_sync_rule) + if message[0]: + email_list = message[0].split() + else: + email_list = self.pop.list()[1] + + return email_list + + def check_imap_uidvalidity(self, folder): + # compare the UIDVALIDITY of email account and imap server + uid_validity = self.settings.uid_validity + + response, message = self.imap.status(folder, "(UIDVALIDITY UIDNEXT)") + current_uid_validity = self.parse_imap_response("UIDVALIDITY", message[0]) or 0 + + uidnext = int(self.parse_imap_response("UIDNEXT", message[0]) or "1") + xhiveframework.db.set_value("Email Account", self.settings.email_account, "uidnext", uidnext) + + if not uid_validity or uid_validity != current_uid_validity: + # uidvalidity changed & all email uids are reindexed by server + Communication = xhiveframework.qb.DocType("Communication") + xhiveframework.qb.update(Communication).set(Communication.uid, -1).where( + Communication.communication_medium == "Email" + ).where(Communication.email_account == self.settings.email_account).run() + + if self.settings.use_imap: + # Remove {"} quotes that are added to handle spaces in IMAP Folder names + if folder[0] == folder[-1] == '"': + folder = folder[1:-1] + # new update for the IMAP Folder DocType + IMAPFolder = xhiveframework.qb.DocType("IMAP Folder") + xhiveframework.qb.update(IMAPFolder).set(IMAPFolder.uidvalidity, current_uid_validity).set( + IMAPFolder.uidnext, uidnext + ).where(IMAPFolder.parent == self.settings.email_account_name).where( + IMAPFolder.folder_name == folder + ).run() + else: + EmailAccount = xhiveframework.qb.DocType("Email Account") + xhiveframework.qb.update(EmailAccount).set(EmailAccount.uidvalidity, current_uid_validity).set( + EmailAccount.uidnext, uidnext + ).where(EmailAccount.name == self.settings.email_account_name).run() + + sync_count = 100 if uid_validity else int(self.settings.initial_sync_count) + from_uid = 1 if uidnext < (sync_count + 1) or (uidnext - sync_count) < 1 else uidnext - sync_count + # sync last 100 email + self.settings.email_sync_rule = f"UID {from_uid}:{uidnext}" + self.uid_reindexed = True + + def parse_imap_response(self, cmd, response): + pattern = rf"(?<={cmd} )[0-9]*" + match = re.search(pattern, response.decode("utf-8"), re.U | re.I) + + if match: + return match.group(0) + else: + return None + + def retrieve_message(self, uid, msg_num): + try: + if cint(self.settings.use_imap): + status, message = self.imap.uid("fetch", uid, "(BODY.PEEK[] BODY.PEEK[HEADER] FLAGS)") + raw = message[0] + + self.get_email_seen_status(uid, raw[0]) + self.latest_messages.append(raw[1]) + else: + msg = self.pop.retr(msg_num) + self.latest_messages.append(b"\n".join(msg[1])) + except _socket.timeout: + # propagate this error to break the loop + raise + + except Exception as e: + if self.has_login_limit_exceeded(e): + raise LoginLimitExceeded(e) + + xhiveframework.log_error("Unable to fetch email", self.make_error_msg(uid, msg_num)) + + self._post_retrieve_cleanup(uid, msg_num) + + def get_email_seen_status(self, uid, flag_string): + """parse the email FLAGS response""" + if not flag_string: + return None + + flags = [] + for flag in imaplib.ParseFlags(flag_string) or []: + match = WORDS_PATTERN.search(xhiveframework.as_unicode(flag)) + flags.append(match.group(0)) + + if "Seen" in flags: + self.seen_status.update({uid: "SEEN"}) + else: + self.seen_status.update({uid: "UNSEEN"}) + + def has_login_limit_exceeded(self, e): + return "-ERR Exceeded the login limit" in strip(cstr(e)) + + def _post_retrieve_cleanup(self, uid, msg_num): + with suppress(Exception): + if not cint(self.settings.use_imap): + self.pop.dele(msg_num) + else: + # mark as seen if email sync rule is UNSEEN (syncing only unseen mails) + if self.settings.email_sync_rule == "UNSEEN": + self.imap.uid("STORE", uid, "+FLAGS", "(\\SEEN)") + + def is_temporary_system_problem(self, e): + messages = ( + "-ERR [SYS/TEMP] Temporary system problem. Please try again later.", + "Connection timed out", + ) + for message in messages: + if message in strip(cstr(e)) or message in strip(cstr(getattr(e, "strerror", ""))): + return True + return False + + def make_error_msg(self, uid, msg_num): + partial_mail = None + traceback = xhiveframework.get_traceback(with_context=True) + with suppress(Exception): + # retrieve headers + if not cint(self.settings.use_imap): + headers = b"\n".join(self.pop.top(msg_num, 5)[1]) + else: + headers = self.imap.uid("fetch", uid, "(BODY.PEEK[HEADER])")[1][0][1] + + partial_mail = Email(headers) + + if partial_mail: + return ( + "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n\n\nTraceback: \n{traceback}".format( + date=partial_mail.date, + from_email=partial_mail.from_email, + subject=partial_mail.subject, + traceback=traceback, + ) + ) + return traceback + + def update_flag(self, folder, uid_list=None): + """set all uids mails the flag as seen""" + if not uid_list: + return + + if not self.connect(): + return + + self.imap.select(folder) + for uid, operation in uid_list.items(): + if not uid: + continue + + op = "+FLAGS" if operation == "Read" else "-FLAGS" + try: + self.imap.uid("STORE", uid, op, "(\\SEEN)") + except Exception: + continue + + +class Email: + """Wrapper for an email.""" + + def __init__(self, content): + """Parses headers, content, attachments from given raw message. + + :param content: Raw message.""" + if isinstance(content, bytes): + self.mail = email.message_from_bytes(content) + else: + self.mail = email.message_from_string(content) + + self.raw_message = content + self.text_content = "" + self.html_content = "" + self.attachments = [] + self.cid_map = {} + self.parse() + self.set_content_and_type() + self.set_subject() + self.set_from() + + message_id = self.mail.get("Message-ID") or "" + self.message_id = get_string_between("<", message_id, ">") + + if self.mail["Date"]: + try: + utc = email.utils.mktime_tz(email.utils.parsedate_tz(self.mail["Date"])) + utc_dt = datetime.datetime.utcfromtimestamp(utc) + self.date = convert_utc_to_system_timezone(utc_dt).strftime("%Y-%m-%d %H:%M:%S") + except Exception: + self.date = now() + else: + self.date = now() + if self.date > now(): + self.date = now() + + @property + def in_reply_to(self): + in_reply_to = self.mail.get("In-Reply-To") or "" + return get_string_between("<", in_reply_to, ">") + + def parse(self): + """Walk and process multi-part email.""" + for part in self.mail.walk(): + self.process_part(part) + + def set_subject(self): + """Parse and decode `Subject` header.""" + _subject = decode_header(self.mail.get("Subject", "No Subject")) + self.subject = _subject[0][0] or "" + + if _subject[0][1]: + # Encoding is known by decode_header (might also be unknown-8bit) + self.subject = safe_decode(self.subject, _subject[0][1]) + + if isinstance(self.subject, bytes): + # Fall back to utf-8 if the charset is unknown or decoding fails + # Replace invalid characters with '' + self.subject = self.subject.decode("utf-8", "replace") + + # Convert non-string (e.g. None) + # Truncate to 140 chars (can be used as a document name) + self.subject = str(self.subject).strip()[:140] + + if not self.subject: + self.subject = "No Subject" + + def set_from(self): + # gmail mailing-list compatibility + # use X-Original-Sender if available, as gmail sometimes modifies the 'From' + _from_email = self.decode_email(self.mail.get("X-Original-From") or self.mail["From"]) + _reply_to = self.decode_email(self.mail.get("Reply-To")) + + if _reply_to and not xhiveframework.db.get_value("Email Account", {"email_id": _reply_to}, "email_id"): + self.from_email = extract_email_id(_reply_to) + else: + self.from_email = extract_email_id(_from_email) + + if self.from_email: + self.from_email = self.from_email.lower() + + self.from_real_name = parse_addr(_from_email)[0] if "@" in _from_email else _from_email + + @staticmethod + def decode_email(email): + if not email: + return + decoded = "" + for part, encoding in decode_header(xhiveframework.as_unicode(email).replace('"', " ").replace("'", " ")): + if encoding: + decoded += part.decode(encoding, "replace") + else: + decoded += safe_decode(part) + return decoded + + def set_content_and_type(self): + self.content, self.content_type = "[Blank Email]", "text/plain" + if self.html_content: + self.content, self.content_type = self.html_content, "text/html" + else: + self.content, self.content_type = ( + EmailReplyParser.read(self.text_content).text.replace("\n", "\n\n"), + "text/plain", + ) + + def process_part(self, part): + """Parse email `part` and set it to `text_content`, `html_content` or `attachments`.""" + content_type = part.get_content_type() + if content_type == "text/plain": + self.text_content += self.get_payload(part) + + # attach txt file from received email as well aside from saving to text_content if it has filename + if part.get_filename(): + self.get_attachment(part) + + elif content_type == "text/html": + self.html_content += self.get_payload(part) + + elif content_type == "message/rfc822": + # sent by outlook when another email is sent as an attachment to this email + self.show_attached_email_headers_in_content(part) + + elif part.get_filename() or "image" in content_type: + self.get_attachment(part) + + def show_attached_email_headers_in_content(self, part): + # get the multipart/alternative message + from html import escape + + message = list(part.walk())[1] + headers = [] + for key in ("From", "To", "Subject", "Date"): + value = cstr(message.get(key)) + if value: + headers.append(f"{_(key)}: {escape(value)}") + + self.text_content += "\n".join(headers) + self.html_content += "
      " + "\n".join(f"

      {h}

      " for h in headers) + + if not message.is_multipart() and message.get_content_type() == "text/plain": + # email.parser didn't parse it! + text_content = self.get_payload(message) + self.text_content += text_content + self.html_content += markdown(text_content) + + def get_charset(self, part): + """Detect charset.""" + charset = part.get_content_charset() + if not charset: + charset = chardet.detect(safe_encode(cstr(part)))["encoding"] + + return charset + + def get_payload(self, part): + charset = self.get_charset(part) + + try: + return str(part.get_payload(decode=True), str(charset), "ignore") + except LookupError: + return part.get_payload() + + def get_attachment(self, part): + # charset = self.get_charset(part) + fcontent = part.get_payload(decode=True) + + if fcontent: + content_type = part.get_content_type() + fname = part.get_filename() + if fname: + try: + fname = fname.replace("\n", " ").replace("\r", "") + fname = cstr(decode_header(fname)[0][0]) + except Exception: + fname = get_random_filename(content_type=content_type) + else: + fname = get_random_filename(content_type=content_type) + # Don't clobber existing filename + while fname in self.cid_map: + fname = get_random_filename(content_type=content_type) + + self.attachments.append( + { + "content_type": content_type, + "fname": fname, + "fcontent": fcontent, + } + ) + + cid = (cstr(part.get("Content-Id")) or "").strip("><") + if cid: + self.cid_map[fname] = cid + + def save_attachments_in_doc(self, doc): + """Save email attachments in given document.""" + saved_attachments = [] + + for attachment in self.attachments: + try: + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_name": attachment["fname"], + "attached_to_doctype": doc.doctype, + "attached_to_name": doc.name, + "is_private": 1, + "content": attachment["fcontent"], + } + ) + _file.save() + saved_attachments.append(_file) + + if attachment["fname"] in self.cid_map: + self.cid_map[_file.name] = self.cid_map[attachment["fname"]] + + except MaxFileSizeReachedError: + # WARNING: bypass max file size exception + pass + except xhiveframework.FileAlreadyAttachedException: + pass + except xhiveframework.DuplicateEntryError: + # same file attached twice?? + pass + + return saved_attachments + + def get_thread_id(self): + """Extract thread ID from `[]`""" + l = THREAD_ID_PATTERN.findall(self.subject) + return l and l[0] or None + + def is_reply(self): + return bool(self.in_reply_to) + + +class InboundMail(Email): + """Class representation of incoming mail along with mail handlers.""" + + def __init__(self, content, email_account, uid=None, seen_status=None, append_to=None): + super().__init__(content) + self.email_account = email_account + self.uid = uid or -1 + self.append_to = append_to + self.seen_status = seen_status or 0 + + # System documents related to this mail + self._parent_email_queue = None + self._parent_communication = None + self._reference_document = None + + self.flags = xhiveframework._dict() + + def get_content(self): + if self.content_type == "text/html": + return clean_email_html(self.content) + + def process(self): + """Create communication record from email.""" + if self.is_sender_same_as_receiver() and not self.is_reply(): + if xhiveframework.flags.in_test: + print("WARN: Cannot pull email. Sender same as recipient inbox") + raise SentEmailInInboxError + + communication = self.is_exist_in_system() + if communication: + communication.update_db(uid=self.uid) + communication.reload() + return communication + + self.flags.is_new_communication = True + return self._build_communication_doc() + + def _build_communication_doc(self): + data = self.as_dict() + data["doctype"] = "Communication" + + if self.parent_communication(): + data["in_reply_to"] = self.parent_communication().name + + append_to = self.append_to if self.email_account.use_imap else self.email_account.append_to + + if self.reference_document(): + data["reference_doctype"] = self.reference_document().doctype + data["reference_name"] = self.reference_document().name + elif append_to and append_to != "Communication": + reference_name = self._create_reference_document(append_to) + if reference_name: + data["reference_doctype"] = append_to + data["reference_name"] = reference_name + + if self.is_notification(): + # Disable notifications for notification. + data["unread_notification_sent"] = 1 + + if self.seen_status: + data["_seen"] = json.dumps(self.get_users_linked_to_account(self.email_account)) + + communication = xhiveframework.get_doc(data) + communication.flags.in_receive = True + communication.insert(ignore_permissions=True) + + # Communication might have been modified by some hooks, reload before saving + communication.reload() + + # save attachments + communication._attachments = self.save_attachments_in_doc(communication) + communication.content = sanitize_html(self.replace_inline_images(communication._attachments)) + communication.save() + return communication + + def replace_inline_images(self, attachments): + # replace inline images + content = self.content + for file in attachments: + if self.cid_map.get(file.name): + content = content.replace(f"cid:{self.cid_map[file.name]}", file.unique_url) + return content + + def is_notification(self): + isnotification = self.mail.get("isnotification") + return isnotification and ("notification" in isnotification) + + def is_exist_in_system(self): + """Check if this email already exists in the system(as communication document).""" + from xhiveframework.core.doctype.communication.communication import Communication + + if not self.message_id: + return + + return Communication.find_one_by_filters(message_id=self.message_id, order_by="creation DESC") + + def is_sender_same_as_receiver(self): + return self.from_email == self.email_account.email_id + + def is_reply_to_system_sent_mail(self): + """Is it a reply to already sent mail.""" + return self.is_reply() and xhiveframework.local.site in self.in_reply_to + + def parent_email_queue(self): + """Get parent record from `Email Queue`. + + If it is a reply to already sent mail, then there will be a parent record in EMail Queue. + """ + from xhiveframework.email.doctype.email_queue.email_queue import EmailQueue + + if self._parent_email_queue is not None: + return self._parent_email_queue + + parent_email_queue = "" + if self.is_reply_to_system_sent_mail(): + parent_email_queue = EmailQueue.find_one_by_filters(message_id=self.in_reply_to) + + self._parent_email_queue = parent_email_queue or "" + return self._parent_email_queue + + def parent_communication(self): + """Find a related communication so that we can prepare a mail thread. + + The way it happens is by using in-reply-to header, and we can't make thread if it does not exist. + + Here are the cases to handle: + 1. If mail is a reply to already sent mail, then we can get parent communicaion from + Email Queue record or message_id on communication. + 2. Sometimes we send communication name in message-ID directly, use that to get parent communication. + 3. Sender sent a reply but reply is on top of what (s)he sent before, + then parent record exists directly in communication. + """ + from xhiveframework.core.doctype.communication.communication import Communication + + if self._parent_communication is not None: + return self._parent_communication + + if not self.is_reply(): + return "" + + communication = Communication.find_one_by_filters(message_id=self.in_reply_to) + if not communication: + if self.parent_email_queue() and self.parent_email_queue().communication: + communication = Communication.find(self.parent_email_queue().communication, ignore_error=True) + else: + reference = self.in_reply_to + if "@" in self.in_reply_to: + reference, _ = self.in_reply_to.split("@", 1) + communication = Communication.find(reference, ignore_error=True) + + self._parent_communication = communication or "" + return self._parent_communication + + def reference_document(self): + """Reference document is a document to which mail relate to. + + We can get reference document from Parent record(EmailQueue | Communication) if exists. + Otherwise we do subject match to find reference document if we know the reference(append_to) doctype. + """ + if self._reference_document is not None: + return self._reference_document + + reference_document = "" + parent = self.parent_email_queue() or self.parent_communication() + + if parent and parent.reference_doctype: + reference_doctype, reference_name = parent.reference_doctype, parent.reference_name + reference_document = self.get_doc(reference_doctype, reference_name, ignore_error=True) + + if not reference_document and self.email_account.append_to: + reference_document = self.match_record_by_subject_and_sender(self.email_account.append_to) + + self._reference_document = reference_document or "" + return self._reference_document + + def get_reference_name_from_subject(self): + """ + Ex: "Re: Your email (#OPP-2020-2334343)" + """ + return self.subject.rsplit("#", 1)[-1].strip(" ()") + + def match_record_by_subject_and_sender(self, doctype): + """Find a record in the given doctype that matches with email subject and sender. + + Cases: + 1. Sometimes record name is part of subject. We can get document by parsing name from subject + 2. Find by matching sender and subject + 3. Find by matching subject alone (Special case) + Ex: when a System User is using Outlook and replies to an email from their own client, + it reaches the Email Account with the threading info lost and the (sender + subject match) + doesn't work because the sender in the first communication was someone different to whom + the system user is replying to via the common email account in XhiveFramework. This fix bypasses + the sender match when the sender is a system user and subject is atleast 10 chars long + (for additional safety) + + NOTE: We consider not to match by subject if match record is very old. + """ + name = self.get_reference_name_from_subject() + email_fields = self.get_email_fields(doctype) + + record = self.get_doc(doctype, name, ignore_error=True) if name else None + + if not record: + subject = self.clean_subject(self.subject) + filters = { + email_fields.subject_field: ("like", f"%{subject}%"), + "creation": (">", self.get_relative_dt(days=-60)), + } + + # Sender check is not needed incase mail is from system user. + if not (len(subject) > 10 and is_system_user(self.from_email)): + filters[email_fields.sender_field] = self.from_email + + name = xhiveframework.db.get_value(self.email_account.append_to, filters=filters) + record = self.get_doc(doctype, name, ignore_error=True) if name else None + return record + + def _create_reference_document(self, doctype): + """Create reference document if it does not exist in the system.""" + parent = xhiveframework.new_doc(doctype) + email_fields = self.get_email_fields(doctype) + + if email_fields.subject_field: + parent.set(email_fields.subject_field, xhiveframework.as_unicode(self.subject)[:140]) + + if email_fields.sender_field: + parent.set(email_fields.sender_field, xhiveframework.as_unicode(self.from_email)) + + if email_fields.sender_name_field: + parent.set(email_fields.sender_name_field, xhiveframework.as_unicode(self.from_real_name)) + + parent.flags.ignore_mandatory = True + + try: + parent.insert(ignore_permissions=True) + return parent.name + except xhiveframework.DuplicateEntryError: + # try and find matching parent + return xhiveframework.db.get_value(doctype, {email_fields.sender_field: self.from_email}) + + @staticmethod + def get_doc(doctype, docname, ignore_error=False): + try: + return xhiveframework.get_doc(doctype, docname) + except xhiveframework.DoesNotExistError: + if ignore_error: + return + raise + + @staticmethod + def get_relative_dt(days): + """Get relative to current datetime. Only relative days are supported.""" + return add_days(get_datetime(), days) + + @staticmethod + def get_users_linked_to_account(email_account): + """Get list of users who linked to Email account.""" + users = xhiveframework.get_all("User Email", filters={"email_account": email_account.name}, fields=["parent"]) + return list({user.get("parent") for user in users}) + + @staticmethod + def clean_subject(subject): + """Remove Prefixes like 'fw', FWD', 're' etc from subject.""" + # Match strings like "fw:", "re :" etc. + regex = r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*" + return xhiveframework.as_unicode(strip(re.sub(regex, "", subject, count=0, flags=re.IGNORECASE))) + + @staticmethod + def get_email_fields(doctype): + """Returns Email related fields of a doctype.""" + fields = xhiveframework._dict() + + email_fields = ["subject_field", "sender_field", "sender_name_field"] + meta = xhiveframework.get_meta(doctype) + + for field in email_fields: + if hasattr(meta, field): + fields[field] = getattr(meta, field) + return fields + + @staticmethod + def get_document(self, doctype, name): + """Is same as xhiveframework.get_doc but suppresses the DoesNotExist error.""" + try: + return xhiveframework.get_doc(doctype, name) + except xhiveframework.DoesNotExistError: + return None + + def as_dict(self): + """ """ + return { + "subject": self.subject, + "content": self.get_content(), + "text_content": self.text_content, + "sent_or_received": "Received", + "sender_full_name": self.from_real_name, + "sender": self.from_email, + "recipients": self.mail.get("To"), + "cc": self.mail.get("CC"), + "email_account": self.email_account.name, + "communication_medium": "Email", + "uid": self.uid, + "message_id": self.message_id, + "communication_date": self.date, + "has_attachment": 1 if self.attachments else 0, + "seen": self.seen_status or 0, + } diff --git a/xhiveframework/email/smtp.py b/xhiveframework/email/smtp.py new file mode 100644 index 0000000..7a0c3d1 --- /dev/null +++ b/xhiveframework/email/smtp.py @@ -0,0 +1,136 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import smtplib +from contextlib import suppress + +import xhiveframework +from xhiveframework import _ +from xhiveframework.email.oauth import Oauth +from xhiveframework.utils import cint, cstr + + +class InvalidEmailCredentials(xhiveframework.ValidationError): + pass + + +class SMTPServer: + def __init__( + self, + server, + login=None, + email_account=None, + password=None, + port=None, + use_tls=None, + use_ssl=None, + use_oauth=0, + access_token=None, + ): + self.login = login + self.email_account = email_account + self.password = password + self._server = server + self._port = port + self.use_tls = use_tls + self.use_ssl = use_ssl + self.use_oauth = use_oauth + self.access_token = access_token + self._session = None + + if not self.server: + xhiveframework.msgprint( + _("Email Account not setup. Please create a new Email Account from Settings > Email Account"), + raise_exception=xhiveframework.OutgoingEmailError, + ) + + @property + def port(self): + port = self._port or (self.use_ssl and 465) or (self.use_tls and 587) + return cint(port) + + @property + def server(self): + return cstr(self._server or "") + + def secure_session(self, conn): + """Secure the connection incase of TLS.""" + if self.use_tls: + conn.ehlo() + conn.starttls() + conn.ehlo() + + @property + def session(self): + """Get SMTP session. + + We make best effort to revive connection if it's disconnected by checking the connection + health before returning it to user.""" + if self.is_session_active(): + return self._session + + SMTP = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP + + try: + _session = SMTP(self.server, self.port, timeout=2 * 60) + if not _session: + xhiveframework.msgprint( + _("Could not connect to outgoing email server"), raise_exception=xhiveframework.OutgoingEmailError + ) + + self.secure_session(_session) + + if self.use_oauth: + Oauth(_session, self.email_account, self.login, self.access_token).connect() + + elif self.password: + res = _session.login(str(self.login or ""), str(self.password or "")) + + # check if logged correctly + if res[0] != 235: + xhiveframework.msgprint(res[1], raise_exception=xhiveframework.OutgoingEmailError) + + self._session = _session + self._enqueue_connection_closure() + return self._session + + except smtplib.SMTPAuthenticationError: + self.throw_invalid_credentials_exception() + + except OSError as e: + # Invalid mail server -- due to refusing connection + xhiveframework.throw( + _("Invalid Outgoing Mail Server or Port: {0}").format(str(e)), + title=_("Incorrect Configuration"), + ) + + def _enqueue_connection_closure(self): + if xhiveframework.request and hasattr(xhiveframework.request, "after_response"): + xhiveframework.request.after_response.add(self.quit) + elif xhiveframework.job: + xhiveframework.job.after_job.add(self.quit) + else: + # Console? + import atexit + + atexit.register(self.quit) + + def is_session_active(self): + if self._session: + try: + return self._session.noop()[0] == 250 + except Exception: + return False + + def quit(self): + with suppress(TimeoutError): + if self.is_session_active(): + self._session.quit() + + @classmethod + def throw_invalid_credentials_exception(cls): + xhiveframework.throw( + _("Please check your email login credentials."), + title=_("Invalid Credentials"), + exc=InvalidEmailCredentials, + ) diff --git a/xhiveframework/email/test_email_body.py b/xhiveframework/email/test_email_body.py new file mode 100644 index 0000000..1e541a7 --- /dev/null +++ b/xhiveframework/email/test_email_body.py @@ -0,0 +1,207 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import base64 +import os + +import xhiveframework +from xhiveframework import safe_decode +from xhiveframework.email.doctype.email_queue.email_queue import QueueBuilder, SendMailContext +from xhiveframework.email.email_body import ( + get_email, + get_header, + inline_style_in_html, + replace_filename_with_cid, +) +from xhiveframework.email.receive import Email +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestEmailBody(XhiveFrameworkTestCase): + def setUp(self): + email_html = """ +
      +

      Hey John Doe!

      +

      This is embedded image you asked for

      + +
      +""" + email_text = """ +Hey John Doe! +This is the text version of this email +""" + + img_path = os.path.abspath("assets/xhiveframework/images/xhiveframework-favicon.svg") + with open(img_path, "rb") as f: + img_content = f.read() + img_base64 = base64.b64encode(img_content).decode() + + # email body keeps 76 characters on one line + self.img_base64 = fixed_column_width(img_base64, 76) + + self.email_string = ( + get_email( + recipients=["test@example.com"], + sender="me@example.com", + subject="Test Subject", + content=email_html, + text_content=email_text, + ) + .as_string() + .replace("\r\n", "\n") + ) + + def test_prepare_message_returns_already_encoded_string(self): + uni_chr1 = chr(40960) + uni_chr2 = chr(1972) + + QueueBuilder( + recipients=["test@example.com"], + sender="me@example.com", + subject="Test Subject", + message=f"

      {uni_chr1}abcd{uni_chr2}

      ", + text_content="whatever", + ).process() + queue_doc = xhiveframework.get_last_doc("Email Queue") + mail_ctx = SendMailContext(queue_doc=queue_doc) + result = mail_ctx.build_message(recipient_email="test@test.com") + self.assertTrue(b"

      =EA=80=80abcd=DE=B4

      " in result) + + def test_prepare_message_returns_cr_lf(self): + QueueBuilder( + recipients=["test@example.com"], + sender="me@example.com", + subject="Test Subject", + message="

      \n this is a test of newlines\n" + "

      ", + text_content="whatever", + ).process() + queue_doc = xhiveframework.get_last_doc("Email Queue") + mail_ctx = SendMailContext(queue_doc=queue_doc) + result = safe_decode(mail_ctx.build_message(recipient_email="test@test.com")) + + self.assertTrue(result.count("\n") == result.count("\r")) + + def test_image(self): + img_signature = """ +Content-Type: image/svg+xml +MIME-Version: 1.0 +Content-Transfer-Encoding: base64 +Content-Disposition: inline; filename="xhiveframework-favicon.svg" +""" + self.assertTrue(img_signature in self.email_string) + self.assertTrue(self.img_base64 in self.email_string) + + def test_text_content(self): + text_content = """ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: quoted-printable + + +Hey John Doe! +This is the text version of this email +""" + self.assertTrue(text_content in self.email_string) + + def test_email_content(self): + html_head = """ +Content-Type: text/html; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: quoted-printable + + + +""" + + html = """

      Hey John Doe!

      """ + + self.assertTrue(html_head in self.email_string) + self.assertTrue(html in self.email_string) + + def test_replace_filename_with_cid(self): + original_message = """ +
      + test + +
      + """ + message, inline_images = replace_filename_with_cid(original_message) + + processed_message = """ +
      + test + +
      + """.format(inline_images[0].get("content_id")) + self.assertEqual(message, processed_message) + + def test_inline_styling(self): + html = """ +

      Hi John

      +

      This is a test email

      +""" + transformed_html = """ +

      Hi John

      +

      This is a test email

      +""" + self.assertTrue(transformed_html in inline_style_in_html(html)) + + def test_email_header(self): + email_html = """ +

      Hey John Doe!

      +

      This is embedded image you asked for

      +""" + email_string = get_email( + recipients=["test@example.com"], + sender="me@example.com", + subject="Test Subject\u2028, with line break, \nand Line feed \rand carriage return.", + content=email_html, + header=["Email Title", "green"], + ).as_string() + # REDESIGN-TODO: Add style for indicators in email + self.assertTrue("""""" in email_string) + self.assertTrue("Email Title" in email_string) + self.assertIn( + "Subject: Test Subject, with line break, and Line feed and carriage return.", email_string + ) + + def test_get_email_header(self): + html = get_header(["This is test", "orange"]) + self.assertTrue('' in html) + self.assertTrue("This is test" in html) + + html = get_header(["This is another test"]) + self.assertTrue("This is another test" in html) + + html = get_header("This is string") + self.assertTrue("This is string" in html) + + def test_8bit_utf_8_decoding(self): + text_content_bytes = b"\xed\x95\x9c\xea\xb8\x80\xe1\xa5\xa1\xe2\x95\xa5\xe0\xba\xaa\xe0\xa4\x8f" + text_content = text_content_bytes.decode("utf-8") + + content_bytes = ( + b"""MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Disposition: inline +Content-Transfer-Encoding: 8bit +From: test1_@xhiveerp.com +Reply-To: test2_@xhiveerp.com +""" + + text_content_bytes + ) + + mail = Email(content_bytes) + self.assertEqual(mail.text_content, text_content) + + def test_poorly_encoded_messages(self): + mail = Email.decode_email( + "=?iso-2022-jp?B?VEFLQVlBTUEgS2FvcnUgWxskQnxiOzMbKEIgGyRCNzAbKEJd?=\n\t" + ) + self.assertIn("user@example.com", mail) + + +def fixed_column_width(string, chunk_size): + parts = [string[0 + i : chunk_size + i] for i in range(0, len(string), chunk_size)] + return "\n".join(parts) diff --git a/xhiveframework/email/test_smtp.py b/xhiveframework/email/test_smtp.py new file mode 100644 index 0000000..ec44b0d --- /dev/null +++ b/xhiveframework/email/test_smtp.py @@ -0,0 +1,88 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: The MIT License + +import xhiveframework +from xhiveframework.email.doctype.email_account.email_account import EmailAccount +from xhiveframework.email.smtp import SMTPServer +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSMTP(XhiveFrameworkTestCase): + def test_smtp_ssl_session(self): + for port in [None, 0, 465, "465"]: + make_server(port, 1, 0) + + def test_smtp_tls_session(self): + for port in [None, 0, 587, "587"]: + make_server(port, 0, 1) + + def test_get_email_account(self): + existing_email_accounts = xhiveframework.get_all( + "Email Account", fields=["name", "enable_outgoing", "default_outgoing", "append_to", "use_imap"] + ) + unset_details = {"enable_outgoing": 0, "default_outgoing": 0, "append_to": None, "use_imap": 0} + for email_account in existing_email_accounts: + xhiveframework.db.set_value("Email Account", email_account["name"], unset_details) + + # remove mail_server config so that test@example.com is not created + mail_server = xhiveframework.conf.get("mail_server") + del xhiveframework.conf["mail_server"] + + xhiveframework.local.outgoing_email_account = {} + + xhiveframework.local.outgoing_email_account = {} + # lowest preference given to email account with default incoming enabled + create_email_account( + email_id="default_outgoing_enabled@gmail.com", + password="password", + enable_outgoing=1, + default_outgoing=1, + ) + self.assertEqual(EmailAccount.find_outgoing().email_id, "default_outgoing_enabled@gmail.com") + + xhiveframework.local.outgoing_email_account = {} + # highest preference given to email account with append_to matching + create_email_account( + email_id="append_to@gmail.com", + password="password", + enable_outgoing=1, + default_outgoing=1, + append_to="Blog Post", + ) + self.assertEqual( + EmailAccount.find_outgoing(match_by_doctype="Blog Post").email_id, "append_to@gmail.com" + ) + + # add back the mail_server + xhiveframework.conf["mail_server"] = mail_server + for email_account in existing_email_accounts: + set_details = { + "enable_outgoing": email_account["enable_outgoing"], + "default_outgoing": email_account["default_outgoing"], + "append_to": email_account["append_to"], + } + xhiveframework.db.set_value("Email Account", email_account["name"], set_details) + + +def create_email_account(email_id, password, enable_outgoing, default_outgoing=0, append_to=None): + email_dict = { + "email_id": email_id, + "passsword": password, + "enable_outgoing": enable_outgoing, + "default_outgoing": default_outgoing, + "enable_incoming": 1, + "append_to": append_to, + "is_dummy_password": 1, + "smtp_server": "127.0.0.1", + "use_imap": 0, + } + + email_account = xhiveframework.new_doc("Email Account") + email_account.update(email_dict) + email_account.save() + + +def make_server(port, ssl, tls): + server = SMTPServer(server="smtp.gmail.com", port=port, use_ssl=ssl, use_tls=tls) + + server.session diff --git a/xhiveframework/email/utils.py b/xhiveframework/email/utils.py new file mode 100644 index 0000000..96ad95e --- /dev/null +++ b/xhiveframework/email/utils.py @@ -0,0 +1,18 @@ +# Copyright (c) 2019, XhiveFramework Technologies Pvt. Ltd. and contributors +# License: MIT. See LICENSE + +import imaplib +import poplib + +from xhiveframework.utils import cint + + +def get_port(doc): + if not doc.incoming_port: + if doc.use_imap: + doc.incoming_port = imaplib.IMAP4_SSL_PORT if doc.use_ssl else imaplib.IMAP4_PORT + + else: + doc.incoming_port = poplib.POP3_SSL_PORT if doc.use_ssl else poplib.POP3_PORT + + return cint(doc.incoming_port) diff --git a/xhiveframework/exceptions.py b/xhiveframework/exceptions.py new file mode 100644 index 0000000..b69bf0b --- /dev/null +++ b/xhiveframework/exceptions.py @@ -0,0 +1,301 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# BEWARE don't put anything in this file except exceptions +from werkzeug.exceptions import NotFound + + +class SiteNotSpecifiedError(Exception): + def __init__(self, *args, **kwargs): + self.message = "Please specify --site sitename" + super(Exception, self).__init__(self.message) + + +class UrlSchemeNotSupported(Exception): + pass + + +class ValidationError(Exception): + http_status_code = 417 + + +class XhiveFrameworkTypeError(TypeError): + http_status_code = 417 + + +class AuthenticationError(Exception): + http_status_code = 401 + + +class SessionExpired(Exception): + http_status_code = 401 + + +class PermissionError(Exception): + http_status_code = 403 + + +class DoesNotExistError(ValidationError): + http_status_code = 404 + + +class PageDoesNotExistError(ValidationError): + http_status_code = 404 + + +class NameError(Exception): + http_status_code = 409 + + +class OutgoingEmailError(Exception): + http_status_code = 501 + + +class SessionStopped(Exception): + http_status_code = 503 + + +class UnsupportedMediaType(Exception): + http_status_code = 415 + + +class RequestToken(Exception): + http_status_code = 200 + + +class Redirect(Exception): + http_status_code = 301 + + +class CSRFTokenError(Exception): + http_status_code = 400 + + +class TooManyRequestsError(Exception): + http_status_code = 429 + + +class ImproperDBConfigurationError(Exception): + """ + Used when xhiveframework detects that database or tables are not properly + configured + """ + + def __init__(self, reason, msg=None): + if not msg: + msg = "MariaDb is not properly configured" + super().__init__(msg) + self.reason = reason + + +class DuplicateEntryError(NameError): + pass + + +class DataError(ValidationError): + pass + + +class UnknownDomainError(Exception): + pass + + +class MappingMismatchError(ValidationError): + pass + + +class InvalidStatusError(ValidationError): + pass + + +class MandatoryError(ValidationError): + pass + + +class NonNegativeError(ValidationError): + pass + + +class InvalidSignatureError(ValidationError): + pass + + +class RateLimitExceededError(ValidationError): + pass + + +class CannotChangeConstantError(ValidationError): + pass + + +class CharacterLengthExceededError(ValidationError): + pass + + +class UpdateAfterSubmitError(ValidationError): + pass + + +class LinkValidationError(ValidationError): + pass + + +class CancelledLinkError(LinkValidationError): + pass + + +class DocstatusTransitionError(ValidationError): + pass + + +class TimestampMismatchError(ValidationError): + pass + + +class EmptyTableError(ValidationError): + pass + + +class LinkExistsError(ValidationError): + pass + + +class InvalidEmailAddressError(ValidationError): + pass + + +class InvalidNameError(ValidationError): + pass + + +class InvalidPhoneNumberError(ValidationError): + pass + + +class TemplateNotFoundError(ValidationError): + pass + + +class UniqueValidationError(ValidationError): + pass + + +class AppNotInstalledError(ValidationError): + pass + + +class IncorrectSitePath(NotFound): + pass + + +class ImplicitCommitError(ValidationError): + pass + + +class RetryBackgroundJobError(Exception): + pass + + +class DocumentLockedError(ValidationError): + pass + + +class CircularLinkingError(ValidationError): + pass + + +class SecurityException(Exception): + pass + + +class InvalidColumnName(ValidationError): + pass + + +class IncompatibleApp(ValidationError): + pass + + +class InvalidDates(ValidationError): + pass + + +class DataTooLongException(ValidationError): + pass + + +class FileAlreadyAttachedException(Exception): + pass + + +class DocumentAlreadyRestored(ValidationError): + pass + + +class AttachmentLimitReached(ValidationError): + pass + + +class QueryTimeoutError(Exception): + pass + + +class QueryDeadlockError(Exception): + pass + + +class InReadOnlyMode(ValidationError): + http_status_code = 503 # temporarily not available + + +class SessionBootFailed(ValidationError): + http_status_code = 500 + + +class PrintFormatError(ValidationError): + pass + + +class TooManyWritesError(Exception): + pass + + +# OAuth exceptions +class InvalidAuthorizationHeader(CSRFTokenError): + pass + + +class InvalidAuthorizationPrefix(CSRFTokenError): + pass + + +class InvalidAuthorizationToken(CSRFTokenError): + pass + + +class InvalidDatabaseFile(ValidationError): + pass + + +class ExecutableNotFound(FileNotFoundError): + pass + + +class InvalidRoundingMethod(FileNotFoundError): + pass + + +class InvalidRemoteException(Exception): + pass + + +class LinkExpired(ValidationError): + http_status_code = 410 + title = "Link Expired" + message = "The link has expired" + + +class InvalidKeyError(ValidationError): + http_status_code = 401 + title = "Invalid Key" + message = "The document key is invalid" diff --git a/xhiveframework/geo/__init__.py b/xhiveframework/geo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/geo/country_info.json b/xhiveframework/geo/country_info.json new file mode 100644 index 0000000..8ce03b9 --- /dev/null +++ b/xhiveframework/geo/country_info.json @@ -0,0 +1,3007 @@ +{ + "Afghanistan": { + "code": "af", + "currency": "AFN", + "currency_fraction": "Pul", + "currency_fraction_units": 100, + "currency_symbol": "\u060b", + "number_format": "#,###.##", + "timezones": [ + "Asia/Kabul" + ], + "isd": "+93" + }, + "Albania": { + "code": "al", + "currency": "ALL", + "currency_fraction": "Qindark\u00eb", + "currency_fraction_units": 100, + "currency_name": "Lek", + "currency_symbol": "L", + "number_format": "#,###.##", + "timezones": [ + "Europe/Tirane" + ], + "isd": "+355" + }, + "Algeria": { + "code": "dz", + "currency": "DZD", + "currency_fraction": "Santeem", + "currency_fraction_units": 100, + "currency_name": "Algerian Dinar", + "currency_symbol": "\u062f.\u062c", + "number_format": "#,###.##", + "timezones": [ + "Africa/Algiers" + ], + "isd": "+213" + }, + "American Samoa": { + "code": "as", + "number_format": "#,###.##", + "isd": "+1684" + }, + "Andorra": { + "code": "ad", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Andorra" + ], + "isd": "+376" + }, + "Angola": { + "code": "ao", + "currency": "KZ", + "currency_fraction": "C\u00eantimo", + "currency_fraction_units": 100, + "currency_symbol": "AOA", + "currency_name": "Kwanza", + "number_format": "#,###.##", + "timezones": [ + "Africa/Luanda" + ], + "isd": "+244" + }, + "Anguilla": { + "code": "ai", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/Anguilla" + ], + "isd": "+1264" + }, + "Antarctica": { + "code": "aq", + "number_format": "#,###.##", + "timezones": [ + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Vostok" + ], + "isd": "+672" + }, + "Antigua and Barbuda": { + "code": "ag", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/Antigua" + ], + "isd": "+1268" + }, + "Argentina": { + "code": "ar", + "currency": "ARS", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Argentine Peso", + "currency_symbol": "$", + "number_format": "#.###,##", + "timezones": [ + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia" + ], + "isd": "+54" + }, + "Armenia": { + "code": "am", + "currency": "AMD", + "currency_fraction": "Luma", + "currency_fraction_units": 100, + "currency_name": "Armenian Dram", + "currency_symbol": "\u058f", + "number_format": "#,###.##", + "timezones": [ + "Asia/Yerevan" + ], + "isd": "+374" + }, + "Aruba": { + "code": "aw", + "currency": "AWG", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Aruban Florin", + "currency_symbol": "Afl", + "number_format": "#,###.##", + "timezones": [ + "America/Aruba" + ], + "isd": "+297" + }, + "Australia": { + "code": "au", + "currency": "AUD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Australian Dollar", + "currency_symbol": "$", + "number_format": "# ###.##", + "timezones": [ + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Currie", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/Perth", + "Australia/Sydney" + ], + "isd": "+61" + }, + "Austria": { + "code": "at", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Vienna" + ], + "isd": "+43" + }, + "Azerbaijan": { + "code": "az", + "currency_fraction": "Q\u0259pik", + "currency_fraction_units": 100, + "currency_symbol": "", + "number_format": "#,###.##", + "timezones": [ + "Asia/Baku" + ], + "isd": "+994" + }, + "Bahamas": { + "code": "bs", + "currency": "BSD", + "currency_name": "Bahamian Dollar", + "number_format": "#,###.##", + "timezones": [ + "America/Nassau" + ], + "isd": "+1242" + }, + "Bahrain": { + "code": "bh", + "currency": "BHD", + "currency_fraction": "Fils", + "currency_fraction_units": 1000, + "currency_name": "Bahraini Dinar", + "currency_symbol": ".\u062f.\u0628", + "number_format": "#,###.###", + "timezones": [ + "Asia/Bahrain" + ], + "isd": "+973" + }, + "Bangladesh": { + "code": "bd", + "currency": "BDT", + "currency_fraction": "Paisa", + "currency_fraction_units": 100, + "currency_name": "Taka", + "currency_symbol": "\u09f3", + "number_format": "#,###.##", + "timezones": [ + "Asia/Dhaka" + ], + "isd": "+880" + }, + "Barbados": { + "code": "bb", + "currency": "BBD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Barbados Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Barbados" + ], + "isd": "+1246" + }, + "Belarus": { + "code": "by", + "currency_fraction": "Kapyeyka", + "currency_fraction_units": 100, + "currency_symbol": "Br", + "number_format": "#,###.##", + "timezones": [ + "Europe/Minsk" + ], + "isd": "+375" + }, + "Belgium": { + "code": "be", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Brussels" + ], + "isd": "+32" + }, + "Belize": { + "code": "bz", + "currency": "BZD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Belize Dollar", + "currency_symbol": "$", + "date_format": "mm-dd-yyyy", + "number_format": "#,###.##", + "timezones": [ + "America/Belize" + ], + "isd": "+501" + }, + "Benin": { + "code": "bj", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Porto-Novo" + ], + "isd": "+229" + }, + "Bermuda": { + "code": "bm", + "currency": "BMD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Bermudian Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Atlantic/Bermuda" + ], + "isd": "+1441" + }, + "Bhutan": { + "code": "bt", + "currency": "BTN", + "currency_fraction": "Chetrum", + "currency_fraction_units": 100, + "currency_name": "Ngultrum", + "currency_symbol": "Nu.", + "number_format": "#,###.##", + "timezones": [ + "Asia/Thimphu" + ], + "isd": "+975" + }, + "Bolivia, Plurinational State of": { + "code": "bo", + "currency": "BOB", + "currency_name": "Boliviano", + "number_format": "#,###.##", + "isd": "+591" + }, + "Bonaire, Sint Eustatius and Saba": { + "code": "bq", + "number_format": "#,###.##" + }, + "Bosnia and Herzegovina": { + "code": "ba", + "currency": "BAM", + "currency_fraction": "Fening", + "currency_fraction_units": 100, + "currency_symbol": "KM", + "number_format": "#.###,##", + "timezones": [ + "Europe/Sarajevo" + ], + "isd": "+387" + }, + "Botswana": { + "code": "bw", + "currency": "BWP", + "currency_fraction": "Thebe", + "currency_fraction_units": 100, + "currency_name": "Pula", + "currency_symbol": "P", + "number_format": "#,###.##", + "timezones": [ + "Africa/Gaborone" + ], + "isd": "+267" + }, + "Bouvet Island": { + "code": "bv", + "number_format": "#,###.##", + "isd": "+47" + }, + "Brazil": { + "code": "br", + "currency": "BRL", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_symbol": "R$", + "date_format": "dd/mm/yyyy", + "number_format": "#.###,##", + "timezones": [ + "America/Araguaina", + "America/Bahia", + "America/Belem", + "America/Boa_Vista", + "America/Campo_Grande", + "America/Cuiaba", + "America/Eirunepe", + "America/Fortaleza", + "America/Maceio", + "America/Manaus", + "America/Noronha", + "America/Porto_Velho", + "America/Recife", + "America/Rio_Branco", + "America/Santarem", + "America/Sao_Paulo" + ], + "isd": "+55" + }, + "British Indian Ocean Territory": { + "code": "io", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Indian/Chagos" + ], + "isd": "+246" + }, + "Brunei Darussalam": { + "code": "bn", + "currency": "BND", + "currency_name": "Brunei Dollar", + "number_format": "#,###.##", + "timezones": [ + "Asia/Brunei" + ], + "isd": "+673" + }, + "Bulgaria": { + "code": "bg", + "currency": "BGN", + "currency_name": "Bulgarian Lev", + "currency_fraction": "Stotinka", + "currency_fraction_units": 100, + "currency_symbol": "\u043b\u0432", + "number_format": "#,###.##", + "timezones": [ + "Europe/Sofia" + ], + "isd": "+359" + }, + "Burkina Faso": { + "code": "bf", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Ouagadougou" + ], + "isd": "+226" + }, + "Burundi": { + "code": "bi", + "currency": "BIF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Burundi Franc", + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Africa/Bujumbura" + ], + "isd": "+257" + }, + "Cambodia": { + "code": "kh", + "currency": "KHR", + "currency_fraction": "Sen", + "currency_fraction_units": 100, + "currency_name": "Riel", + "currency_symbol": "\u17db", + "number_format": "#,###.##", + "timezones": [ + "Asia/Phnom_Penh" + ], + "isd": "+855" + }, + "Cameroon": { + "code": "cm", + "currency": "XAF", + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Douala" + ], + "isd": "+237" + }, + "Canada": { + "code": "ca", + "currency": "CAD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Canadian Dollar", + "currency_symbol": "$", + "date_format": "mm-dd-yyyy", + "number_format": "#,###.##", + "timezones": [ + "America/Atikokan", + "America/Blanc-Sablon", + "America/Cambridge_Bay", + "America/Creston", + "America/Dawson", + "America/Dawson_Creek", + "America/Edmonton", + "America/Glace_Bay", + "America/Goose_Bay", + "America/Halifax", + "America/Inuvik", + "America/Iqaluit", + "America/Moncton", + "America/Montreal", + "America/Nipigon", + "America/Pangnirtung", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Regina", + "America/Resolute", + "America/St_Johns", + "America/Swift_Current", + "America/Thunder_Bay", + "America/Toronto", + "America/Vancouver", + "America/Whitehorse", + "America/Winnipeg", + "America/Yellowknife" + ], + "isd": "+1" + }, + "Cape Verde": { + "code": "cv", + "currency": "CVE", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Cape Verde Escudo", + "currency_symbol": "Esc or $", + "number_format": "#,###.##", + "timezones": [ + "Atlantic/Cape_Verde" + ], + "isd": "+238" + }, + "Cayman Islands": { + "code": "ky", + "currency": "KYD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Cayman Islands Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Cayman" + ], + "isd": "+ 345" + }, + "Central African Republic": { + "code": "cf", + "currency": "XAF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "number_format": "#,###.##", + "timezones": [ + "Africa/Bangui" + ], + "isd": "+236" + }, + "Chad": { + "code": "td", + "currency": "XAF", + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Ndjamena" + ], + "isd": "+235" + }, + "Chile": { + "code": "cl", + "currency": "CLP", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Chilean Peso", + "currency_symbol": "$", + "number_format": "#.###", + "timezones": [ + "America/Santiago", + "Pacific/Easter" + ], + "isd": "+56" + }, + "China": { + "code": "cn", + "currency": "CNY", + "currency_name": "Yuan Renminbi", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "date_format": "yyyy-mm-dd", + "number_format": "#,###.##", + "timezones": [ + "Asia/Chongqing", + "Asia/Harbin", + "Asia/Kashgar", + "Asia/Shanghai", + "Asia/Urumqi" + ], + "isd": "+86" + }, + "Christmas Island": { + "code": "cx", + "number_format": "#,###.##", + "timezones": [ + "Indian/Christmas" + ], + "isd": "+61" + }, + "Cocos (Keeling) Islands": { + "code": "cc", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Indian/Cocos" + ], + "isd": "+61" + }, + "Colombia": { + "code": "co", + "currency": "COP", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Colombian Peso", + "currency_symbol": "$", + "number_format": "#.###,##", + "timezones": [ + "America/Bogota" + ], + "isd": "+57" + }, + "Comoros": { + "code": "km", + "currency": "KMF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Comoro Franc", + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Indian/Comoro" + ], + "isd": "+269" + }, + "Congo": { + "code": "cg", + "number_format": "#,###.##", + "currency": "XAF", + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "isd": "+242" + }, + "Congo, The Democratic Republic of the": { + "code": "cd", + "number_format": "#,###.##", + "currency": "CDF", + "currency_name": "Congolese franc", + "currency_symbol": "FC", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "isd": "+243" + }, + "Cook Islands": { + "code": "ck", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Rarotonga" + ], + "isd": "+682" + }, + "Costa Rica": { + "code": "cr", + "currency": "CRC", + "currency_fraction": "C\u00e9ntimo", + "currency_fraction_units": 100, + "currency_name": "Costa Rican Colon", + "currency_symbol": "\u20a1", + "number_format": "#.###,##", + "timezones": [ + "America/Costa_Rica" + ], + "isd": "+506" + }, + "Croatia": { + "code": "hr", + "currency": "HRK", + "currency_fraction": "Lipa", + "currency_fraction_units": 100, + "currency_name": "Croatian Kuna", + "currency_symbol": "kn", + "number_format": "#.###,##", + "timezones": [ + "Europe/Zagreb" + ], + "isd": "+385" + }, + "Cuba": { + "code": "cu", + "currency": "CUP", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Cuban Peso", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Havana" + ], + "isd": "+53" + }, + "Cura\u00e7ao": { + "code": "cw", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "\u0192", + "number_format": "#,###.##" + }, + "Cyprus": { + "code": "cy", + "currency": "CYP", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Cyprus Pound", + "currency_symbol": "\u20ac", + "number_format": "#.###,##", + "timezones": [ + "Asia/Nicosia" + ], + "isd": "+357" + }, + "Czech Republic": { + "code": "cz", + "currency": "CZK", + "currency_fraction": "Hal\u00e9\u0159", + "currency_fraction_units": 100, + "currency_name": "Czech Koruna", + "currency_symbol": "K\u010d", + "number_format": "#.###,##", + "timezones": [ + "Europe/Prague" + ], + "isd": "+420" + }, + "Denmark": { + "code": "dk", + "currency": "DKK", + "currency_fraction": "\u00d8re", + "currency_fraction_units": 100, + "currency_name": "Danish Krone", + "currency_symbol": "kr", + "number_format": "#.###,##", + "timezones": [ + "Europe/Copenhagen" + ], + "isd": "+45" + }, + "Djibouti": { + "code": "dj", + "currency": "DJF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Djibouti Franc", + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Africa/Djibouti" + ], + "isd": "+253" + }, + "Dominica": { + "code": "dm", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/Dominica" + ], + "isd": "+1767" + }, + "Dominican Republic": { + "code": "do", + "currency": "DOP", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Dominican Peso", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Santo_Domingo" + ], + "isd": "+1849" + }, + "Ecuador": { + "code": "ec", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Guayaquil", + "Pacific/Galapagos" + ], + "isd": "+593" + }, + "Egypt": { + "code": "eg", + "currency": "EGP", + "currency_fraction": "Piastre[F]", + "currency_fraction_units": 100, + "currency_name": "Egyptian Pound", + "currency_symbol": "\u00a3 or \u062c.\u0645", + "number_format": "#,###.##", + "timezones": [ + "Africa/Cairo" + ], + "isd": "+20" + }, + "El Salvador": { + "code": "sv", + "currency": "USD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_name": "Dolar estadounidense", + "currency_symbol": "$", + "date_format": "dd-mm-yyyy", + "number_format": "#,###.##", + "timezones": [ + "America/El_Salvador" + ], + "isd": "+503" + }, + "Equatorial Guinea": { + "code": "gq", + "currency": "XAF", + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Malabo" + ], + "isd": "+240" + }, + "Eritrea": { + "code": "er", + "currency": "ERN", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Nakfa", + "currency_symbol": "Nfk", + "number_format": "#,###.##", + "timezones": [ + "Africa/Asmara" + ], + "isd": "+291" + }, + "Estonia": { + "code": "ee", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Euro", + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Tallinn" + ], + "isd": "+372" + }, + "Ethiopia": { + "code": "et", + "currency": "ETB", + "currency_fraction": "Santim", + "currency_fraction_units": 100, + "currency_name": "Ethiopian Birr", + "currency_symbol": "Br", + "number_format": "#,###.##", + "timezones": [ + "Africa/Addis_Ababa" + ], + "isd": "+251" + }, + "Falkland Islands (Malvinas)": { + "code": "fk", + "currency": "FKP", + "currency_name": "Falkland Islands Pound", + "number_format": "#,###.##", + "isd": "+500" + }, + "Faroe Islands": { + "code": "fo", + "currency_fraction": "\u00d8re", + "currency_fraction_units": 100, + "currency_symbol": "kr", + "number_format": "#,###.##", + "timezones": [ + "Atlantic/Faroe" + ], + "isd": "+298" + }, + "Fiji": { + "code": "fj", + "currency": "FJD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Fiji Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Fiji" + ], + "isd": "+679" + }, + "Finland": { + "code": "fi", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Helsinki" + ], + "isd": "+358" + }, + "France": { + "code": "fr", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "# ###,##", + "date_format": "dd/mm/yyyy", + "timezones": [ + "Europe/Paris" + ], + "isd": "+33" + }, + "French Guiana": { + "code": "gf", + "number_format": "#,###.##", + "timezones": [ + "America/Cayenne" + ], + "isd": "+594" + }, + "French Polynesia": { + "code": "pf", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Gambier", + "Pacific/Marquesas", + "Pacific/Tahiti" + ], + "isd": "+689" + }, + "French Southern Territories": { + "code": "tf", + "number_format": "#,###.##", + "isd": "+262" + }, + "Gabon": { + "code": "ga", + "currency": "XAF", + "currency_name": "Central African CFA Franc", + "currency_symbol": "FCFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Libreville" + ], + "isd": "+241" + }, + "Gambia": { + "code": "gm", + "currency": "GMD", + "currency_name": "Dalasi", + "number_format": "#,###.##", + "timezones": [ + "Africa/Banjul" + ], + "isd": "+220" + }, + "Georgia": { + "code": "ge", + "currency_fraction": "Tetri", + "currency_fraction_units": 100, + "currency_symbol": "\u10da", + "number_format": "#,###.##", + "timezones": [ + "Asia/Tbilisi" + ], + "isd": "+995" + }, + "Germany": { + "code": "de", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#.###,##", + "date_format": "dd.mm.yyyy", + "time_format": "HH:mm", + "timezones": [ + "Europe/Berlin" + ], + "isd": "+49" + }, + "Ghana": { + "code": "gh", + "currency": "GHS", + "currency_fraction": "Pesewa", + "currency_fraction_units": 100, + "currency_symbol": "\u20b5", + "number_format": "#,###.##", + "timezones": [ + "Africa/Accra" + ], + "isd": "+233" + }, + "Gibraltar": { + "code": "gi", + "currency": "GIP", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_name": "Gibraltar Pound", + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Europe/Gibraltar" + ], + "isd": "+350" + }, + "Greece": { + "code": "gr", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Athens" + ], + "isd": "+30" + }, + "Greenland": { + "code": "gl", + "number_format": "#,###.##", + "timezones": [ + "America/Danmarkshavn", + "America/Godthab", + "America/Scoresbysund", + "America/Thule" + ], + "isd": "+299" + }, + "Grenada": { + "code": "gd", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/Grenada" + ], + "isd": "+1473" + }, + "Guadeloupe": { + "code": "gp", + "number_format": "#,###.##", + "timezones": [ + "America/Guadeloupe" + ], + "isd": "+590" + }, + "Guam": { + "code": "gu", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Guam" + ], + "isd": "+1671" + }, + "Guatemala": { + "code": "gt", + "currency": "GTQ", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Quetzal", + "currency_symbol": "Q", + "number_format": "#,###.##", + "timezones": [ + "America/Guatemala" + ], + "isd": "+502" + }, + "Guernsey": { + "code": "gg", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Europe/London" + ], + "isd": "+44" + }, + "Guinea": { + "code": "gn", + "currency": "GNF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Guinea Franc", + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Africa/Conakry" + ], + "isd": "+224" + }, + "Guinea-Bissau": { + "code": "gw", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Bissau" + ], + "isd": "+245" + }, + "Guyana": { + "code": "gy", + "currency": "GYD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Guyana Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Guyana" + ], + "isd": "+592" + }, + "Haiti": { + "code": "ht", + "currency": "HTG", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Gourde", + "currency_symbol": "G", + "number_format": "#,###.##", + "timezones": [ + "America/Guatemala", + "America/Port-au-Prince" + ], + "isd": "+509" + }, + "Heard Island and McDonald Islands": { + "code": "hm", + "number_format": "#,###.##", + "isd": "+0" + }, + "Holy See (Vatican City State)": { + "code": "va", + "number_format": "#,###.##", + "isd": "+379" + }, + "Honduras": { + "code": "hn", + "currency": "HNL", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Lempira", + "currency_symbol": "L", + "number_format": "#,###.##", + "timezones": [ + "America/Tegucigalpa" + ], + "isd": "+504" + }, + "Hong Kong": { + "code": "hk", + "currency": "HKD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Hong Kong Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Asia/Hong_Kong" + ], + "isd": "+852" + }, + "Hungary": { + "code": "hu", + "currency": "HUF", + "currency_fraction": "Fill\u00e9r", + "currency_fraction_units": 100, + "currency_name": "Forint", + "currency_symbol": "Ft", + "date_format": "yyyy-mm-dd", + "number_format": "#.###", + "timezones": [ + "Europe/Budapest" + ], + "isd": "+36" + }, + "Iceland": { + "code": "is", + "currency": "ISK", + "currency_fraction": "Eyrir", + "currency_fraction_units": 100, + "currency_name": "Iceland Krona", + "currency_symbol": "kr", + "number_format": "#.###", + "timezones": [ + "Atlantic/Reykjavik" + ], + "isd": "+354" + }, + "India": { + "code": "in", + "currency": "INR", + "currency_fraction": "Paisa", + "currency_fraction_units": 100, + "currency_name": "Indian Rupee", + "currency_symbol": "\u20b9", + "number_format": "#,##,###.##", + "timezones": [ + "Asia/Kolkata" + ], + "isd": "+91" + }, + "Indonesia": { + "code": "id", + "currency": "IDR", + "currency_fraction": "Sen", + "currency_fraction_units": 100, + "currency_name": "Rupiah", + "currency_symbol": "Rp", + "number_format": "#.###,##", + "timezones": [ + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Makassar", + "Asia/Pontianak" + ], + "isd": "+62" + }, + "Iran": { + "code": "ir", + "currency": "IRR", + "currency_name": "Iranian Rial", + "currency_symbol": "\ufdfc", + "number_format": "#,###.##", + "timezones": [ + "Asia/Tehran" + ], + "isd": "+98" + }, + "Iraq": { + "code": "iq", + "currency": "IQD", + "currency_fraction": "Fils", + "currency_fraction_units": 1000, + "currency_name": "Iraqi Dinar", + "currency_symbol": "\u0639.\u062f", + "number_format": "#,###.###", + "timezones": [ + "Asia/Baghdad" + ], + "isd": "+964" + }, + "Ireland": { + "code": "ie", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Dublin" + ], + "isd": "+353" + }, + "Isle of Man": { + "code": "im", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Europe/London" + ], + "isd": "+44" + }, + "Israel": { + "code": "il", + "currency": "ILS", + "currency_fraction": "Agora", + "currency_fraction_units": 100, + "currency_name": "New Israeli Sheqel", + "currency_symbol": "\u20aa", + "number_format": "#,###.##", + "timezones": [ + "Asia/Jerusalem" + ], + "isd": "+972" + }, + "Italy": { + "code": "it", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#.###,##", + "date_format": "dd/mm/yyyy", + "timezones": [ + "Europe/Rome" + ], + "isd": "+39" + }, + "Ivory Coast": { + "code": "ci", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timeszones": [ + "Africa/Abidjan" + ], + "isd": "+225" + }, + "Jamaica": { + "code": "jm", + "currency": "JMD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Jamaican Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Jamaica" + ], + "isd": "+1876" + }, + "Japan": { + "code": "jp", + "currency": "JPY", + "currency_fraction": "Sen[G]", + "currency_fraction_units": 100, + "currency_name": "Yen", + "currency_symbol": "\u00a5", + "number_format": "#,###", + "timezones": [ + "Asia/Tokyo" + ], + "isd": "+81" + }, + "Jersey": { + "code": "je", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Europe/London" + ], + "isd": "+44" + }, + "Jordan": { + "code": "jo", + "currency": "JOD", + "currency_fraction": "Piastre[H]", + "currency_fraction_units": 100, + "currency_name": "Jordanian Dinar", + "currency_symbol": "\u062f.\u0627", + "number_format": "#,###.###", + "timezones": [ + "Asia/Amman" + ], + "isd": "+962" + }, + "Kazakhstan": { + "code": "kz", + "currency": "KZT", + "currency_fraction": "T\u00ef\u0131n", + "currency_fraction_units": 100, + "currency_name": "Tenge", + "currency_symbol": "\u20b8", + "number_format": "#,###.##", + "timezones": [ + "Asia/Almaty", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Oral", + "Asia/Qyzylorda" + ], + "isd": "+7" + }, + "Kenya": { + "code": "ke", + "currency": "KES", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Kenyan Shilling", + "currency_symbol": "Sh", + "number_format": "#,###.##", + "timezones": [ + "Africa/Nairobi" + ], + "isd": "+254" + }, + "Kiribati": { + "code": "ki", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Enderbury", + "Pacific/Kiritimati", + "Pacific/Tarawa" + ], + "isd": "+686" + }, + "Korea, Democratic Peoples Republic of": { + "code": "kp", + "currency": "KPW", + "currency_name": "North Korean Won", + "number_format": "#,###.##", + "isd": "+850" + }, + "Korea, Republic of": { + "code": "kr", + "currency": "KRW", + "currency_name": "Won", + "number_format": "#,###", + "isd": "+82" + }, + "Kuwait": { + "code": "kw", + "currency": "KWD", + "currency_fraction": "Fils", + "currency_fraction_units": 1000, + "currency_name": "Kuwaiti Dinar", + "currency_symbol": "\u062f.\u0643", + "number_format": "#,###.###", + "timezones": [ + "Asia/Kuwait" + ], + "isd": "+965" + }, + "Kyrgyzstan": { + "code": "kg", + "currency": "KGS", + "currency_fraction": "Tyiyn", + "currency_fraction_units": 100, + "currency_name": "Som", + "currency_symbol": "\u043b\u0432", + "number_format": "#,###.##", + "timezones": [ + "Asia/Bishkek" + ], + "isd": "+996" + }, + "Lao Peoples Democratic Republic": { + "code": "la", + "currency": "LAK", + "currency_name": "Kip", + "number_format": "#,###.##", + "timezones": [ + "Asia/Vientiane" + ], + "isd": "+856" + }, + "Latvia": { + "code": "lv", + "currency": "LVL", + "currency_fraction": "Sant\u012bms", + "currency_fraction_units": 100, + "currency_name": "Latvian Lats", + "currency_symbol": "Ls", + "number_format": "#,###.##", + "timezones": [ + "Europe/Riga" + ], + "isd": "+371" + }, + "Lebanon": { + "code": "lb", + "currency": "LBP", + "currency_fraction": "Piastre", + "currency_fraction_units": 100, + "currency_name": "Lebanese Pound", + "currency_symbol": "\u0644.\u0644", + "number_format": "#,###.##", + "timezones": [ + "Asia/Beirut" + ], + "isd": "+961" + }, + "Lesotho": { + "code": "ls", + "currency": "LSL", + "currency_fraction": "Sente", + "currency_fraction_units": 100, + "currency_name": "Loti", + "currency_symbol": "L", + "number_format": "#,###.##", + "timezones": [ + "Africa/Maseru" + ], + "isd": "+266" + }, + "Liberia": { + "code": "lr", + "currency": "LRD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Liberian Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Africa/Monrovia" + ], + "isd": "+231" + }, + "Libya": { + "code": "ly", + "currency": "LYD", + "currency_fraction": "Dirham", + "currency_fraction_units": 1000, + "currency_name": "Libyan Dinar", + "currency_symbol": "\u0644.\u062f", + "number_format": "#,###.###", + "timezones": [ + "Africa/Tripoli" + ], + "isd": "+218" + }, + "Liechtenstein": { + "code": "li", + "currency_fraction": "Rappen", + "currency_fraction_units": 100, + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Europe/Vaduz" + ], + "isd": "+423" + }, + "Lithuania": { + "code": "lt", + "currency": "LTL", + "currency_fraction": "Centas", + "currency_fraction_units": 100, + "currency_name": "Lithuanian Litas", + "currency_symbol": "Lt", + "date_format": "yyyy-mm-dd", + "number_format": "# ###,##", + "timezones": [ + "Europe/Vilnius" + ], + "isd": "+370" + }, + "Luxembourg": { + "code": "lu", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Luxembourg" + ], + "isd": "+352" + }, + "Macao": { + "code": "mo", + "currency": "MOP", + "currency_name": "Pataca", + "number_format": "#,###.##", + "isd": "+853" + }, + "Macedonia": { + "code": "mk", + "currency": "MKD", + "currency_fraction": "Deni", + "currency_fraction_units": 100, + "currency_name": "Denar", + "currency_symbol": "\u0434\u0435\u043d", + "number_format": "#,###.##", + "isd": "+389" + }, + "Madagascar": { + "code": "mg", + "currency_fraction": "Iraimbilanja", + "currency_fraction_units": 5, + "currency_symbol": "Ar", + "number_format": "#,###.##", + "timezones": [ + "Indian/Antananarivo" + ], + "isd": "+261" + }, + "Malawi": { + "code": "mw", + "currency": "MWK", + "currency_fraction": "Tambala", + "currency_fraction_units": 100, + "currency_name": "Kwacha", + "currency_symbol": "MK", + "number_format": "#,###.##", + "timezones": [ + "Africa/Blantyre" + ], + "isd": "+265" + }, + "Malaysia": { + "code": "my", + "currency": "MYR", + "currency_fraction": "Sen", + "currency_fraction_units": 100, + "currency_name": "Malaysian Ringgit", + "currency_symbol": "RM", + "number_format": "#,###.##", + "timezones": [ + "Asia/Kuala_Lumpur", + "Asia/Kuching" + ], + "isd": "+60" + }, + "Maldives": { + "code": "mv", + "currency": "MVR", + "currency_fraction": "Laari", + "currency_fraction_units": 100, + "currency_name": "Rufiyaa", + "currency_symbol": ".\u0783", + "number_format": "#,###.##", + "timezones": [ + "Indian/Maldives" + ], + "isd": "+960" + }, + "Mali": { + "code": "ml", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Bamako" + ], + "isd": "+223" + }, + "Malta": { + "code": "mt", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "date_format": "dd/mm/yyyy", + "timezones": [ + "Europe/Malta" + ], + "isd": "+356" + }, + "Marshall Islands": { + "code": "mh", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Kwajalein", + "Pacific/Majuro" + ], + "isd": "+692" + }, + "Martinique": { + "code": "mq", + "number_format": "#,###.##", + "timezones": [ + "America/Martinique" + ], + "isd": "+596" + }, + "Mauritania": { + "code": "mr", + "currency": "MRO", + "currency_fraction": "Khoums", + "currency_fraction_units": 5, + "currency_name": "Ouguiya", + "currency_symbol": "UM", + "number_format": "#,###.##", + "timezones": [ + "Africa/Nouakchott" + ], + "isd": "+222" + }, + "Mauritius": { + "code": "mu", + "currency": "MUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Mauritius Rupee", + "currency_symbol": "\u20a8", + "number_format": "#,###", + "timezones": [ + "Indian/Mauritius" + ], + "isd": "+230" + }, + "Mayotte": { + "code": "yt", + "number_format": "#,###.##", + "timezones": [ + "Indian/Mayotte" + ], + "isd": "+262" + }, + "Mexico": { + "code": "mx", + "currency": "MXN", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Mexican Peso", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Bahia_Banderas", + "America/Cancun", + "America/Chihuahua", + "America/Hermosillo", + "America/Matamoros", + "America/Mazatlan", + "America/Merida", + "America/Mexico_City", + "America/Monterrey", + "America/Ojinaga", + "America/Santa_Isabel", + "America/Tijuana" + ], + "isd": "+52" + }, + "Micronesia, Federated States of": { + "code": "fm", + "number_format": "#,###.##", + "isd": "+691" + }, + "Moldova, Republic of": { + "code": "md", + "currency": "MDL", + "currency_name": "Moldovan Leu", + "number_format": "#,###.##", + "isd": "+373" + }, + "Monaco": { + "code": "mc", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Monaco" + ], + "isd": "+377" + }, + "Mongolia": { + "code": "mn", + "currency": "MNT", + "currency_fraction": "M\u00f6ng\u00f6", + "currency_fraction_units": 100, + "currency_name": "Tugrik", + "currency_symbol": "\u20ae", + "date_format": "yyyy-mm-dd", + "number_format": "#,###.##", + "timezones": [ + "Asia/Choibalsan", + "Asia/Hovd", + "Asia/Ulaanbaatar" + ], + "isd": "+976" + }, + "Montenegro": { + "code": "me", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Belgrade" + ], + "isd": "+382" + }, + "Montserrat": { + "code": "ms", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/Montserrat" + ], + "isd": "+1664" + }, + "Morocco": { + "code": "ma", + "currency": "MAD", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Moroccan Dirham", + "currency_symbol": "\u062f.\u0645.", + "number_format": "#,###.##", + "timezones": [ + "Africa/Casablanca" + ], + "isd": "+212" + }, + "Mozambique": { + "code": "mz", + "currency": "MZN", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_symbol": "MZN", + "number_format": "#,###.##", + "timezones": [ + "Africa/Maputo" + ], + "isd": "+258" + }, + "Myanmar": { + "code": "mm", + "currency": "MMK", + "currency_name": "Kyat", + "number_format": "#,###.##", + "timezones": [ + "Asia/Rangoon" + ], + "isd": "+95" + }, + "Namibia": { + "code": "na", + "currency": "NAD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Namibia Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Africa/Windhoek" + ], + "isd": "+264" + }, + "Nauru": { + "code": "nr", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Nauru" + ], + "isd": "+674" + }, + "Nepal": { + "code": "np", + "currency": "NPR", + "currency_fraction": "Paisa", + "currency_fraction_units": 100, + "currency_name": "Nepalese Rupee", + "currency_symbol": "\u20a8", + "number_format": "#,##,###.##", + "timezones": [ + "Asia/Kathmandu" + ], + "isd": "+977" + }, + "Netherlands": { + "code": "nl", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Amsterdam" + ], + "isd": "+31" + }, + "New Caledonia": { + "code": "nc", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Noumea" + ], + "isd": "+687" + }, + "New Zealand": { + "code": "nz", + "currency": "NZD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "New Zealand Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Auckland", + "Pacific/Chatham" + ], + "isd": "+64" + }, + "Nicaragua": { + "code": "ni", + "currency": "NIO", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Cordoba Oro", + "currency_symbol": "C$", + "number_format": "#,###.##", + "timezones": [ + "America/Managua" + ], + "isd": "+505" + }, + "Niger": { + "code": "ne", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Niamey" + ], + "isd": "+227" + }, + "Nigeria": { + "code": "ng", + "currency": "NGN", + "currency_fraction": "Kobo", + "currency_fraction_units": 100, + "currency_name": "Naira", + "currency_symbol": "\u20a6", + "number_format": "#,###.##", + "timezones": [ + "Africa/Lagos" + ], + "isd": "+234" + }, + "Niue": { + "code": "nu", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Niue" + ], + "isd": "+683" + }, + "Norfolk Island": { + "code": "nf", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Norfolk" + ], + "isd": "+672" + }, + "Northern Mariana Islands": { + "code": "mp", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Saipan" + ], + "isd": "+1670" + }, + "Norway": { + "code": "no", + "currency": "NOK", + "currency_fraction": "\u00d8re", + "currency_fraction_units": 100, + "currency_name": "Norwegian Krone", + "currency_symbol": "kr", + "number_format": "#.###,##", + "timezones": [ + "Europe/Oslo" + ], + "isd": "+47" + }, + "Oman": { + "code": "om", + "currency": "OMR", + "currency_fraction": "Baisa", + "currency_fraction_units": 1000, + "currency_name": "Rial Omani", + "currency_symbol": "\u0631.\u0639.", + "number_format": "#,###.###", + "timezones": [ + "Asia/Muscat" + ], + "isd": "+968" + }, + "Pakistan": { + "code": "pk", + "currency": "PKR", + "currency_fraction": "Paisa", + "currency_fraction_units": 100, + "currency_name": "Pakistan Rupee", + "currency_symbol": "\u20a8", + "number_format": "#,###.##", + "timezones": [ + "Asia/Karachi" + ], + "isd": "+92" + }, + "Palau": { + "code": "pw", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "date_format": "mm-dd-yyyy", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Palau" + ], + "isd": "+680" + }, + "Palestinian Territory, Occupied": { + "code": "ps", + "currency": "ILS", + "currency_fraction": "Agora", + "currency_fraction_units": 100, + "currency_name": "New Israeli Sheqel", + "currency_symbol": "\u20aa", + "number_format": "#,###.##", + "isd": "+970", + "timezones": [ + "Asia/Hebron", + "Asia/Jerusalem" + ] + }, + "Panama": { + "code": "pa", + "currency_fraction": "Cent\u00e9simo", + "currency_fraction_units": 100, + "currency_symbol": "B/.", + "number_format": "#,###.##", + "timezones": [ + "America/Panama" + ], + "isd": "+507" + }, + "Papua New Guinea": { + "code": "pg", + "currency": "PGK", + "currency_fraction": "Toea", + "currency_fraction_units": 100, + "currency_name": "Kina", + "currency_symbol": "K", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Port_Moresby" + ], + "isd": "+675" + }, + "Paraguay": { + "code": "py", + "currency": "PYG", + "currency_fraction": "C\u00e9ntimo", + "currency_fraction_units": 100, + "currency_name": "Guarani", + "currency_symbol": "\u20b2", + "number_format": "#,###.##", + "timezones": [ + "America/Asuncion" + ], + "isd": "+595" + }, + "Peru": { + "code": "pe", + "currency": "PEN", + "currency_fraction": "C\u00e9ntimo", + "currency_fraction_units": 100, + "currency_name": "Nuevo Sol", + "currency_symbol": "S/.", + "number_format": "#,###.##", + "timezones": [ + "America/Lima" + ], + "isd": "+51" + }, + "Philippines": { + "code": "ph", + "currency": "PHP", + "currency_fraction": "Centavo", + "currency_fraction_units": 100, + "currency_name": "Philippine Peso", + "currency_symbol": "\u20b1", + "date_format": "mm-dd-yyyy", + "number_format": "#,###.##", + "timezones": [ + "Asia/Manila" + ], + "isd": "+63" + }, + "Pitcairn": { + "code": "pn", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Pitcairn" + ], + "isd": "+64" + }, + "Poland": { + "code": "pl", + "currency": "PLN", + "currency_fraction": "Grosz", + "currency_fraction_units": 100, + "currency_symbol": "z\u0142", + "number_format": "#.###,##", + "timezones": [ + "Europe/Warsaw" + ], + "isd": "+48" + }, + "Portugal": { + "code": "pt", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Atlantic/Azores", + "Atlantic/Madeira", + "Europe/Lisbon" + ], + "isd": "+351" + }, + "Puerto Rico": { + "code": "pr", + "number_format": "#,###.##", + "timezones": [ + "America/Puerto_Rico" + ], + "isd": "+1939" + }, + "Qatar": { + "code": "qa", + "currency": "QAR", + "currency_fraction": "Dirham", + "currency_fraction_units": 100, + "currency_name": "Qatari Rial", + "currency_symbol": "\u0631.\u0642", + "number_format": "#,###.##", + "timezones": [ + "Asia/Qatar" + ], + "isd": "+974" + }, + "Romania": { + "code": "ro", + "currency": "RON", + "currency_fraction": "Bani", + "currency_fraction_units": 100, + "currency_name": "Romanian New Leu", + "currency_symbol": "lei", + "number_format": "#,###.##", + "timezones": [ + "Europe/Bucharest" + ], + "isd": "+40" + }, + "Russian Federation": { + "code": "ru", + "currency": "RUB", + "currency_name": "Russian Ruble", + "number_format": "#.###,##", + "isd": "+7" + }, + "Rwanda": { + "code": "rw", + "currency": "RWF", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_name": "Rwanda Franc", + "currency_symbol": "Fr", + "number_format": "#,###.##", + "timezones": [ + "Africa/Kigali" + ], + "isd": "+250" + }, + "R\u00e9union": { + "code": "re", + "number_format": "#,###.##", + "isd": "+262" + }, + "Saint Barth\u00e9lemy": { + "code": "bl", + "number_format": "#,###.##", + "isd": "+590" + }, + "Saint Helena, Ascension and Tristan da Cunha": { + "code": "sh", + "currency": "SHP", + "currency_name": "Saint Helena Pound", + "number_format": "#,###.##", + "isd": "+290" + }, + "Saint Kitts and Nevis": { + "code": "kn", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/St_Kitts" + ], + "isd": "+1869" + }, + "Saint Lucia": { + "code": "lc", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/St_Lucia" + ], + "isd": "+1758" + }, + "Saint Martin (French part)": { + "code": "mf", + "number_format": "#,###.##", + "isd": "+590" + }, + "Saint Pierre and Miquelon": { + "code": "pm", + "number_format": "#,###.##", + "isd": "+508" + }, + "Saint Vincent and the Grenadines": { + "code": "vc", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "currency_name": "Eastern Carribean Dollar", + "currency": "XCD", + "number_format": "#,###.##", + "timezones": [ + "America/St_Vincent" + ], + "isd": "+1784" + }, + "Samoa": { + "code": "ws", + "currency": "WST", + "currency_fraction": "Sene", + "currency_fraction_units": 100, + "currency_name": "Tala", + "currency_symbol": "T", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Apia" + ], + "isd": "+685" + }, + "San Marino": { + "code": "sm", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Rome" + ], + "isd": "+378" + }, + "Sao Tome and Principe": { + "code": "st", + "currency": "STD", + "currency_name": "Dobra", + "number_format": "#,###.##", + "isd": "+239" + }, + "Saudi Arabia": { + "code": "sa", + "currency": "SAR", + "currency_fraction": "Halala", + "currency_fraction_units": 100, + "currency_name": "Saudi Riyal", + "currency_symbol": "\u0631.\u0633", + "number_format": "#,###.##", + "timezones": [ + "Asia/Riyadh" + ], + "isd": "+966" + }, + "Senegal": { + "code": "sn", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Dakar" + ], + "isd": "+221" + }, + "Serbia": { + "code": "rs", + "currency": "RSD", + "currency_fraction": "Para", + "currency_fraction_units": 100, + "currency_name": "Serbian Dinar", + "currency_symbol": "\u0434\u0438\u043d.", + "number_format": "#,###.##", + "timezones": [ + "Europe/Belgrade" + ], + "isd": "+381" + }, + "Seychelles": { + "code": "sc", + "currency": "SCR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Seychelles Rupee", + "currency_symbol": "\u20a8", + "number_format": "#,###.##", + "timezones": [ + "Indian/Mahe" + ], + "isd": "+248" + }, + "Sierra Leone": { + "code": "sl", + "currency": "SLL", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Leone", + "currency_symbol": "Le", + "number_format": "#,###.##", + "timezones": [ + "Africa/Freetown" + ], + "isd": "+232" + }, + "Singapore": { + "code": "sg", + "currency": "SGD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Singapore Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Asia/Singapore" + ], + "isd": "+65" + }, + "Sint Maarten (Dutch part)": { + "code": "sx", + "number_format": "#,###.##" + }, + "Slovakia": { + "code": "sk", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Bratislava" + ], + "isd": "+421" + }, + "Slovenia": { + "code": "si", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Europe/Belgrade" + ], + "isd": "+386" + }, + "Solomon Islands": { + "code": "sb", + "currency": "SBD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Solomon Islands Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Guadalcanal" + ], + "isd": "+677" + }, + "Somalia": { + "code": "so", + "currency": "SOS", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Somali Shilling", + "currency_symbol": "Sh", + "number_format": "#,###.##", + "timezones": [ + "Africa/Mogadishu" + ], + "isd": "+252" + }, + "South Africa": { + "code": "za", + "currency": "ZAR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Rand", + "currency_symbol": "R", + "date_format": "yyyy-mm-dd", + "number_format": "# ###.##", + "timezones": [ + "Africa/Johannesburg" + ], + "isd": "+27" + }, + "South Georgia and the South Sandwich Islands": { + "code": "gs", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "isd": "+500" + }, + "South Sudan": { + "code": "ss", + "currency_fraction": "Piastre", + "currency_fraction_units": 100, + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Africa/Juba" + ], + "isd": "+211" + }, + "Spain": { + "code": "es", + "currency": "EUR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_symbol": "\u20ac", + "number_format": "#,###.##", + "timezones": [ + "Africa/Ceuta", + "Atlantic/Canary", + "Europe/Madrid" + ], + "isd": "+34" + }, + "Sri Lanka": { + "code": "lk", + "currency": "LKR", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Sri Lanka Rupee", + "currency_symbol": "Rs", + "number_format": "#,###.##", + "timezones": [ + "Asia/Colombo" + ], + "isd": "+94" + }, + "Sudan": { + "code": "sd", + "currency": "SDG", + "currency_fraction": "Piastre", + "currency_fraction_units": 100, + "currency_name": "Sudanese Pound", + "currency_symbol": "\u062c.\u0633.", + "number_format": "#,###.##", + "timezones": [ + "Africa/Khartoum" + ], + "isd": "+249" +}, + "Suriname": { + "code": "sr", + "currency": "SRD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Paramaribo" + ], + "isd": "+597" + }, + "Svalbard and Jan Mayen": { + "code": "sj", + "number_format": "#,###.##", + "isd": "+47" + }, + "Swaziland": { + "code": "sz", + "currency": "SZL", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Lilangeni", + "currency_symbol": "L", + "number_format": "#, ###.##", + "timezones": [ + "Africa/Mbabane" + ], + "isd": "+268" + }, + "Sweden": { + "code": "se", + "currency": "SEK", + "currency_fraction": "\u00d6re", + "currency_fraction_units": 100, + "currency_name": "Swedish Krona", + "currency_symbol": "kr", + "number_format": "#.###,##", + "timezones": [ + "Europe/Stockholm" + ], + "isd": "+46" + }, + "Switzerland": { + "code": "ch", + "currency": "CHF", + "currency_fraction": "Rappen[K]", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.05, + "currency_name": "Swiss Franc", + "currency_symbol": "Fr", + "number_format": "#'###.##", + "timezones": [ + "Europe/Zurich" + ], + "isd": "+41" + }, + "Syria": { + "code": "sy", + "currency": "SYP", + "currency_name": "Syrian Pound", + "number_format": "#,###.##", + "isd": "+963" + }, + "Taiwan": { + "code": "tw", + "currency": "TWD", + "date_format": "yyyy-mm-dd", + "number_format": "#,###.##", + "isd": "+886" + }, + "Tajikistan": { + "code": "tj", + "currency_fraction": "Diram", + "currency_fraction_units": 100, + "currency_symbol": "\u0405\u041c", + "number_format": "#,###.##", + "timezones": [ + "Asia/Dushanbe" + ], + "isd": "+992" + }, + "Tanzania": { + "code": "tz", + "currency": "TZS", + "currency_name": "Tanzanian Shilling", + "number_format": "#,###.##", + "timezones": [ + "Africa/Dar_es_Salaam" + ], + "isd": "+255" + }, + "Thailand": { + "code": "th", + "currency": "THB", + "currency_fraction": "Satang", + "currency_fraction_units": 100, + "currency_name": "Baht", + "currency_symbol": "\u0e3f", + "number_format": "#,###.##", + "timezones": [ + "Asia/Bangkok" + ], + "isd": "+66" + }, + "Timor-Leste": { + "code": "tl", + "number_format": "#,###.##", + "isd": "+670" + }, + "Togo": { + "code": "tg", + "currency": "XOF", + "currency_name": "West African CFA Franc", + "currency_symbol": "CFA", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "number_format": "#,###.##", + "timezones": [ + "Africa/Lome" + ], + "isd": "+228" + }, + "Tokelau": { + "code": "tk", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Fakaofo" + ], + "isd": "+690" + }, + "Tonga": { + "code": "to", + "currency": "TOP", + "currency_fraction": "Seniti[L]", + "currency_fraction_units": 100, + "currency_name": "Pa'anga", + "currency_symbol": "T$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Tongatapu" + ], + "isd": "+676" + }, + "Trinidad and Tobago": { + "code": "tt", + "currency": "TTD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Trinidad and Tobago Dollar", + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "America/Port_of_Spain" + ], + "isd": "+1868" + }, + "Tunisia": { + "code": "tn", + "currency": "TND", + "currency_fraction": "Millime", + "currency_fraction_units": 1000, + "currency_name": "Tunisian Dinar", + "currency_symbol": "\u062f.\u062a", + "number_format": "#,###.###", + "timezones": [ + "Africa/Tunis" + ], + "isd": "+216" + }, + "Turkey": { + "code": "tr", + "currency": "TRY", + "currency_fraction": "Kuru\u015f", + "currency_fraction_units": 100, + "currency_symbol": "\u20ba", + "number_format": "#.###,##", + "timezones": [ + "Europe/Istanbul" + ], + "isd": "+90" + }, + "Turkmenistan": { + "code": "tm", + "currency": "TMM", + "currency_fraction": "Tennesi", + "currency_fraction_units": 100, + "currency_name": "Manat", + "currency_symbol": "m", + "number_format": "#,###.##", + "timezones": [ + "Asia/Ashgabat" + ], + "isd": "+993" + }, + "Turks and Caicos Islands": { + "code": "tc", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "isd": "+1649" + }, + "Tuvalu": { + "code": "tv", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_symbol": "$", + "number_format": "#,###.##", + "timezones": [ + "Pacific/Funafuti" + ], + "isd": "+688" + }, + "Uganda": { + "code": "ug", + "currency": "UGX", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Uganda Shilling", + "currency_symbol": "Sh", + "number_format": "#,###.##", + "timezones": [ + "Africa/Kampala" + ], + "isd": "+256" + }, + "Ukraine": { + "code": "ua", + "currency": "UAH", + "currency_fraction": "Kopiyka", + "currency_fraction_units": 100, + "currency_name": "Ukrainian Hryvnia", + "currency_symbol": "\u20b4", + "number_format": "#,###.##", + "timezones": [ + "Europe/Kiev", + "Europe/Simferopol", + "Europe/Uzhgorod", + "Europe/Zaporozhye" + ], + "isd": "+380" + }, + "United Arab Emirates": { + "code": "ae", + "currency": "AED", + "currency_fraction": "Fils", + "currency_fraction_units": 100, + "currency_name": "UAE Dirham", + "currency_symbol": "\u062f.\u0625", + "number_format": "#,###.##", + "timezones": [ + "Asia/Dubai" + ], + "isd": "+971" + }, + "United Kingdom": { + "code": "gb", + "currency": "GBP", + "currency_fraction": "Penny", + "currency_fraction_units": 100, + "currency_name": "Pound Sterling", + "currency_symbol": "\u00a3", + "number_format": "#,###.##", + "timezones": [ + "Europe/London" + ], + "isd": "+44" + }, + "United States": { + "code": "us", + "currency": "USD", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_name": "US Dollar", + "currency_symbol": "$", + "date_format": "mm-dd-yyyy", + "number_format": "#,###.##", + "timezones": [ + "America/Adak", + "America/Anchorage", + "America/Boise", + "America/Chicago", + "America/Denver", + "America/Detroit", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/Los_Angeles", + "America/Menominee", + "America/Metlakatla", + "America/New_York", + "America/Nome", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Phoenix", + "America/Denver", + "America/Sitka", + "America/Yakutat", + "Pacific/Honolulu" + ], + "isd": "+1" + }, + "United States Minor Outlying Islands": { + "code": "um", + "number_format": "#,###.##" + }, + "Uruguay": { + "code": "uy", + "currency": "UYU", + "currency_fraction": "Cent\u00e9simo", + "currency_fraction_units": 100, + "currency_name": "Peso Uruguayo", + "currency_symbol": "$", + "number_format": "#.###,##", + "timezones": [ + "America/Montevideo" + ], + "isd": "+598" + }, + "Uzbekistan": { + "code": "uz", + "currency": "UZS", + "currency_fraction": "Tiyin", + "currency_fraction_units": 100, + "currency_name": "Uzbekistan Sum", + "currency_symbol": "\u043b\u0432", + "number_format": "#,###.##", + "timezones": [ + "Asia/Samarkand", + "Asia/Tashkent" + ], + "isd": "+998" + }, + "Vanuatu": { + "code": "vu", + "currency": "VUV", + "currency_fraction": "None", + "currency_fraction_units": 0, + "currency_name": "Vatu", + "currency_symbol": "Vt", + "number_format": "#,###", + "timezones": [ + "Pacific/Efate" + ], + "isd": "+678" + }, + "Venezuela, Bolivarian Republic of": { + "code": "ve", + "number_format": "#.###,##", + "currency": "VEF", + "currency_symbol": "Bs.", + "currency_fraction": "Centimos", + "currency_fraction_units": 100, + "isd": "+58" + }, + "Vietnam": { + "code": "vn", + "currency": "VND", + "currency_name": "Dong", + "number_format": "#.###", + "isd": "+84" + }, + "Virgin Islands, British": { + "code": "vg", + "number_format": "#,###.##", + "isd": "+1284" + }, + "Virgin Islands, U.S.": { + "code": "vi", + "number_format": "#,###.##", + "isd": "+1340" + }, + "Wallis and Futuna": { + "code": "wf", + "currency_fraction": "Centime", + "currency_fraction_units": 100, + "currency_symbol": "Fr", + "number_format": "#,###.##", + "isd": "+681" + }, + "Western Sahara": { + "code": "eh", + "number_format": "#,###.##", + "timezones": [ + "Africa/El_Aaiun" + ] + }, + "Yemen": { + "code": "ye", + "currency": "YER", + "currency_fraction": "Fils", + "currency_fraction_units": 100, + "smallest_currency_fraction_value": 0.01, + "currency_name": "Yemeni Rial", + "currency_symbol": "\ufdfc", + "number_format": "#,###.##", + "timezones": [ + "Asia/Aden" + ], + "isd": "+967" + }, + "Zambia": { + "code": "zm", + "currency": "ZMW", + "currency_fraction": "Ngwee", + "currency_fraction_units": 100, + "currency_name": "Zambian Kwacha", + "currency_symbol": "ZK", + "number_format": "#,###.##", + "timezones": [ + "Africa/Lusaka" + ], + "isd": "+260" + }, + "Zimbabwe": { + "code": "zw", + "currency": "ZWL", + "currency_fraction": "Cent", + "currency_fraction_units": 100, + "currency_name": "Zimbabwe Dollar", + "currency_symbol": "ZWL$", + "number_format": "# ###.##", + "timezones": [ + "Africa/Harare" + ], + "isd": "+263" + }, + "\u00c5land Islands": { + "code": "ax", + "number_format": "#,###.##", + "isd": "+358" + } +} diff --git a/xhiveframework/geo/country_info.py b/xhiveframework/geo/country_info.py new file mode 100644 index 0000000..8c0593c --- /dev/null +++ b/xhiveframework/geo/country_info.py @@ -0,0 +1,80 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +# all country info +import os +from functools import lru_cache + +import xhiveframework +from xhiveframework.utils.momentjs import get_all_timezones + + +def get_country_info(country=None): + data = get_all() + data = xhiveframework._dict(data.get(country, {})) + if "date_format" not in data: + data.date_format = "dd-mm-yyyy" + if "time_format" not in data: + data.time_format = "HH:mm:ss" + + return data + + +def get_all(): + with open(os.path.join(os.path.dirname(__file__), "country_info.json")) as local_info: + all_data = json.loads(local_info.read()) + return all_data + + +@xhiveframework.whitelist(allow_guest=True) +def get_country_timezone_info(): + return _get_country_timezone_info() + + +@lru_cache(maxsize=2) +def _get_country_timezone_info(): + return {"country_info": get_all(), "all_timezones": get_all_timezones()} + + +def get_translated_dict(): + from babel.dates import Locale, get_timezone, get_timezone_name + + translated_dict = {} + locale = Locale.parse(xhiveframework.local.lang, sep="-") + + # timezones + for tz in get_all_timezones(): + timezone_name = get_timezone_name(get_timezone(tz), locale=locale, width="short") + if timezone_name: + translated_dict[tz] = timezone_name + " - " + tz + + # country names && currencies + for country, info in get_all().items(): + country_name = locale.territories.get((info.get("code") or "").upper()) + if country_name: + translated_dict[country] = country_name + + currency = info.get("currency") + currency_name = locale.currencies.get(currency) + if currency_name: + translated_dict[currency] = currency_name + + return translated_dict + + +def update(): + with open(os.path.join(os.path.dirname(__file__), "currency_info.json")) as nformats: + nformats = json.loads(nformats.read()) + + all_data = get_all() + + for country in all_data: + data = all_data[country] + data["number_format"] = nformats.get(data.get("currency", "default"), nformats.get("default"))[ + "display" + ] + + with open(os.path.join(os.path.dirname(__file__), "country_info.json"), "w") as local_info: + local_info.write(json.dumps(all_data, indent=1)) diff --git a/xhiveframework/geo/doctype/__init__.py b/xhiveframework/geo/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/geo/doctype/country/README.md b/xhiveframework/geo/doctype/country/README.md new file mode 100644 index 0000000..0e3f46c --- /dev/null +++ b/xhiveframework/geo/doctype/country/README.md @@ -0,0 +1 @@ +Country Master. \ No newline at end of file diff --git a/xhiveframework/geo/doctype/country/__init__.py b/xhiveframework/geo/doctype/country/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/geo/doctype/country/country.js b/xhiveframework/geo/doctype/country/country.js new file mode 100644 index 0000000..ed5ff88 --- /dev/null +++ b/xhiveframework/geo/doctype/country/country.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Country", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/geo/doctype/country/country.json b/xhiveframework/geo/doctype/country/country.json new file mode 100644 index 0000000..8f62458 --- /dev/null +++ b/xhiveframework/geo/doctype/country/country.json @@ -0,0 +1,89 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:country_name", + "creation": "2013-01-19 10:23:30", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "country_name", + "date_format", + "time_format", + "time_zones", + "code" + ], + "fields": [ + { + "fieldname": "country_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Country Name", + "oldfieldname": "country_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "date_format", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Date Format" + }, + { + "default": "HH:mm:ss", + "fieldname": "time_format", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Time format" + }, + { + "fieldname": "time_zones", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Time Zones" + }, + { + "fieldname": "code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Code" + } + ], + "icon": "fa fa-globe", + "idx": 1, + "links": [], + "modified": "2022-08-05 18:33:27.880783", + "modified_by": "Administrator", + "module": "Geo", + "name": "Country", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All" + } + ], + "quick_entry": 1, + "sort_field": "country_name", + "sort_order": "ASC", + "states": [], + "track_changes": 1, + "translated_doctype": 1 +} diff --git a/xhiveframework/geo/doctype/country/country.py b/xhiveframework/geo/doctype/country/country.py new file mode 100644 index 0000000..1037682 --- /dev/null +++ b/xhiveframework/geo/doctype/country/country.py @@ -0,0 +1,77 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document, bulk_insert + + +class Country(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + code: DF.Data | None + country_name: DF.Data + date_format: DF.Data | None + time_format: DF.Data | None + time_zones: DF.Text | None + # end: auto-generated types + # NOTE: During installation country docs are bulk inserted. + pass + + +def import_country_and_currency(): + from xhiveframework.geo.doctype.currency.currency import enable_default_currencies + + countries, currencies = get_countries_and_currencies() + + bulk_insert("Country", countries, ignore_duplicates=True) + bulk_insert("Currency", currencies, ignore_duplicates=True) + + enable_default_currencies() + + +def get_countries_and_currencies(): + from xhiveframework.geo.country_info import get_all as get_geo_data + + data = get_geo_data() + + countries = [] + currencies = [] + + added_currencies = set() + + for name, country in data.items(): + country = xhiveframework._dict(country) + countries.append( + xhiveframework.get_doc( + doctype="Country", + name=name, + country_name=name, + code=country.code, + date_format=country.date_format or "dd-mm-yyyy", + time_format=country.time_format or "HH:mm:ss", + time_zones="\n".join(country.timezones or []), + ) + ) + if country.currency and country.currency not in added_currencies: + added_currencies.add(country.currency) + + currencies.append( + xhiveframework.get_doc( + doctype="Currency", + name=country.currency, + currency_name=country.currency, + fraction=country.currency_fraction, + symbol=country.currency_symbol, + fraction_units=country.currency_fraction_units, + smallest_currency_fraction_value=country.smallest_currency_fraction_value, + number_format=country.number_format, + ) + ) + + return countries, currencies diff --git a/xhiveframework/geo/doctype/country/test_country.py b/xhiveframework/geo/doctype/country/test_country.py new file mode 100644 index 0000000..4e10544 --- /dev/null +++ b/xhiveframework/geo/doctype/country/test_country.py @@ -0,0 +1,53 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.geo.doctype.country.country import ( + get_countries_and_currencies, + import_country_and_currency, +) +from xhiveframework.geo.doctype.currency.currency import enable_default_currencies +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_records = xhiveframework.get_test_records("Country") + + +def get_table_snapshot(doctype): + data = xhiveframework.db.sql(f"select * from `tab{doctype}` order by name", as_dict=True) + + inconsequential_keys = ["modified", "creation"] + for row in data: + for key in inconsequential_keys: + row.pop(key, None) + return data + + +class TestCountry(XhiveFrameworkTestCase): + def test_bulk_insert_correctness(self): + def clear_tables(): + xhiveframework.db.delete("Currency") + xhiveframework.db.delete("Country") + + # Clear data + clear_tables() + + # Reimport and verify same results + import_country_and_currency() + + countries_before = get_table_snapshot("Country") + currencies_before = get_table_snapshot("Currency") + + clear_tables() + + countries, currencies = get_countries_and_currencies() + for country in countries: + country.db_insert(ignore_if_duplicate=True) + for currency in currencies: + currency.db_insert(ignore_if_duplicate=True) + enable_default_currencies() + + countries_after = get_table_snapshot("Country") + currencies_after = get_table_snapshot("Currency") + + self.assertEqual(countries_before, countries_after) + self.assertEqual(currencies_before, currencies_after) diff --git a/xhiveframework/geo/doctype/country/test_records.json b/xhiveframework/geo/doctype/country/test_records.json new file mode 100644 index 0000000..5a7c8a5 --- /dev/null +++ b/xhiveframework/geo/doctype/country/test_records.json @@ -0,0 +1,6 @@ +[ + { + "country_name": "_Test Country", + "doctype": "Country" + } +] \ No newline at end of file diff --git a/xhiveframework/geo/doctype/currency/README.md b/xhiveframework/geo/doctype/currency/README.md new file mode 100644 index 0000000..3e1558e --- /dev/null +++ b/xhiveframework/geo/doctype/currency/README.md @@ -0,0 +1 @@ +Currency Master with details about abbreviation, symbol etc. \ No newline at end of file diff --git a/xhiveframework/geo/doctype/currency/__init__.py b/xhiveframework/geo/doctype/currency/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/geo/doctype/currency/currency.js b/xhiveframework/geo/doctype/currency/currency.js new file mode 100644 index 0000000..7023553 --- /dev/null +++ b/xhiveframework/geo/doctype/currency/currency.js @@ -0,0 +1,11 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// License: See license.txt + +xhiveframework.ui.form.on("Currency", { + refresh(frm) { + frm.set_intro(""); + if (!frm.doc.enabled) { + frm.set_intro(__("This Currency is disabled. Enable to use in transactions")); + } + }, +}); diff --git a/xhiveframework/geo/doctype/currency/currency.json b/xhiveframework/geo/doctype/currency/currency.json new file mode 100644 index 0000000..9a4df0f --- /dev/null +++ b/xhiveframework/geo/doctype/currency/currency.json @@ -0,0 +1,126 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:currency_name", + "creation": "2013-01-28 10:06:02", + "description": "Currency list stores the currency value, its symbol and fraction unit", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "currency_name", + "enabled", + "fraction", + "fraction_units", + "smallest_currency_fraction_value", + "symbol", + "symbol_on_right", + "number_format" + ], + "fields": [ + { + "fieldname": "currency_name", + "fieldtype": "Data", + "label": "Currency Name", + "oldfieldname": "currency_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enabled" + }, + { + "description": "Sub-currency. For e.g. \"Cent\"", + "fieldname": "fraction", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fraction" + }, + { + "description": "1 Currency = [?] Fraction\nFor e.g. 1 USD = 100 Cent", + "fieldname": "fraction_units", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Fraction Units" + }, + { + "description": "Smallest circulating fraction unit (coin). For e.g. 1 cent for USD and it should be entered as 0.01", + "fieldname": "smallest_currency_fraction_value", + "fieldtype": "Currency", + "label": "Smallest Currency Fraction Value", + "non_negative": 1 + }, + { + "description": "A symbol for this currency. For e.g. $", + "fieldname": "symbol", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Symbol" + }, + { + "description": "How should this currency be formatted? If not set, will use system defaults", + "fieldname": "number_format", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Number Format", + "options": "\n#,###.##\n#.###,##\n# ###.##\n# ###,##\n#'###.##\n#, ###.##\n#,##,###.##\n#,###.###\n#.###\n#,###" + }, + { + "default": "0", + "fieldname": "symbol_on_right", + "fieldtype": "Check", + "label": "Show Currency Symbol on Right Side" + } + ], + "icon": "fa fa-bitcoin", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-01-30 13:18:12.053557", + "modified_by": "Administrator", + "module": "Geo", + "name": "Currency", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Accounts Manager" + }, + { + "read": 1, + "role": "Accounts User" + }, + { + "read": 1, + "role": "Sales User" + }, + { + "read": 1, + "role": "Purchase User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/geo/doctype/currency/currency.py b/xhiveframework/geo/doctype/currency/currency.py new file mode 100644 index 0000000..5420ccf --- /dev/null +++ b/xhiveframework/geo/doctype/currency/currency.py @@ -0,0 +1,47 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + +DEFAULT_ENABLED_CURRENCIES = ("INR", "USD", "GBP", "EUR", "AED", "AUD", "JPY", "CNY", "CHF") + + +class Currency(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + currency_name: DF.Data + enabled: DF.Check + fraction: DF.Data | None + fraction_units: DF.Int + number_format: DF.Literal[ + "", + "#,###.##", + "#.###,##", + "# ###.##", + "# ###,##", + "#'###.##", + "#, ###.##", + "#,##,###.##", + "#,###.###", + "#.###", + "#,###", + ] + smallest_currency_fraction_value: DF.Currency + symbol: DF.Data | None + symbol_on_right: DF.Check + + # end: auto-generated types + # NOTE: During installation country docs are bulk inserted. + def validate(self): + xhiveframework.clear_cache() + + +def enable_default_currencies(): + xhiveframework.db.set_value("Currency", {"name": ("in", DEFAULT_ENABLED_CURRENCIES)}, "enabled", 1) diff --git a/xhiveframework/geo/doctype/currency/test_currency.py b/xhiveframework/geo/doctype/currency/test_currency.py new file mode 100644 index 0000000..15a23d7 --- /dev/null +++ b/xhiveframework/geo/doctype/currency/test_currency.py @@ -0,0 +1,13 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# pre loaded + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestUser(XhiveFrameworkTestCase): + def test_default_currency_on_setup(self): + usd = xhiveframework.get_doc("Currency", "USD") + self.assertDocumentEqual({"enabled": 1, "fraction": "Cent"}, usd) diff --git a/xhiveframework/geo/doctype/currency/test_records.json b/xhiveframework/geo/doctype/currency/test_records.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/xhiveframework/geo/doctype/currency/test_records.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/xhiveframework/geo/languages.json b/xhiveframework/geo/languages.json new file mode 100644 index 0000000..33092a2 --- /dev/null +++ b/xhiveframework/geo/languages.json @@ -0,0 +1,330 @@ +[ + { + "code": "af", + "name": "Afrikaans" + }, + { + "code": "am", + "name": "\u12a0\u121b\u122d\u129b" + }, + { + "code": "ar", + "name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629" + }, + { + "code": "bg", + "name": "B\u01celgarski" + }, + { + "code": "bn", + "name": "\u09ac\u09be\u0999\u09be\u09b2\u09bf" + }, + { + "code": "bo", + "name": "\u0f63\u0fb7\u0f0b\u0f66\u0f60\u0f72\u0f0b\u0f66\u0f90\u0f51\u0f0b" + }, + { + "code": "bs", + "name": "Bosanski" + }, + { + "code": "ca", + "name": "Catal\u00e0" + }, + { + "code": "cs", + "name": "\u010desky" + }, + { + "code": "da", + "name": "Dansk" + }, + { + "code": "da-DK", + "name": "Dansk (Danmark)" + }, + { + "code": "de", + "name": "Deutsch" + }, + { + "code": "el", + "name": "\u03b5\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac" + }, + { + "code": "en", + "name": "English" + }, + { + "code": "en-GB", + "name": "English (United Kingdom)" + }, + { + "code": "en-US", + "name": "English (United States)" + }, + { + "code": "es", + "name": "Espa\u00f1ol" + }, + { + "code": "es-AR", + "name": "Espa\u00f1ol (Argentina)" + }, + { + "code": "es-BO", + "name": "Espa\u00f1ol (Bolivia)" + }, + { + "code": "es-CL", + "name": "Espa\u00f1ol (Chile)" + }, + { + "code": "es-CO", + "name": "Espa\u00f1ol (Colombia)" + }, + { + "code": "es-DO", + "name": "Espa\u00f1ol (Rep\u00fablica Dominicana)" + }, + { + "code": "es-EC", + "name": "Espa\u00f1ol (Ecuador)" + }, + { + "code": "es-GT", + "name": "Espa\u00f1ol (Guatemala)" + }, + { + "code": "es-MX", + "name": "Espa\u00f1ol (M\u00e9xico)" + }, + { + "code": "es-NI", + "name": "Espa\u00f1ol (Nicaragua)" + }, + { + "code": "es-PE", + "name": "Espa\u00f1ol (Per\u00fa)" + }, + { + "code": "et", + "name": "Eesti" + }, + { + "code": "fa", + "name": "\u067e\u0627\u0631\u0633\u06cc" + }, + { + "code": "fi", + "name": "Suomi" + }, + { + "code": "fil", + "name": "Filipino" + }, + { + "code": "fr", + "name": "Fran\u00e7ais" + }, + { + "code": "fr-CA", + "name": "Fran\u00e7ais Canadien" + }, + { + "code": "gu", + "name": "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0" + }, + { + "code": "he", + "name": "\u05e2\u05d1\u05e8\u05d9\u05ea" + }, + { + "code": "hi", + "name": "\u0939\u093f\u0902\u0926\u0940" + }, + { + "code": "hr", + "name": "Hrvatski" + }, + { + "code": "hu", + "name": "Magyar" + }, + { + "code": "id", + "name": "Indonesia" + }, + { + "code": "is", + "name": "\u00edslenska" + }, + { + "code": "it", + "name": "Italiano" + }, + { + "code": "ja", + "name": "\u65e5\u672c\u8a9e" + }, + { + "code": "km", + "name": "\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a" + }, + { + "code": "kn", + "name": "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1" + }, + { + "code": "ko", + "name": "\ud55c\uad6d\uc758" + }, + { + "code": "ku", + "name": "\u06a9\u0648\u0631\u062f\u06cc" + }, + { + "code": "lo", + "name": "\u0ea5\u0eb2\u0ea7" + }, + { + "code": "lt", + "name": "Lietuvi\u0173 kalba" + }, + { + "code": "lv", + "name": "Latvie\u0161u valoda" + }, + { + "code": "mk", + "name": "\u043c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438" + }, + { + "code": "ml", + "name": "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02" + }, + { + "code": "mn", + "name": "\u041c\u043e\u043d\u0433\u043e\u043b (Mongolian)" + }, + { + "code": "mr", + "name": "\u092e\u0930\u093e\u0920\u0940" + }, + { + "code": "ms", + "name": "Melayu" + }, + { + "code": "my", + "name": "\u1019\u103c\u1014\u103a\u1019\u102c" + }, + { + "code": "nl", + "name": "Nederlands" + }, + { + "code": "no", + "name": "Norsk" + }, + { + "code": "pl", + "name": "Polski" + }, + { + "code": "ps", + "name": "\u067e\u069a\u062a\u0648" + }, + { + "code": "pt", + "name": "Portugu\u00eas" + }, + { + "code": "pt-BR", + "name": "Portugu\u00eas Brasileiro" + }, + { + "code": "ro", + "name": "Rom\u00e2n" + }, + { + "code": "ru", + "name": "\u0440\u0443\u0441\u0441\u043a\u0438\u0439" + }, + { + "code": "rw", + "name": "Kinyarwanda" + }, + { + "code": "si", + "name": "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd" + }, + { + "code": "sk", + "name": "Sloven\u010dina (Slovak)" + }, + { + "code": "sl", + "name": "Sloven\u0161\u010dina (Slovene)" + }, + { + "code": "sq", + "name": "Shqiptar" + }, + { + "code": "sr", + "name": "\u0441\u0440\u043f\u0441\u043a\u0438" + }, + { + "code": "sr-BA", + "name": "Srpski" + }, + { + "code": "sv", + "name": "Svenska" + }, + { + "code": "sw", + "name": "Swahili" + }, + { + "code": "ta", + "name": "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd" + }, + { + "code": "te", + "name": "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41" + }, + { + "code": "th", + "name": "\u0e44\u0e17\u0e22" + }, + { + "code": "tr", + "name": "Türkçe" + }, + { + "code": "uk", + "name": "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430" + }, + { + "code": "ur", + "name": "\u0627\u0631\u062f\u0648" + }, + { + "code": "uz", + "name": "\u040e\u0437\u0431\u0435\u043a" + }, + { + "code": "vi", + "name": "Vi\u1ec7t" + }, + { + "code": "zh", + "name": "\u7b80\u4f53\u4e2d\u6587" + }, + { + "code": "zh-TW", + "name": "\u7e41\u9ad4\u4e2d\u6587" + } +] diff --git a/xhiveframework/geo/report/__init__.py b/xhiveframework/geo/report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/geo/utils.py b/xhiveframework/geo/utils.py new file mode 100644 index 0000000..5fee53e --- /dev/null +++ b/xhiveframework/geo/utils.py @@ -0,0 +1,99 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework + + +@xhiveframework.whitelist() +def get_coords(doctype, filters, type): + """Get a geojson dict representing a doctype.""" + filters_sql = get_coords_conditions(doctype, filters)[4:] + + coords = None + if type == "location_field": + coords = return_location(doctype, filters_sql) + elif type == "coordinates": + coords = return_coordinates(doctype, filters_sql) + + return convert_to_geojson(type, coords) + + +def convert_to_geojson(type, coords): + """Converts GPS coordinates to geoJSON string.""" + geojson = {"type": "FeatureCollection", "features": None} + + if type == "location_field": + geojson["features"] = merge_location_features_in_one(coords) + elif type == "coordinates": + geojson["features"] = create_gps_markers(coords) + + return geojson + + +def merge_location_features_in_one(coords): + """Merging all features from location field.""" + geojson_dict = [] + for element in coords: + geojson_loc = xhiveframework.parse_json(element["location"]) + if not geojson_loc: + continue + for coord in geojson_loc["features"]: + coord["properties"]["name"] = element["name"] + geojson_dict.append(coord.copy()) + + return geojson_dict + + +def create_gps_markers(coords): + """Build Marker based on latitude and longitude.""" + geojson_dict = [] + for i in coords: + node = {"type": "Feature", "properties": {}, "geometry": {"type": "Point", "coordinates": None}} + node["properties"]["name"] = i.name + node["geometry"]["coordinates"] = [i.longitude, i.latitude] # geojson needs it reverse! + geojson_dict.append(node.copy()) + + return geojson_dict + + +def return_location(doctype, filters_sql): + """Get name and location fields for Doctype.""" + if filters_sql: + try: + coords = xhiveframework.db.sql( + f"""SELECT name, location FROM `tab{doctype}` WHERE {filters_sql}""", as_dict=True + ) + except xhiveframework.db.InternalError: + xhiveframework.msgprint(xhiveframework._("This Doctype does not contain location fields"), raise_exception=True) + return + else: + coords = xhiveframework.get_all(doctype, fields=["name", "location"]) + return coords + + +def return_coordinates(doctype, filters_sql): + """Get name, latitude and longitude fields for Doctype.""" + if filters_sql: + try: + coords = xhiveframework.db.sql( + f"""SELECT name, latitude, longitude FROM `tab{doctype}` WHERE {filters_sql}""", + as_dict=True, + ) + except xhiveframework.db.InternalError: + xhiveframework.msgprint( + xhiveframework._("This Doctype does not contain latitude and longitude fields"), raise_exception=True + ) + return + else: + coords = xhiveframework.get_all(doctype, fields=["name", "latitude", "longitude"]) + return coords + + +def get_coords_conditions(doctype, filters=None): + """Returns SQL conditions with user permissions and filters for event queries.""" + from xhiveframework.desk.reportview import get_filters_cond + + if not xhiveframework.has_permission(doctype): + xhiveframework.throw(xhiveframework._("Not Permitted"), xhiveframework.PermissionError) + + return get_filters_cond(doctype, filters, [], with_match_conditions=True) diff --git a/xhiveframework/gettext/extractors/navbar.py b/xhiveframework/gettext/extractors/navbar.py new file mode 100644 index 0000000..963e8c3 --- /dev/null +++ b/xhiveframework/gettext/extractors/navbar.py @@ -0,0 +1,51 @@ +import importlib +from pathlib import Path + +from xhiveframework.utils import get_bench_path + + +def extract(fileobj, *args, **kwargs): + """Extract standard navbar and help items from a python file. + + :param fileobj: file-like object to extract messages from. Should be a + python file containing two global variables `standard_navbar_items` and + `standard_help_items` which are lists of dicts. + """ + module = get_module(fileobj.name) + + if hasattr(module, "standard_navbar_items"): + standard_navbar_items = module.standard_navbar_items + for nav_item in standard_navbar_items: + if label := nav_item.get("item_label"): + item_type = nav_item.get("item_type") + yield ( + None, + "_", + label, + [ + "Label of a standard navbar item", + f"Type: {item_type}", + ], + ) + + if hasattr(module, "standard_help_items"): + standard_help_items = module.standard_help_items + for help_item in standard_help_items: + if label := help_item.get("item_label"): + item_type = nav_item.get("item_type") + yield ( + None, + "_", + label, + [ + "Label of a standard help item", + f"Type: {item_type}", + ], + ) + + +def get_module(path): + _path = Path(path) + rel_path = _path.relative_to(get_bench_path()) + import_path = ".".join(rel_path.parts[2:]).rstrip(".py") + return importlib.import_module(import_path) diff --git a/xhiveframework/handler.py b/xhiveframework/handler.py new file mode 100644 index 0000000..0add94f --- /dev/null +++ b/xhiveframework/handler.py @@ -0,0 +1,349 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import os +from mimetypes import guess_type +from typing import TYPE_CHECKING + +from werkzeug.wrappers import Response + +import xhiveframework +import xhiveframework.sessions +import xhiveframework.utils +from xhiveframework import _, is_whitelisted, ping +from xhiveframework.core.doctype.server_script.server_script_utils import get_server_script_map +from xhiveframework.monitor import add_data_to_monitor +from xhiveframework.utils import cint +from xhiveframework.utils.csvutils import build_csv_response +from xhiveframework.utils.deprecations import deprecation_warning +from xhiveframework.utils.image import optimize_image +from xhiveframework.utils.response import build_response + +if TYPE_CHECKING: + from xhiveframework.core.doctype.file.file import File + from xhiveframework.core.doctype.user.user import User + +ALLOWED_MIMETYPES = ( + "image/png", + "image/jpeg", + "application/pdf", + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.ms-excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.oasis.opendocument.text", + "application/vnd.oasis.opendocument.spreadsheet", + "text/plain", + "video/quicktime", + "video/mp4", +) + + +def handle(): + """handle request""" + + cmd = xhiveframework.local.form_dict.cmd + data = None + + if cmd != "login": + data = execute_cmd(cmd) + + # data can be an empty string or list which are valid responses + if data is not None: + if isinstance(data, Response): + # method returns a response object, pass it on + return data + + # add the response to `message` label + xhiveframework.response["message"] = data + + +def execute_cmd(cmd, from_async=False): + """execute a request as python module""" + for hook in reversed(xhiveframework.get_hooks("override_whitelisted_methods", {}).get(cmd, [])): + # override using the last hook + cmd = hook + break + + # via server script + server_script = get_server_script_map().get("_api", {}).get(cmd) + if server_script: + return run_server_script(server_script) + + try: + method = get_attr(cmd) + except Exception as e: + xhiveframework.throw(_("Failed to get method for command {0} with {1}").format(cmd, e)) + + if from_async: + method = method.queue + + if method != run_doc_method: + is_whitelisted(method) + is_valid_http_method(method) + + return xhiveframework.call(method, **xhiveframework.form_dict) + + +def run_server_script(server_script): + response = xhiveframework.get_doc("Server Script", server_script).execute_method() + + # some server scripts return output using flags (empty dict by default), + # while others directly modify xhiveframework.response + # return flags if not empty dict (this overwrites xhiveframework.response.message) + if response != {}: + return response + + +def is_valid_http_method(method): + if xhiveframework.flags.in_safe_exec: + return + + http_method = xhiveframework.local.request.method + + if http_method not in xhiveframework.allowed_http_methods_for_whitelisted_func[method]: + throw_permission_error() + + +def throw_permission_error(): + xhiveframework.throw(_("Not permitted"), xhiveframework.PermissionError) + + +@xhiveframework.whitelist(allow_guest=True) +def logout(): + xhiveframework.local.login_manager.logout() + xhiveframework.db.commit() + + +@xhiveframework.whitelist(allow_guest=True) +def web_logout(): + xhiveframework.local.login_manager.logout() + xhiveframework.db.commit() + xhiveframework.respond_as_web_page( + _("Logged Out"), _("You have been successfully logged out"), indicator_color="green" + ) + + +@xhiveframework.whitelist() +def uploadfile(): + ret = None + check_write_permission(xhiveframework.form_dict.doctype, xhiveframework.form_dict.docname) + + try: + if xhiveframework.form_dict.get("from_form"): + try: + ret = xhiveframework.get_doc( + { + "doctype": "File", + "attached_to_name": xhiveframework.form_dict.docname, + "attached_to_doctype": xhiveframework.form_dict.doctype, + "attached_to_field": xhiveframework.form_dict.docfield, + "file_url": xhiveframework.form_dict.file_url, + "file_name": xhiveframework.form_dict.filename, + "is_private": xhiveframework.utils.cint(xhiveframework.form_dict.is_private), + "content": xhiveframework.form_dict.filedata, + "decode": True, + } + ) + ret.save() + except xhiveframework.DuplicateEntryError: + # ignore pass + ret = None + xhiveframework.db.rollback() + else: + if xhiveframework.form_dict.get("method"): + method = xhiveframework.get_attr(xhiveframework.form_dict.method) + is_whitelisted(method) + ret = method() + except Exception: + xhiveframework.errprint(xhiveframework.utils.get_traceback()) + xhiveframework.response["http_status_code"] = 500 + ret = None + + return ret + + +@xhiveframework.whitelist(allow_guest=True) +def upload_file(): + user = None + if xhiveframework.session.user == "Guest": + if xhiveframework.get_system_settings("allow_guests_to_upload_files"): + ignore_permissions = True + else: + raise xhiveframework.PermissionError + else: + user: "User" = xhiveframework.get_doc("User", xhiveframework.session.user) + ignore_permissions = False + + files = xhiveframework.request.files + is_private = xhiveframework.form_dict.is_private + doctype = xhiveframework.form_dict.doctype + docname = xhiveframework.form_dict.docname + fieldname = xhiveframework.form_dict.fieldname + file_url = xhiveframework.form_dict.file_url + folder = xhiveframework.form_dict.folder or "Home" + method = xhiveframework.form_dict.method + filename = xhiveframework.form_dict.file_name + optimize = xhiveframework.form_dict.optimize + content = None + + if library_file := xhiveframework.form_dict.get("library_file_name"): + xhiveframework.has_permission("File", doc=library_file, throw=True) + doc = xhiveframework.get_value( + "File", + xhiveframework.form_dict.library_file_name, + ["is_private", "file_url", "file_name"], + as_dict=True, + ) + is_private = doc.is_private + file_url = doc.file_url + filename = doc.file_name + + if not ignore_permissions: + check_write_permission(doctype, docname) + + if "file" in files: + file = files["file"] + content = file.stream.read() + filename = file.filename + + content_type = guess_type(filename)[0] + if optimize and content_type and content_type.startswith("image/"): + args = {"content": content, "content_type": content_type} + if xhiveframework.form_dict.max_width: + args["max_width"] = int(xhiveframework.form_dict.max_width) + if xhiveframework.form_dict.max_height: + args["max_height"] = int(xhiveframework.form_dict.max_height) + content = optimize_image(**args) + + xhiveframework.local.uploaded_file = content + xhiveframework.local.uploaded_filename = filename + + if content is not None and (xhiveframework.session.user == "Guest" or (user and not user.has_desk_access())): + filetype = guess_type(filename)[0] + if filetype not in ALLOWED_MIMETYPES: + xhiveframework.throw(_("You can only upload JPG, PNG, PDF, TXT or Microsoft documents.")) + + if method: + method = xhiveframework.get_attr(method) + is_whitelisted(method) + return method() + else: + return xhiveframework.get_doc( + { + "doctype": "File", + "attached_to_doctype": doctype, + "attached_to_name": docname, + "attached_to_field": fieldname, + "folder": folder, + "file_name": filename, + "file_url": file_url, + "is_private": cint(is_private), + "content": content, + } + ).save(ignore_permissions=ignore_permissions) + + +def check_write_permission(doctype: str | None = None, name: str | None = None): + check_doctype = doctype and not name + if doctype and name: + try: + doc = xhiveframework.get_doc(doctype, name) + doc.has_permission("write") + except xhiveframework.DoesNotExistError: + # doc has not been inserted yet, name is set to "new-some-doctype" + check_doctype = True + + if check_doctype: + xhiveframework.has_permission(doctype, "write", throw=True) + + +@xhiveframework.whitelist(allow_guest=True) +def download_file(file_url: str): + """ + Download file using token and REST API. Valid session or + token is required to download private files. + + Method : GET + Endpoints : download_file, xhiveframework.core.doctype.file.file.download_file + URL Params : file_name = /path/to/file relative to site path + """ + file: "File" = xhiveframework.get_doc("File", {"file_url": file_url}) + if not file.is_downloadable(): + raise xhiveframework.PermissionError + + xhiveframework.local.response.filename = os.path.basename(file_url) + xhiveframework.local.response.filecontent = file.get_content() + xhiveframework.local.response.type = "download" + + +def get_attr(cmd): + """get method object from cmd""" + if "." in cmd: + method = xhiveframework.get_attr(cmd) + else: + deprecation_warning( + f"Calling shorthand for {cmd} is deprecated, please specify full path in RPC call." + ) + method = globals()[cmd] + return method + + +def run_doc_method(method, docs=None, dt=None, dn=None, arg=None, args=None): + """run a whitelisted controller method""" + from inspect import signature + + if not args and arg: + args = arg + + if dt: # not called from a doctype (from a page) + if not dn: + dn = dt # single + doc = xhiveframework.get_doc(dt, dn) + + else: + docs = xhiveframework.parse_json(docs) + doc = xhiveframework.get_doc(docs) + doc._original_modified = doc.modified + doc.check_if_latest() + + if not doc or not doc.has_permission("read"): + throw_permission_error() + + try: + args = xhiveframework.parse_json(args) + except ValueError: + pass + + method_obj = getattr(doc, method) + fn = getattr(method_obj, "__func__", method_obj) + is_whitelisted(fn) + is_valid_http_method(fn) + + fnargs = list(signature(method_obj).parameters) + + if not fnargs or (len(fnargs) == 1 and fnargs[0] == "self"): + response = doc.run_method(method) + + elif "args" in fnargs or not isinstance(args, dict): + response = doc.run_method(method, args) + + else: + response = doc.run_method(method, **args) + + xhiveframework.response.docs.append(doc) + if response is None: + return + + # build output as csv + if cint(xhiveframework.form_dict.get("as_csv")): + build_csv_response(response, _(doc.doctype).replace(" ", "")) + return + + xhiveframework.response["message"] = response + + add_data_to_monitor(methodname=method) + + +# for backwards compatibility +runserverobj = run_doc_method diff --git a/xhiveframework/hooks.py b/xhiveframework/hooks.py new file mode 100644 index 0000000..e57eb8b --- /dev/null +++ b/xhiveframework/hooks.py @@ -0,0 +1,473 @@ +import os + +from . import __version__ as app_version + +app_name = "xhiveframework" +app_title = "Xhive Framework" +app_publisher = "XhiveFramework Technologies" +app_description = "Full stack web framework with Python, Javascript, MariaDB, Redis, Node" +source_link = "https://lab.membtech.com/xhiveframework/xhiveframework15" +app_license = "MIT" +app_logo_url = "/assets/xhiveframework/images/xhiveframework-framework-logo.svg" + +develop_version = "15.x.x-develop" + +app_email = "developers@xhiveframework.io" + +docs_app = "xhiveframework_docs" + +translator_url = "https://translate.xhiveerp.com" + +before_install = "xhiveframework.utils.install.before_install" +after_install = "xhiveframework.utils.install.after_install" + +page_js = {"setup-wizard": "public/js/xhiveframework/setup_wizard.js"} + +# website +app_include_js = [ + "libs.bundle.js", + "desk.bundle.js", + "list.bundle.js", + "form.bundle.js", + "controls.bundle.js", + "report.bundle.js", + "telemetry.bundle.js", +] + +app_include_css = [ + "desk.bundle.css", + "report.bundle.css", +] +app_include_icons = [ + "xhiveframework/icons/timeless/icons.svg", + "xhiveframework/icons/espresso/icons.svg", +] + +doctype_js = { + "Web Page": "public/js/xhiveframework/utils/web_template.js", + "Website Settings": "public/js/xhiveframework/utils/web_template.js", +} + +web_include_js = ["website_script.js"] + +web_include_css = [] + +email_css = ["email.bundle.css"] + +website_route_rules = [ + {"from_route": "/blog/", "to_route": "Blog Post"}, + {"from_route": "/kb/", "to_route": "Help Article"}, + {"from_route": "/newsletters", "to_route": "Newsletter"}, + {"from_route": "/profile", "to_route": "me"}, + {"from_route": "/app/", "to_route": "app"}, +] + +website_redirects = [ + {"source": r"/desk(.*)", "target": r"/app\1"}, + { + "source": "/.well-known/openid-configuration", + "target": "/api/method/xhiveframework.integrations.oauth2.openid_configuration", + }, +] + +base_template = "templates/base.html" + +write_file_keys = ["file_url", "file_name"] + +notification_config = "xhiveframework.core.notifications.get_notification_config" + +before_tests = "xhiveframework.utils.install.before_tests" + +email_append_to = ["Event", "ToDo", "Communication"] + +calendars = ["Event"] + +leaderboards = "xhiveframework.desk.leaderboard.get_leaderboards" + +# login + +on_session_creation = [ + "xhiveframework.core.doctype.activity_log.feed.login_feed", + "xhiveframework.core.doctype.user.user.notify_admin_access_to_system_manager", +] + +on_logout = "xhiveframework.core.doctype.session_default_settings.session_default_settings.clear_session_defaults" + +# PDF +pdf_header_html = "xhiveframework.utils.pdf.pdf_header_html" +pdf_body_html = "xhiveframework.utils.pdf.pdf_body_html" +pdf_footer_html = "xhiveframework.utils.pdf.pdf_footer_html" + +# permissions + +permission_query_conditions = { + "Event": "xhiveframework.desk.doctype.event.event.get_permission_query_conditions", + "ToDo": "xhiveframework.desk.doctype.todo.todo.get_permission_query_conditions", + "User": "xhiveframework.core.doctype.user.user.get_permission_query_conditions", + "Dashboard Settings": "xhiveframework.desk.doctype.dashboard_settings.dashboard_settings.get_permission_query_conditions", + "Notification Log": "xhiveframework.desk.doctype.notification_log.notification_log.get_permission_query_conditions", + "Dashboard": "xhiveframework.desk.doctype.dashboard.dashboard.get_permission_query_conditions", + "Dashboard Chart": "xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.get_permission_query_conditions", + "Number Card": "xhiveframework.desk.doctype.number_card.number_card.get_permission_query_conditions", + "Notification Settings": "xhiveframework.desk.doctype.notification_settings.notification_settings.get_permission_query_conditions", + "Note": "xhiveframework.desk.doctype.note.note.get_permission_query_conditions", + "Kanban Board": "xhiveframework.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions", + "Contact": "xhiveframework.contacts.address_and_contact.get_permission_query_conditions_for_contact", + "Address": "xhiveframework.contacts.address_and_contact.get_permission_query_conditions_for_address", + "Communication": "xhiveframework.core.doctype.communication.communication.get_permission_query_conditions_for_communication", + "Workflow Action": "xhiveframework.workflow.doctype.workflow_action.workflow_action.get_permission_query_conditions", + "Prepared Report": "xhiveframework.core.doctype.prepared_report.prepared_report.get_permission_query_condition", + "File": "xhiveframework.core.doctype.file.file.get_permission_query_conditions", +} + +has_permission = { + "Event": "xhiveframework.desk.doctype.event.event.has_permission", + "ToDo": "xhiveframework.desk.doctype.todo.todo.has_permission", + "Note": "xhiveframework.desk.doctype.note.note.has_permission", + "User": "xhiveframework.core.doctype.user.user.has_permission", + "Dashboard Chart": "xhiveframework.desk.doctype.dashboard_chart.dashboard_chart.has_permission", + "Number Card": "xhiveframework.desk.doctype.number_card.number_card.has_permission", + "Kanban Board": "xhiveframework.desk.doctype.kanban_board.kanban_board.has_permission", + "Contact": "xhiveframework.contacts.address_and_contact.has_permission", + "Address": "xhiveframework.contacts.address_and_contact.has_permission", + "Communication": "xhiveframework.core.doctype.communication.communication.has_permission", + "Workflow Action": "xhiveframework.workflow.doctype.workflow_action.workflow_action.has_permission", + "File": "xhiveframework.core.doctype.file.file.has_permission", + "Prepared Report": "xhiveframework.core.doctype.prepared_report.prepared_report.has_permission", + "Notification Settings": "xhiveframework.desk.doctype.notification_settings.notification_settings.has_permission", +} + +has_website_permission = {"Address": "xhiveframework.contacts.doctype.address.address.has_website_permission"} + +jinja = { + "methods": "xhiveframework.utils.jinja_globals", + "filters": [ + "xhiveframework.utils.data.global_date_format", + "xhiveframework.utils.markdown", + "xhiveframework.website.utils.abs_url", + ], +} + +standard_queries = {"User": "xhiveframework.core.doctype.user.user.user_query"} + +doc_events = { + "*": { + "on_update": [ + "xhiveframework.desk.notifications.clear_doctype_notifications", + "xhiveframework.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", + "xhiveframework.core.doctype.file.utils.attach_files_to_document", + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.apply", + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.update_due_date", + "xhiveframework.core.doctype.user_type.user_type.apply_permissions_for_non_standard_user_type", + ], + "after_rename": "xhiveframework.desk.notifications.clear_doctype_notifications", + "on_cancel": [ + "xhiveframework.desk.notifications.clear_doctype_notifications", + "xhiveframework.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.apply", + ], + "on_trash": [ + "xhiveframework.desk.notifications.clear_doctype_notifications", + "xhiveframework.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", + ], + "on_update_after_submit": [ + "xhiveframework.workflow.doctype.workflow_action.workflow_action.process_workflow_actions", + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.apply", + "xhiveframework.automation.doctype.assignment_rule.assignment_rule.update_due_date", + "xhiveframework.core.doctype.file.utils.attach_files_to_document", + ], + "on_change": [ + "xhiveframework.social.doctype.energy_point_rule.energy_point_rule.process_energy_points", + "xhiveframework.automation.doctype.milestone_tracker.milestone_tracker.evaluate_milestone", + ], + }, + "Event": { + "after_insert": "xhiveframework.integrations.doctype.google_calendar.google_calendar.insert_event_in_google_calendar", + "on_update": "xhiveframework.integrations.doctype.google_calendar.google_calendar.update_event_in_google_calendar", + "on_trash": "xhiveframework.integrations.doctype.google_calendar.google_calendar.delete_event_from_google_calendar", + }, + "Contact": { + "after_insert": "xhiveframework.integrations.doctype.google_contacts.google_contacts.insert_contacts_to_google_contacts", + "on_update": "xhiveframework.integrations.doctype.google_contacts.google_contacts.update_contacts_to_google_contacts", + }, + "DocType": { + "on_update": "xhiveframework.cache_manager.build_domain_restriced_doctype_cache", + }, + "Page": { + "on_update": "xhiveframework.cache_manager.build_domain_restriced_page_cache", + }, +} + +scheduler_events = { + "cron": { + "0/15 * * * *": [ + "xhiveframework.oauth.delete_oauth2_data", + "xhiveframework.website.doctype.web_page.web_page.check_publish_status", + "xhiveframework.twofactor.delete_all_barcodes_for_users", + ], + "0/10 * * * *": [ + "xhiveframework.email.doctype.email_account.email_account.pull", + ], + # Hourly but offset by 30 minutes + "30 * * * *": [ + "xhiveframework.core.doctype.prepared_report.prepared_report.expire_stalled_report", + ], + # Daily but offset by 45 minutes + "45 0 * * *": [ + "xhiveframework.core.doctype.log_settings.log_settings.run_log_clean_up", + ], + }, + "all": [ + "xhiveframework.email.queue.flush", + "xhiveframework.email.doctype.email_account.email_account.notify_unreplied", + "xhiveframework.utils.global_search.sync_global_search", + "xhiveframework.monitor.flush", + "xhiveframework.automation.doctype.reminder.reminder.send_reminders", + ], + "hourly": [ + "xhiveframework.model.utils.link_count.update_link_count", + "xhiveframework.model.utils.user_settings.sync_user_settings", + "xhiveframework.desk.page.backups.backups.delete_downloadable_backups", + "xhiveframework.deferred_insert.save_to_db", + "xhiveframework.desk.form.document_follow.send_hourly_updates", + "xhiveframework.integrations.doctype.google_calendar.google_calendar.sync", + "xhiveframework.email.doctype.newsletter.newsletter.send_scheduled_email", + "xhiveframework.website.doctype.personal_data_deletion_request.personal_data_deletion_request.process_data_deletion_request", + ], + "daily": [ + "xhiveframework.desk.notifications.clear_notifications", + "xhiveframework.desk.doctype.event.event.send_event_digest", + "xhiveframework.sessions.clear_expired_sessions", + "xhiveframework.email.doctype.notification.notification.trigger_daily_alerts", + "xhiveframework.website.doctype.personal_data_deletion_request.personal_data_deletion_request.remove_unverified_record", + "xhiveframework.desk.form.document_follow.send_daily_updates", + "xhiveframework.social.doctype.energy_point_settings.energy_point_settings.allocate_review_points", + "xhiveframework.integrations.doctype.google_contacts.google_contacts.sync", + "xhiveframework.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat_entry", + "xhiveframework.automation.doctype.auto_repeat.auto_repeat.set_auto_repeat_as_completed", + ], + "daily_long": [ + "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily", + "xhiveframework.utils.change_log.check_for_update", + "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_daily", + "xhiveframework.email.doctype.auto_email_report.auto_email_report.send_daily", + "xhiveframework.integrations.doctype.google_drive.google_drive.daily_backup", + ], + "weekly_long": [ + "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly", + "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_weekly", + "xhiveframework.desk.form.document_follow.send_weekly_updates", + "xhiveframework.social.doctype.energy_point_log.energy_point_log.send_weekly_summary", + "xhiveframework.integrations.doctype.google_drive.google_drive.weekly_backup", + ], + "monthly": [ + "xhiveframework.email.doctype.auto_email_report.auto_email_report.send_monthly", + "xhiveframework.social.doctype.energy_point_log.energy_point_log.send_monthly_summary", + ], + "monthly_long": [ + "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_monthly" + ], +} + +get_translated_dict = { + ("doctype", "System Settings"): "xhiveframework.geo.country_info.get_translated_dict", + ("page", "setup-wizard"): "xhiveframework.geo.country_info.get_translated_dict", +} + +sounds = [ + {"name": "email", "src": "/assets/xhiveframework/sounds/email.mp3", "volume": 0.1}, + {"name": "submit", "src": "/assets/xhiveframework/sounds/submit.mp3", "volume": 0.1}, + {"name": "cancel", "src": "/assets/xhiveframework/sounds/cancel.mp3", "volume": 0.1}, + {"name": "delete", "src": "/assets/xhiveframework/sounds/delete.mp3", "volume": 0.05}, + {"name": "click", "src": "/assets/xhiveframework/sounds/click.mp3", "volume": 0.05}, + {"name": "error", "src": "/assets/xhiveframework/sounds/error.mp3", "volume": 0.1}, + {"name": "alert", "src": "/assets/xhiveframework/sounds/alert.mp3", "volume": 0.2}, + # {"name": "chime", "src": "/assets/xhiveframework/sounds/chime.mp3"}, +] + +setup_wizard_exception = [ + "xhiveframework.desk.page.setup_wizard.setup_wizard.email_setup_wizard_exception", + "xhiveframework.desk.page.setup_wizard.setup_wizard.log_setup_wizard_exception", +] + +before_migrate = ["xhiveframework.core.doctype.patch_log.patch_log.before_migrate"] +after_migrate = ["xhiveframework.website.doctype.website_theme.website_theme.after_migrate"] + +otp_methods = ["OTP App", "Email", "SMS"] + +user_data_fields = [ + {"doctype": "Access Log", "strict": True}, + {"doctype": "Activity Log", "strict": True}, + {"doctype": "Comment", "strict": True}, + { + "doctype": "Contact", + "filter_by": "email_id", + "redact_fields": ["first_name", "last_name", "phone", "mobile_no"], + "rename": True, + }, + {"doctype": "Contact Email", "filter_by": "email_id"}, + { + "doctype": "Address", + "filter_by": "email_id", + "redact_fields": [ + "address_title", + "address_line1", + "address_line2", + "city", + "county", + "state", + "pincode", + "phone", + "fax", + ], + }, + { + "doctype": "Communication", + "filter_by": "sender", + "redact_fields": ["sender_full_name", "phone_no", "content"], + }, + {"doctype": "Communication", "filter_by": "recipients"}, + {"doctype": "Email Group Member", "filter_by": "email"}, + {"doctype": "Email Unsubscribe", "filter_by": "email", "partial": True}, + {"doctype": "Email Queue", "filter_by": "sender"}, + {"doctype": "Email Queue Recipient", "filter_by": "recipient"}, + { + "doctype": "File", + "filter_by": "attached_to_name", + "redact_fields": ["file_name", "file_url"], + }, + { + "doctype": "User", + "filter_by": "name", + "redact_fields": [ + "email", + "username", + "first_name", + "middle_name", + "last_name", + "full_name", + "birth_date", + "user_image", + "phone", + "mobile_no", + "location", + "banner_image", + "interest", + "bio", + "email_signature", + ], + }, + {"doctype": "Version", "strict": True}, +] + +global_search_doctypes = { + "Default": [ + {"doctype": "Contact"}, + {"doctype": "Address"}, + {"doctype": "ToDo"}, + {"doctype": "Note"}, + {"doctype": "Event"}, + {"doctype": "Blog Post"}, + {"doctype": "Dashboard"}, + {"doctype": "Country"}, + {"doctype": "Currency"}, + {"doctype": "Newsletter"}, + {"doctype": "Letter Head"}, + {"doctype": "Workflow"}, + {"doctype": "Web Page"}, + {"doctype": "Web Form"}, + ] +} + +override_whitelisted_methods = { + # Legacy File APIs + "xhiveframework.core.doctype.file.file.download_file": "download_file", + "xhiveframework.core.doctype.file.file.unzip_file": "xhiveframework.core.api.file.unzip_file", + "xhiveframework.core.doctype.file.file.get_attached_images": "xhiveframework.core.api.file.get_attached_images", + "xhiveframework.core.doctype.file.file.get_files_in_folder": "xhiveframework.core.api.file.get_files_in_folder", + "xhiveframework.core.doctype.file.file.get_files_by_search_text": "xhiveframework.core.api.file.get_files_by_search_text", + "xhiveframework.core.doctype.file.file.get_max_file_size": "xhiveframework.core.api.file.get_max_file_size", + "xhiveframework.core.doctype.file.file.create_new_folder": "xhiveframework.core.api.file.create_new_folder", + "xhiveframework.core.doctype.file.file.move_file": "xhiveframework.core.api.file.move_file", + "xhiveframework.core.doctype.file.file.zip_files": "xhiveframework.core.api.file.zip_files", + # Legacy (& Consistency) OAuth2 APIs + "xhiveframework.www.login.login_via_google": "xhiveframework.integrations.oauth2_logins.login_via_google", + "xhiveframework.www.login.login_via_github": "xhiveframework.integrations.oauth2_logins.login_via_github", + "xhiveframework.www.login.login_via_facebook": "xhiveframework.integrations.oauth2_logins.login_via_facebook", + "xhiveframework.www.login.login_via_xhiveframework": "xhiveframework.integrations.oauth2_logins.login_via_xhiveframework", + "xhiveframework.www.login.login_via_office365": "xhiveframework.integrations.oauth2_logins.login_via_office365", + "xhiveframework.www.login.login_via_salesforce": "xhiveframework.integrations.oauth2_logins.login_via_salesforce", + "xhiveframework.www.login.login_via_fairlogin": "xhiveframework.integrations.oauth2_logins.login_via_fairlogin", +} + +ignore_links_on_delete = [ + "Communication", + "ToDo", + "DocShare", + "Email Unsubscribe", + "Activity Log", + "File", + "Version", + "Document Follow", + "Comment", + "View Log", + "Tag Link", + "Notification Log", + "Email Queue", + "Document Share Key", + "Integration Request", + "Unhandled Email", + "Webhook Request Log", + "Workspace", + "Route History", + "Access Log", +] + +# Request Hooks +before_request = [ + "xhiveframework.recorder.record", + "xhiveframework.monitor.start", + "xhiveframework.rate_limiter.apply", +] + +# Background Job Hooks +before_job = [ + "xhiveframework.recorder.record", + "xhiveframework.monitor.start", +] + +if os.getenv("XHIVEFRAMEWORK_SENTRY_DSN") and ( + os.getenv("ENABLE_SENTRY_DB_MONITORING") or os.getenv("SENTRY_TRACING_SAMPLE_RATE") +): + before_request.append("xhiveframework.utils.sentry.set_sentry_context") + before_job.append("xhiveframework.utils.sentry.set_sentry_context") + +after_job = [ + "xhiveframework.recorder.dump", + "xhiveframework.monitor.stop", + "xhiveframework.utils.file_lock.release_document_locks", + "xhiveframework.utils.background_jobs.flush_telemetry", +] + +extend_bootinfo = [ + "xhiveframework.utils.telemetry.add_bootinfo", + "xhiveframework.core.doctype.user_permission.user_permission.send_user_permissions", + "xhiveframework.utils.sentry.add_bootinfo", +] + +export_python_type_annotations = True + +# log doctype cleanups to automatically add in log settings +default_log_clearing_doctypes = { + "Error Log": 14, + "Email Queue": 30, + "Scheduled Job Log": 7, + "Submission Queue": 7, + "Prepared Report": 14, + "Webhook Request Log": 30, + "Unhandled Email": 30, + "Reminder": 30, + "Integration Request": 90, + "Activity Log": 90, + "Route History": 90, +} diff --git a/xhiveframework/installer.py b/xhiveframework/installer.py new file mode 100644 index 0000000..a0df017 --- /dev/null +++ b/xhiveframework/installer.py @@ -0,0 +1,869 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import configparser +import gzip +import json +import os +import re +import subprocess +import sys +from collections import OrderedDict +from contextlib import suppress +from shutil import which + +import click +from semantic_version import Version + +import xhiveframework +from xhiveframework.defaults import _clear_cache +from xhiveframework.utils import cint, is_git_url +from xhiveframework.utils.dashboard import sync_dashboards +from xhiveframework.utils.synchronization import filelock + + +def _is_scheduler_enabled() -> bool: + enable_scheduler = False + try: + xhiveframework.connect() + enable_scheduler = cint(xhiveframework.db.get_single_value("System Settings", "enable_scheduler")) + except Exception: + pass + finally: + xhiveframework.db.close() + + return bool(enable_scheduler) + + +def _new_site( + db_name, + site, + db_root_username=None, + db_root_password=None, + admin_password=None, + verbose=False, + install_apps=None, + source_sql=None, + force=False, + no_mariadb_socket=False, + reinstall=False, + db_password=None, + db_type=None, + db_host=None, + db_port=None, +): + """Install a new XhiveFramework site""" + + from xhiveframework.utils import scheduler + + if not force and os.path.exists(site): + print(f"Site {site} already exists") + sys.exit(1) + + if no_mariadb_socket and not db_type == "mariadb": + print("--no-mariadb-socket requires db_type to be set to mariadb.") + sys.exit(1) + + xhiveframework.init(site=site) + + if not db_name: + import hashlib + + db_name = ( + "_" + + hashlib.sha1( + os.path.realpath(xhiveframework.get_site_path()).encode(), usedforsecurity=False + ).hexdigest()[:16] + ) + + try: + # enable scheduler post install? + enable_scheduler = _is_scheduler_enabled() + except Exception: + enable_scheduler = False + + make_site_dirs() + + with filelock("bench_new_site", timeout=1): + install_db( + root_login=db_root_username, + root_password=db_root_password, + db_name=db_name, + admin_password=admin_password, + verbose=verbose, + source_sql=source_sql, + force=force, + reinstall=reinstall, + db_password=db_password, + db_type=db_type, + db_host=db_host, + db_port=db_port, + no_mariadb_socket=no_mariadb_socket, + ) + + apps_to_install = ["xhiveframework"] + (xhiveframework.conf.get("install_apps") or []) + (list(install_apps) or []) + + for app in apps_to_install: + # NOTE: not using force here for 2 reasons: + # 1. It's not really needed here as we've freshly installed a new db + # 2. If someone uses a sql file to do restore and that file already had + # installed_apps then it might cause problems as that sql file can be of any previous version(s) + # which might be incompatible with the current version and using force might cause problems. + # Example: the DocType DocType might not have `migration_hash` column which will cause failure in the restore. + install_app(app, verbose=verbose, set_as_patched=not source_sql, force=False) + + scheduler.toggle_scheduler(enable_scheduler) + xhiveframework.db.commit() + + scheduler_status = "disabled" if xhiveframework.utils.scheduler.is_scheduler_disabled() else "enabled" + print("*** Scheduler is", scheduler_status, "***") + + +def install_db( + root_login=None, + root_password=None, + db_name=None, + source_sql=None, + admin_password=None, + verbose=True, + force=0, + site_config=None, + reinstall=False, + db_password=None, + db_type=None, + db_host=None, + db_port=None, + no_mariadb_socket=False, +): + import xhiveframework.database + from xhiveframework.database import setup_database + + if not db_type: + db_type = xhiveframework.conf.db_type + + if not root_login and db_type == "mariadb": + root_login = "root" + elif not root_login and db_type == "postgres": + root_login = "postgres" + + make_conf( + db_name, + site_config=site_config, + db_password=db_password, + db_type=db_type, + db_host=db_host, + db_port=db_port, + ) + xhiveframework.flags.in_install_db = True + + xhiveframework.flags.root_login = root_login + xhiveframework.flags.root_password = root_password + setup_database(force, source_sql, verbose, no_mariadb_socket) + + xhiveframework.conf.admin_password = xhiveframework.conf.admin_password or admin_password + + remove_missing_apps() + + xhiveframework.db.create_auth_table() + xhiveframework.db.create_global_search_table() + xhiveframework.db.create_user_settings_table() + + xhiveframework.flags.in_install_db = False + + +def find_org(org_repo: str) -> tuple[str, str]: + """find the org a repo is in + + find_org() + ref -> https://lab.membtech.com/xhiveframework/bench_new/blob/develop/bench/utils/__init__.py#L390 + + :param org_repo: + :type org_repo: str + + :raises InvalidRemoteException: if the org is not found + + :return: organisation and repository + :rtype: Tuple[str, str] + """ + import requests + + from xhiveframework.exceptions import InvalidRemoteException + + for org in ["xhiveframework", "xhiveerp"]: + response = requests.head(f"https://api.github.com/repos/{org}/{org_repo}") + if response.status_code == 400: + response = requests.head(f"https://github.com/{org}/{org_repo}") + if response.ok: + return org, org_repo + + raise InvalidRemoteException + + +def fetch_details_from_tag(_tag: str) -> tuple[str, str, str]: + """parse org, repo, tag from string + + fetch_details_from_tag() + ref -> https://lab.membtech.com/xhiveframework/bench_new/blob/develop/bench/utils/__init__.py#L403 + + :param _tag: input string + :type _tag: str + + :return: organisation, repostitory, tag + :rtype: Tuple[str, str, str] + """ + app_tag = _tag.split("@") + org_repo = app_tag[0].split("/") + + try: + repo, tag = app_tag + except ValueError: + repo, tag = [*app_tag, None] + + try: + org, repo = org_repo + except Exception: + org, repo = find_org(org_repo[0]) + + return org, repo, tag + + +def parse_app_name(name: str) -> str: + """parse repo name from name + + __setup_details_from_git() + ref -> https://lab.membtech.com/xhiveframework/bench_new/blob/develop/bench/app.py#L114 + + + :param name: git tag + :type name: str + + :return: repository name + :rtype: str + """ + name = name.rstrip("/") + if os.path.exists(name): + repo = os.path.split(name)[-1] + elif is_git_url(name): + if name.startswith("git@") or name.startswith("ssh://"): + _repo = name.split(":")[1].rsplit("/", 1)[1] + else: + _repo = name.rsplit("/", 2)[2] + repo = _repo.split(".", 1)[0] + else: + _, repo, _ = fetch_details_from_tag(name) + return repo + + +def install_app(name, verbose=False, set_as_patched=True, force=False): + from xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs + from xhiveframework.model.sync import sync_for + from xhiveframework.modules.utils import sync_customizations + from xhiveframework.utils.fixtures import sync_fixtures + + xhiveframework.flags.in_install = name + xhiveframework.flags.ignore_in_install = False + + xhiveframework.clear_cache() + app_hooks = xhiveframework.get_hooks(app_name=name) + installed_apps = xhiveframework.get_installed_apps() + + # install pre-requisites + if app_hooks.required_apps: + for app in app_hooks.required_apps: + required_app = parse_app_name(app) + install_app(required_app, verbose=verbose) + + xhiveframework.flags.in_install = name + xhiveframework.clear_cache() + + if name not in xhiveframework.get_all_apps(): + raise Exception(f"App {name} not in apps.txt") + + if not force and name in installed_apps: + click.secho(f"App {name} already installed", fg="yellow") + return + + print(f"\nInstalling {name}...") + + if name != "xhiveframework": + xhiveframework.only_for("System Manager") + + for before_install in app_hooks.before_install or []: + out = xhiveframework.get_attr(before_install)() + if out is False: + return + + for fn in xhiveframework.get_hooks("before_app_install"): + xhiveframework.get_attr(fn)(name) + + if name != "xhiveframework": + add_module_defs(name, ignore_if_duplicate=force) + + sync_for(name, force=force, reset_permissions=True) + + add_to_installed_apps(name) + + xhiveframework.get_doc("Portal Settings", "Portal Settings").sync_menu() + + if set_as_patched: + set_all_patches_as_completed(name) + + for after_install in app_hooks.after_install or []: + xhiveframework.get_attr(after_install)() + + for fn in xhiveframework.get_hooks("after_app_install"): + xhiveframework.get_attr(fn)(name) + + sync_jobs() + sync_fixtures(name) + sync_customizations(name) + sync_dashboards(name) + + for after_sync in app_hooks.after_sync or []: + xhiveframework.get_attr(after_sync)() # + + xhiveframework.flags.in_install = False + + +def add_to_installed_apps(app_name, rebuild_website=True): + installed_apps = xhiveframework.get_installed_apps() + if app_name not in installed_apps: + installed_apps.append(app_name) + xhiveframework.db.set_global("installed_apps", json.dumps(installed_apps)) + xhiveframework.db.commit() + if xhiveframework.flags.in_install: + post_install(rebuild_website) + + +def remove_from_installed_apps(app_name): + installed_apps = xhiveframework.get_installed_apps() + if app_name in installed_apps: + installed_apps.remove(app_name) + xhiveframework.db.set_value( + "DefaultValue", {"defkey": "installed_apps"}, "defvalue", json.dumps(installed_apps) + ) + _clear_cache("__global") + xhiveframework.db.commit() + if xhiveframework.flags.in_install: + post_install() + + +def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False): + """Remove app and all linked to the app's module with the app from a site.""" + + site = xhiveframework.local.site + app_hooks = xhiveframework.get_hooks(app_name=app_name) + + # dont allow uninstall app if not installed unless forced + if not force: + if app_name not in xhiveframework.get_installed_apps(): + click.secho(f"App {app_name} not installed on Site {site}", fg="yellow") + return + + # Don't allow uninstalling if we have dependent apps installed + for app in xhiveframework.get_installed_apps(): + if app != app_name: + hooks = xhiveframework.get_hooks(app_name=app) + if hooks.required_apps and any(app_name in required_app for required_app in hooks.required_apps): + click.secho(f"App {app_name} is a dependency of {app}. Uninstall {app} first.", fg="yellow") + return + + print(f"Uninstalling App {app_name} from Site {site}...") + + if not dry_run and not yes: + confirm = click.confirm( + "All doctypes (including custom), modules related to this app will be" + " deleted. Are you sure you want to continue?" + ) + if not confirm: + return + + if not (dry_run or no_backup): + from xhiveframework.utils.backups import scheduled_backup + + print("Backing up...") + scheduled_backup(ignore_files=True) + + xhiveframework.flags.in_uninstall = True + + for before_uninstall in app_hooks.before_uninstall or []: + xhiveframework.get_attr(before_uninstall)() + + for fn in xhiveframework.get_hooks("before_app_uninstall"): + xhiveframework.get_attr(fn)(app_name) + + modules = xhiveframework.get_all("Module Def", filters={"app_name": app_name}, pluck="name") + + drop_doctypes = _delete_modules(modules, dry_run=dry_run) + _delete_doctypes(drop_doctypes, dry_run=dry_run) + + if not dry_run: + remove_from_installed_apps(app_name) + xhiveframework.get_single("Installed Applications").update_versions() + xhiveframework.db.commit() + + for after_uninstall in app_hooks.after_uninstall or []: + xhiveframework.get_attr(after_uninstall)() + + for fn in xhiveframework.get_hooks("after_app_uninstall"): + xhiveframework.get_attr(fn)(app_name) + + click.secho(f"Uninstalled App {app_name} from Site {site}", fg="green") + xhiveframework.flags.in_uninstall = False + + +def _delete_modules(modules: list[str], dry_run: bool) -> list[str]: + """Delete modules belonging to the app and all related doctypes. + + Note: All record linked linked to Module Def are also deleted. + + Returns: list of deleted doctypes.""" + drop_doctypes = [] + + doctype_link_field_map = _get_module_linked_doctype_field_map() + for module_name in modules: + print(f"Deleting Module '{module_name}'") + + for doctype in xhiveframework.get_all( + "DocType", filters={"module": module_name}, fields=["name", "issingle"] + ): + print(f"* removing DocType '{doctype.name}'...") + + if not dry_run: + if doctype.issingle: + xhiveframework.delete_doc("DocType", doctype.name, ignore_on_trash=True, force=True) + else: + drop_doctypes.append(doctype.name) + + _delete_linked_documents(module_name, doctype_link_field_map, dry_run=dry_run) + + print(f"* removing Module Def '{module_name}'...") + if not dry_run: + xhiveframework.delete_doc("Module Def", module_name, ignore_on_trash=True, force=True) + + return drop_doctypes + + +def _delete_linked_documents(module_name: str, doctype_linkfield_map: dict[str, str], dry_run: bool) -> None: + """Deleted all records linked with module def""" + for doctype, fieldname in doctype_linkfield_map.items(): + for record in xhiveframework.get_all(doctype, filters={fieldname: module_name}, pluck="name"): + print(f"* removing {doctype} '{record}'...") + if not dry_run: + xhiveframework.delete_doc(doctype, record, ignore_on_trash=True, force=True) + + +def _get_module_linked_doctype_field_map() -> dict[str, str]: + """Get all the doctypes which have module linked with them. + + returns ordered dictionary with doctype->link field mapping.""" + + # Hardcoded to change order of deletion + ordered_doctypes = [ + ("Workspace", "module"), + ("Report", "module"), + ("Page", "module"), + ("Web Form", "module"), + ] + doctype_to_field_map = OrderedDict(ordered_doctypes) + + linked_doctypes = xhiveframework.get_all( + "DocField", + filters={"fieldtype": "Link", "options": "Module Def"}, + fields=["parent", "fieldname"], + ) + existing_linked_doctypes = [d for d in linked_doctypes if xhiveframework.db.exists("DocType", d.parent)] + + for d in existing_linked_doctypes: + # DocType deletion is handled separately in the end + if d.parent not in doctype_to_field_map and d.parent != "DocType": + doctype_to_field_map[d.parent] = d.fieldname + + return doctype_to_field_map + + +def _delete_doctypes(doctypes: list[str], dry_run: bool) -> None: + for doctype in set(doctypes): + print(f"* dropping Table for '{doctype}'...") + if not dry_run: + xhiveframework.delete_doc("DocType", doctype, ignore_on_trash=True, force=True) + xhiveframework.db.sql_ddl(f"DROP TABLE IF EXISTS `tab{doctype}`") + + +def post_install(rebuild_website=False): + from xhiveframework.website.utils import clear_website_cache + + if rebuild_website: + clear_website_cache() + + init_singles() + xhiveframework.db.commit() + xhiveframework.clear_cache() + + +def set_all_patches_as_completed(app): + from xhiveframework.modules.patch_handler import get_patches_from_app + + patches = get_patches_from_app(app) + for patch in patches: + xhiveframework.get_doc({"doctype": "Patch Log", "patch": patch}).insert(ignore_permissions=True) + xhiveframework.db.commit() + + +def init_singles(): + singles = xhiveframework.get_all("DocType", filters={"issingle": True}, pluck="name") + for single in singles: + if xhiveframework.db.get_singles_dict(single): + continue + + try: + doc = xhiveframework.new_doc(single) + doc.flags.ignore_mandatory = True + doc.flags.ignore_validate = True + doc.save() + except (ImportError, xhiveframework.DoesNotExistError): + # The doctype exists, but controller is deleted, + # no need to attempt to init such single, ref: #16917 + continue + + +def make_conf(db_name=None, db_password=None, site_config=None, db_type=None, db_host=None, db_port=None): + site = xhiveframework.local.site + make_site_config(db_name, db_password, site_config, db_type=db_type, db_host=db_host, db_port=db_port) + sites_path = xhiveframework.local.sites_path + xhiveframework.destroy() + xhiveframework.init(site, sites_path=sites_path) + + +def make_site_config( + db_name=None, db_password=None, site_config=None, db_type=None, db_host=None, db_port=None +): + xhiveframework.create_folder(os.path.join(xhiveframework.local.site_path)) + site_file = get_site_config_path() + + if not os.path.exists(site_file): + if not (site_config and isinstance(site_config, dict)): + site_config = get_conf_params(db_name, db_password) + + if db_type: + site_config["db_type"] = db_type + + if db_host: + site_config["db_host"] = db_host + + if db_port: + site_config["db_port"] = db_port + + with open(site_file, "w") as f: + f.write(json.dumps(site_config, indent=1, sort_keys=True)) + + +def update_site_config(key, value, validate=True, site_config_path=None): + """Update a value in site_config""" + from xhiveframework.utils.synchronization import filelock + + if not site_config_path: + site_config_path = get_site_config_path() + + # Sometimes global config file is passed directly to this function + _is_global_conf = "common_site_config" in site_config_path + + with filelock("site_config", is_global=_is_global_conf): + _update_config_file(key=key, value=value, config_file=site_config_path) + + +def _update_config_file(key: str, value, config_file: str): + """Updates site or common config""" + with open(config_file) as f: + site_config = json.loads(f.read()) + + # In case of non-int value + if value in ("0", "1"): + value = int(value) + + # boolean + if value == "false": + value = False + if value == "true": + value = True + + # remove key if value is None + if value == "None": + if key in site_config: + del site_config[key] + else: + site_config[key] = value + + with open(config_file, "w") as f: + f.write(json.dumps(site_config, indent=1, sort_keys=True)) + + if hasattr(xhiveframework.local, "conf"): + xhiveframework.local.conf[key] = value + + +def get_site_config_path(): + return os.path.join(xhiveframework.local.site_path, "site_config.json") + + +def get_conf_params(db_name=None, db_password=None): + if not db_name: + db_name = input("Database Name: ") + if not db_name: + raise Exception("Database Name Required") + + if not db_password: + from xhiveframework.utils import random_string + + db_password = random_string(16) + + return {"db_name": db_name, "db_password": db_password} + + +def make_site_dirs(): + for dir_path in [ + os.path.join("public", "files"), + os.path.join("private", "backups"), + os.path.join("private", "files"), + "locks", + "logs", + ]: + path = xhiveframework.get_site_path(dir_path) + os.makedirs(path, exist_ok=True) + + +def add_module_defs(app, ignore_if_duplicate=False): + modules = xhiveframework.get_module_list(app) + for module in modules: + d = xhiveframework.new_doc("Module Def") + d.app_name = app + d.module_name = module + d.insert(ignore_permissions=True, ignore_if_duplicate=ignore_if_duplicate) + + +def remove_missing_apps(): + import importlib + + apps = ("xhiveframework_subscription", "shopping_cart") + installed_apps = json.loads(xhiveframework.db.get_global("installed_apps") or "[]") + for app in apps: + if app in installed_apps: + try: + importlib.import_module(app) + + except ImportError: + installed_apps.remove(app) + xhiveframework.db.set_global("installed_apps", json.dumps(installed_apps)) + + +def convert_archive_content(sql_file_path): + if xhiveframework.conf.db_type == "mariadb": + # ever since mariaDB 10.6, row_format COMPRESSED has been deprecated and removed + # this step is added to ease restoring sites depending on older mariaDB servers + # This change was reverted by mariadb in 10.6.6 + # Ref: https://mariadb.com/kb/en/innodb-compressed-row-format/#read-only + from pathlib import Path + + from xhiveframework.utils import random_string + + version = _guess_mariadb_version() + if not version or (version <= (10, 6, 0) or version >= (10, 6, 6)): + return + + click.secho( + "MariaDB version being used does not support ROW_FORMAT=COMPRESSED, " + "converting into DYNAMIC format.", + fg="yellow", + ) + + old_sql_file_path = Path(f"{sql_file_path}_{random_string(10)}") + sql_file_path = Path(sql_file_path) + + os.rename(sql_file_path, old_sql_file_path) + sql_file_path.touch() + + with open(old_sql_file_path) as r, open(sql_file_path, "a") as w: + for line in r: + w.write(line.replace("ROW_FORMAT=COMPRESSED", "ROW_FORMAT=DYNAMIC")) + + old_sql_file_path.unlink() + + +def _guess_mariadb_version() -> tuple[int] | None: + # Using command-line because we *might* not have a connection yet and this command is required + # in non-interactive mode. + # Use db.sql("select version()") instead if connection is available. + with suppress(Exception): + mariadb = which("mariadb") or which("mysql") + version_output = subprocess.getoutput(f"{mariadb} --version") + version_regex = r"(?P\d+\.\d+\.\d+)-MariaDB" + + version = re.search(version_regex, version_output).group("version") + + return tuple(int(v) for v in version.split(".")) + + +def extract_files(site_name, file_path): + import shutil + import subprocess + + from xhiveframework.utils import get_bench_relative_path + + file_path = get_bench_relative_path(file_path) + + # Need to do xhiveframework.init to maintain the site locals + xhiveframework.init(site=site_name) + abs_site_path = os.path.abspath(xhiveframework.get_site_path()) + + # Copy the files to the parent directory and extract + shutil.copy2(os.path.abspath(file_path), abs_site_path) + + # Get the file name splitting the file path on + tar_name = os.path.split(file_path)[1] + tar_path = os.path.join(abs_site_path, tar_name) + + try: + if file_path.endswith(".tar"): + subprocess.check_output(["tar", "xvf", tar_path, "--strip", "2"], cwd=abs_site_path) + elif file_path.endswith(".tgz"): + subprocess.check_output(["tar", "zxvf", tar_path, "--strip", "2"], cwd=abs_site_path) + except Exception: + raise + finally: + xhiveframework.destroy() + + return tar_path + + +def is_downgrade(sql_file_path, verbose=False): + """Check if input db backup will get downgraded on current bench + + This function is only tested with mariadb. + TODO: Add postgres support + """ + if xhiveframework.conf.db_type != "mariadb": + return False + + backup_version = get_backup_version(sql_file_path) or get_old_backup_version(sql_file_path) + current_version = Version(xhiveframework.__version__) + + # Assume it's not a downgrade if we can't determine backup version + if backup_version is None: + return False + + is_downgrade = backup_version > current_version + + if verbose and is_downgrade: + print(f"Your site is currently on XhiveFramework {current_version} and your backup is {backup_version}.") + + return is_downgrade + + +def get_old_backup_version(sql_file_path: str) -> Version | None: + """Return the xhiveframework version used to create the specified database dump. + + This methods supports older versions of XhiveFramework wich used a different format. + """ + header = get_db_dump_header(sql_file_path).split("\n") + if match := re.search(r"XhiveFramework (\d+\.\d+\.\d+)", header[0]): + backup_version = match[1] + + return Version(backup_version) if backup_version else None + + +def get_backup_version(sql_file_path: str) -> Version | None: + """Return the xhiveframework version used to create the specified database dump.""" + header = get_db_dump_header(sql_file_path).split("\n") + metadata = "" + if "begin xhiveframework metadata" in header[0]: + for line in header[1:]: + if "end xhiveframework metadata" in line: + break + metadata += line.replace("--", "").strip() + "\n" + parser = configparser.ConfigParser() + parser.read_string(metadata) + return Version(parser["xhiveframework"]["version"]) + + return None + + +def is_partial(sql_file_path: str) -> bool: + """ + Function to return whether the database dump is a partial backup or not + + :param sql_file_path: path to the database dump file + :return: True if the database dump is a partial backup, False otherwise + """ + header = get_db_dump_header(sql_file_path) + return "Partial Backup" in header + + +def partial_restore(sql_file_path, verbose=False): + if xhiveframework.conf.db_type == "mariadb": + from xhiveframework.database.mariadb.setup_db import import_db_from_sql + elif xhiveframework.conf.db_type == "postgres": + import warnings + + from xhiveframework.database.postgres.setup_db import import_db_from_sql + + warn = click.style( + "Delete the tables you want to restore manually before attempting" + " partial restore operation for PostreSQL databases", + fg="yellow", + ) + warnings.warn(warn, stacklevel=1) + else: + click.secho("Unsupported database type", fg="red") + return + + import_db_from_sql(source_sql=sql_file_path, verbose=verbose) + + +def validate_database_sql(path: str, _raise: bool = True) -> None: + """Check if file has contents and if `__Auth` table exists + + Args: + path (str): Path of the decompressed SQL file + _raise (bool, optional): Raise exception if invalid file. Defaults to True. + """ + + if path.endswith(".gz"): + executable_name = "zgrep" + else: + executable_name = "grep" + + if os.path.getsize(path): + if (executable := which(executable_name)) is None: + xhiveframework.throw( + f"`{executable_name}` not found in PATH! This is required to take a backup.", + exc=xhiveframework.ExecutableNotFound, + ) + try: + xhiveframework.utils.execute_in_shell(f"{executable} -m1 __Auth {path}", check_exit_code=True) + return + except Exception: + error_message = "Table `__Auth` not found in file." + else: + error_message = f"{path} is an empty file!" + + if error_message: + click.secho(error_message, fg="red") + + if _raise: + raise xhiveframework.InvalidDatabaseFile + + +def get_db_dump_header(file_path: str, file_bytes: int = 256) -> str: + """ + Get the header of a database dump file + + :param file_path: path to the database dump file + :param file_bytes: number of bytes to read from the file + :return: The first few bytes of the file as requested + """ + + # Use `gzip` to open the file if the extension is `.gz` + if file_path.endswith(".gz"): + with gzip.open(file_path, "rb") as f: + return f.read(file_bytes).decode() + + with open(file_path, "rb") as f: + return f.read(file_bytes).decode() diff --git a/xhiveframework/integrations/__init__.py b/xhiveframework/integrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/__init__.py b/xhiveframework/integrations/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/connected_app/__init__.py b/xhiveframework/integrations/doctype/connected_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/connected_app/connected_app.js b/xhiveframework/integrations/doctype/connected_app/connected_app.js new file mode 100644 index 0000000..540003a --- /dev/null +++ b/xhiveframework/integrations/doctype/connected_app/connected_app.js @@ -0,0 +1,38 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Connected App", { + refresh: (frm) => { + frm.add_custom_button(__("Get OpenID Configuration"), async () => { + if (!frm.doc.openid_configuration) { + xhiveframework.msgprint(__("Please enter OpenID Configuration URL")); + } else { + try { + const response = await fetch(frm.doc.openid_configuration); + const oidc = await response.json(); + frm.set_value("authorization_uri", oidc.authorization_endpoint); + frm.set_value("token_uri", oidc.token_endpoint); + frm.set_value("userinfo_uri", oidc.userinfo_endpoint); + frm.set_value("introspection_uri", oidc.introspection_endpoint); + frm.set_value("revocation_uri", oidc.revocation_endpoint); + } catch (error) { + xhiveframework.msgprint(__("Please check OpenID Configuration URL")); + } + } + }); + + if (!frm.is_new()) { + frm.add_custom_button(__("Connect to {}", [frm.doc.provider_name]), async () => { + xhiveframework.call({ + method: "initiate_web_application_flow", + doc: frm.doc, + callback: function (r) { + window.open(r.message, "_blank"); + }, + }); + }); + } + + frm.toggle_display("sb_client_credentials_section", !frm.is_new()); + }, +}); diff --git a/xhiveframework/integrations/doctype/connected_app/connected_app.json b/xhiveframework/integrations/doctype/connected_app/connected_app.json new file mode 100644 index 0000000..b66cd90 --- /dev/null +++ b/xhiveframework/integrations/doctype/connected_app/connected_app.json @@ -0,0 +1,169 @@ +{ + "actions": [], + "beta": 1, + "creation": "2019-01-24 15:51:06.362222", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "provider_name", + "cb_00", + "openid_configuration", + "sb_client_credentials_section", + "client_id", + "redirect_uri", + "cb_01", + "client_secret", + "sb_scope_section", + "scopes", + "sb_endpoints_section", + "authorization_uri", + "token_uri", + "revocation_uri", + "cb_02", + "userinfo_uri", + "introspection_uri", + "section_break_18", + "query_parameters" + ], + "fields": [ + { + "fieldname": "provider_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Provider Name", + "reqd": 1 + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "fieldname": "openid_configuration", + "fieldtype": "Data", + "label": "OpenID Configuration" + }, + { + "collapsible": 1, + "fieldname": "sb_client_credentials_section", + "fieldtype": "Section Break", + "label": "Client Credentials" + }, + { + "fieldname": "client_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Client Id", + "mandatory_depends_on": "eval:doc.redirect_uri" + }, + { + "fieldname": "redirect_uri", + "fieldtype": "Data", + "label": "Redirect URI", + "read_only": 1 + }, + { + "fieldname": "cb_01", + "fieldtype": "Column Break" + }, + { + "fieldname": "client_secret", + "fieldtype": "Password", + "label": "Client Secret" + }, + { + "collapsible": 1, + "fieldname": "sb_scope_section", + "fieldtype": "Section Break", + "label": "Scopes" + }, + { + "collapsible": 1, + "fieldname": "sb_endpoints_section", + "fieldtype": "Section Break", + "label": "Endpoints" + }, + { + "fieldname": "cb_02", + "fieldtype": "Column Break" + }, + { + "fieldname": "scopes", + "fieldtype": "Table", + "label": "Scopes", + "options": "OAuth Scope" + }, + { + "fieldname": "authorization_uri", + "fieldtype": "Small Text", + "label": "Authorization URI", + "mandatory_depends_on": "eval:doc.redirect_uri" + }, + { + "fieldname": "token_uri", + "fieldtype": "Data", + "label": "Token URI", + "mandatory_depends_on": "eval:doc.redirect_uri" + }, + { + "fieldname": "revocation_uri", + "fieldtype": "Data", + "label": "Revocation URI" + }, + { + "fieldname": "userinfo_uri", + "fieldtype": "Data", + "label": "Userinfo URI" + }, + { + "fieldname": "introspection_uri", + "fieldtype": "Data", + "label": "Introspection URI" + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "label": "Extra Parameters" + }, + { + "fieldname": "query_parameters", + "fieldtype": "Table", + "label": "Query Parameters", + "options": "Query Parameters" + } + ], + "links": [ + { + "link_doctype": "Token Cache", + "link_fieldname": "connected_app" + } + ], + "modified": "2022-01-07 05:28:45.073041", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Connected App", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "All" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "provider_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/connected_app/connected_app.py b/xhiveframework/integrations/doctype/connected_app/connected_app.py new file mode 100644 index 0000000..5969c54 --- /dev/null +++ b/xhiveframework/integrations/doctype/connected_app/connected_app.py @@ -0,0 +1,193 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import os +from urllib.parse import urlencode, urljoin + +from requests_oauthlib import OAuth2Session + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + +if any((os.getenv("CI"), xhiveframework.conf.developer_mode, xhiveframework.conf.allow_tests)): + # Disable mandatory TLS in developer mode and tests + os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" + +os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1" + + +class ConnectedApp(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.integrations.doctype.oauth_scope.oauth_scope import OAuthScope + from xhiveframework.integrations.doctype.query_parameters.query_parameters import QueryParameters + from xhiveframework.types import DF + + authorization_uri: DF.SmallText | None + client_id: DF.Data | None + client_secret: DF.Password | None + introspection_uri: DF.Data | None + openid_configuration: DF.Data | None + provider_name: DF.Data + query_parameters: DF.Table[QueryParameters] + redirect_uri: DF.Data | None + revocation_uri: DF.Data | None + scopes: DF.Table[OAuthScope] + token_uri: DF.Data | None + userinfo_uri: DF.Data | None + # end: auto-generated types + """Connect to a remote oAuth Server. Retrieve and store user's access token + in a Token Cache. + """ + + def validate(self): + base_url = xhiveframework.utils.get_url() + callback_path = ( + "/api/method/xhiveframework.integrations.doctype.connected_app.connected_app.callback/" + self.name + ) + self.redirect_uri = urljoin(base_url, callback_path) + + def get_oauth2_session(self, user=None, init=False): + """Return an auto-refreshing OAuth2 session which is an extension of a requests.Session()""" + token = None + token_updater = None + auto_refresh_kwargs = None + + if not init: + user = user or xhiveframework.session.user + token_cache = self.get_user_token(user) + token = token_cache.get_json() + token_updater = token_cache.update_data + auto_refresh_kwargs = {"client_id": self.client_id} + client_secret = self.get_password("client_secret") + if client_secret: + auto_refresh_kwargs["client_secret"] = client_secret + + return OAuth2Session( + client_id=self.client_id, + token=token, + token_updater=token_updater, + auto_refresh_url=self.token_uri, + auto_refresh_kwargs=auto_refresh_kwargs, + redirect_uri=self.redirect_uri, + scope=self.get_scopes(), + ) + + @xhiveframework.whitelist() + def initiate_web_application_flow(self, user=None, success_uri=None): + """Return an authorization URL for the user. Save state in Token Cache.""" + user = user or xhiveframework.session.user + oauth = self.get_oauth2_session(user, init=True) + query_params = self.get_query_params() + authorization_url, state = oauth.authorization_url(self.authorization_uri, **query_params) + token_cache = self.get_token_cache(user) + + if not token_cache: + token_cache = xhiveframework.new_doc("Token Cache") + token_cache.user = user + token_cache.connected_app = self.name + + token_cache.success_uri = success_uri + token_cache.state = state + token_cache.save(ignore_permissions=True) + xhiveframework.db.commit() + + return authorization_url + + def get_user_token(self, user=None, success_uri=None): + """Return an existing user token or initiate a Web Application Flow.""" + user = user or xhiveframework.session.user + token_cache = self.get_token_cache(user) + + if token_cache: + return token_cache + + redirect = self.initiate_web_application_flow(user, success_uri) + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = redirect + return redirect + + def get_token_cache(self, user): + token_cache = None + token_cache_name = self.name + "-" + user + + if xhiveframework.db.exists("Token Cache", token_cache_name): + token_cache = xhiveframework.get_doc("Token Cache", token_cache_name) + + return token_cache + + def get_scopes(self): + return [row.scope for row in self.scopes] + + def get_query_params(self): + return {param.key: param.value for param in self.query_parameters} + + def get_active_token(self, user=None): + user = user or xhiveframework.session.user + token_cache = self.get_token_cache(user) + if token_cache and token_cache.is_expired(): + oauth_session = self.get_oauth2_session(user) + + try: + token = oauth_session.refresh_token( + body=f"redirect_uri={self.redirect_uri}", + token_url=self.token_uri, + ) + except Exception: + self.log_error("Token Refresh Error") + return None + + token_cache.update_data(token) + + return token_cache + + +@xhiveframework.whitelist(methods=["GET"], allow_guest=True) +def callback(code=None, state=None): + """Handle client's code. + + Called during the oauthorization flow by the remote oAuth2 server to + transmit a code that can be used by the local server to obtain an access + token. + """ + + if xhiveframework.session.user == "Guest": + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = "/login?" + urlencode({"redirect-to": xhiveframework.request.url}) + return + + path = xhiveframework.request.path[1:].split("/") + if len(path) != 4 or not path[3]: + xhiveframework.throw(_("Invalid Parameters.")) + + connected_app = xhiveframework.get_doc("Connected App", path[3]) + token_cache = xhiveframework.get_doc("Token Cache", connected_app.name + "-" + xhiveframework.session.user) + + if state != token_cache.state: + xhiveframework.throw(_("Invalid state.")) + + oauth_session = connected_app.get_oauth2_session(init=True) + query_params = connected_app.get_query_params() + token = oauth_session.fetch_token( + connected_app.token_uri, + code=code, + client_secret=connected_app.get_password("client_secret"), + include_client_id=True, + **query_params, + ) + token_cache.update_data(token) + + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = token_cache.get("success_uri") or connected_app.get_url() + + +@xhiveframework.whitelist() +def has_token(connected_app, connected_user=None): + app = xhiveframework.get_doc("Connected App", connected_app) + token_cache = app.get_token_cache(connected_user or xhiveframework.session.user) + return bool(token_cache and token_cache.get_password("access_token", False)) diff --git a/xhiveframework/integrations/doctype/connected_app/test_connected_app.py b/xhiveframework/integrations/doctype/connected_app/test_connected_app.py new file mode 100644 index 0000000..fe43ac8 --- /dev/null +++ b/xhiveframework/integrations/doctype/connected_app/test_connected_app.py @@ -0,0 +1,148 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +from urllib.parse import urljoin + +import requests + +import xhiveframework +from xhiveframework.integrations.doctype.social_login_key.test_social_login_key import ( + create_or_update_social_login_key, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +def get_user(usr, pwd): + user = xhiveframework.new_doc("User") + user.email = usr + user.enabled = 1 + user.first_name = "_Test" + user.new_password = pwd + user.roles = [] + user.append("roles", {"doctype": "Has Role", "parentfield": "roles", "role": "System Manager"}) + user.insert() + + return user + + +def get_connected_app(): + doctype = "Connected App" + connected_app = xhiveframework.new_doc(doctype) + connected_app.provider_name = "xhiveframework" + connected_app.scopes = [] + connected_app.append("scopes", {"scope": "all"}) + connected_app.insert() + + return connected_app + + +def get_oauth_client(): + oauth_client = xhiveframework.new_doc("OAuth Client") + oauth_client.app_name = "_Test Connected App" + oauth_client.redirect_uris = "to be replaced" + oauth_client.default_redirect_uri = "to be replaced" + oauth_client.grant_type = "Authorization Code" + oauth_client.response_type = "Code" + oauth_client.skip_authorization = 1 + oauth_client.insert() + + return oauth_client + + +class TestConnectedApp(XhiveFrameworkTestCase): + def setUp(self): + """Set up a Connected App that connects to our own oAuth provider. + + XhiveFramework comes with it's own oAuth2 provider that we can test against. The + client credentials can be obtained from an "OAuth Client". All depends + on "Social Login Key" so we create one as well. + + The redirect URIs from "Connected App" and "OAuth Client" have to match. + XhiveFramework's "Authorization URL" and "Access Token URL" (actually they're + just endpoints) are stored in "Social Login Key" so we get them from + there. + """ + self.user_name = "test-connected-app@example.com" + self.user_password = "Eastern_43A1W" + + self.user = get_user(self.user_name, self.user_password) + self.connected_app = get_connected_app() + self.oauth_client = get_oauth_client() + social_login_key = create_or_update_social_login_key() + self.base_url = social_login_key.get("base_url") + + xhiveframework.db.commit() + self.connected_app.reload() + self.oauth_client.reload() + + redirect_uri = self.connected_app.get("redirect_uri") + self.oauth_client.update({"redirect_uris": redirect_uri, "default_redirect_uri": redirect_uri}) + self.oauth_client.save() + + self.connected_app.update( + { + "authorization_uri": urljoin(self.base_url, social_login_key.get("authorize_url")), + "client_id": self.oauth_client.get("client_id"), + "client_secret": self.oauth_client.get("client_secret"), + "token_uri": urljoin(self.base_url, social_login_key.get("access_token_url")), + } + ) + self.connected_app.save() + + xhiveframework.db.commit() + self.connected_app.reload() + self.oauth_client.reload() + + def test_web_application_flow(self): + """Simulate a logged in user who opens the authorization URL.""" + + def login(): + return session.get( + urljoin(self.base_url, "/api/method/login"), + params={"usr": self.user_name, "pwd": self.user_password}, + ) + + session = requests.Session() + + first_login = login() + self.assertEqual(first_login.status_code, 200) + + authorization_url = self.connected_app.initiate_web_application_flow(user=self.user_name) + + auth_response = session.get(authorization_url) + self.assertEqual(auth_response.status_code, 200) + + callback_response = session.get(auth_response.url) + self.assertEqual(callback_response.status_code, 200) + + self.token_cache = self.connected_app.get_token_cache(self.user_name) + token = self.token_cache.get_password("access_token") + self.assertNotEqual(token, None) + + oauth2_session = self.connected_app.get_oauth2_session(self.user_name) + resp = oauth2_session.get(urljoin(self.base_url, "/api/method/xhiveframework.auth.get_logged_user")) + self.assertEqual(resp.json().get("message"), self.user_name) + + def tearDown(self): + def delete_if_exists(attribute): + doc = getattr(self, attribute, None) + if doc: + doc.delete(force=True) + + delete_if_exists("token_cache") + delete_if_exists("connected_app") + + if getattr(self, "oauth_client", None): + tokens = xhiveframework.get_all("OAuth Bearer Token", filters={"client": self.oauth_client.name}) + for token in tokens: + doc = xhiveframework.get_doc("OAuth Bearer Token", token.name) + doc.delete() + + codes = xhiveframework.get_all("OAuth Authorization Code", filters={"client": self.oauth_client.name}) + for code in codes: + doc = xhiveframework.get_doc("OAuth Authorization Code", code.name) + doc.delete() + + delete_if_exists("user") + delete_if_exists("oauth_client") + + xhiveframework.db.commit() diff --git a/xhiveframework/integrations/doctype/connected_app/test_records.json b/xhiveframework/integrations/doctype/connected_app/test_records.json new file mode 100644 index 0000000..d311f35 --- /dev/null +++ b/xhiveframework/integrations/doctype/connected_app/test_records.json @@ -0,0 +1,13 @@ +[ + { + "doctype": "Connected App", + "provider_name": "xhiveframework", + "client_id": "test_client_id", + "client_secret": "test_client_secret", + "scopes": [ + { + "scope": "all" + } + ] + } +] diff --git a/xhiveframework/integrations/doctype/dropbox_settings/__init__.py b/xhiveframework/integrations/doctype/dropbox_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.js b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.js new file mode 100644 index 0000000..ceda15c --- /dev/null +++ b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.js @@ -0,0 +1,47 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Dropbox Settings", { + refresh: function (frm) { + frm.toggle_display( + ["app_access_key", "app_secret_key"], + !frm.doc.__onload?.dropbox_setup_via_site_config + ); + frm.events.take_backup(frm); + }, + + are_keys_present: function (frm) { + return ( + (frm.doc.app_access_key && frm.doc.app_secret_key) || + frm.doc.__onload?.dropbox_setup_via_site_config + ); + }, + + allow_dropbox_access: function (frm) { + if (!frm.events.are_keys_present(frm)) { + xhiveframework.msgprint(__("App Access Key and/or Secret Key are not present.")); + return; + } + + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.get_dropbox_authorize_url", + freeze: true, + callback: function (r) { + if (!r.exc) { + window.open(r.message.auth_url); + } + }, + }); + }, + + take_backup: function (frm) { + if (frm.doc.enabled && (frm.doc.dropbox_refresh_token || frm.doc.dropbox_access_token)) { + frm.add_custom_button(__("Take Backup Now"), function () { + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.take_backup", + freeze: true, + }); + }); + } + }, +}); diff --git a/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.json b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.json new file mode 100644 index 0000000..15535f0 --- /dev/null +++ b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.json @@ -0,0 +1,126 @@ +{ + "actions": [], + "creation": "2016-09-21 10:12:57.399174", + "doctype": "DocType", + "document_type": "System", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "send_notifications_to", + "send_email_for_successful_backup", + "backup_frequency", + "limit_no_of_backups", + "no_of_backups", + "file_backup", + "app_access_key", + "app_secret_key", + "allow_dropbox_access", + "dropbox_refresh_token", + "dropbox_access_token" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "send_notifications_to", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Send Notifications To", + "reqd": 1 + }, + { + "default": "1", + "description": "Note: By default emails for failed backups are sent.", + "fieldname": "send_email_for_successful_backup", + "fieldtype": "Check", + "label": "Send Email for Successful Backup" + }, + { + "fieldname": "backup_frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Backup Frequency", + "options": "\nDaily\nWeekly", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "limit_no_of_backups", + "fieldtype": "Check", + "label": "Limit Number of DB Backups" + }, + { + "default": "5", + "depends_on": "eval:doc.limit_no_of_backups", + "fieldname": "no_of_backups", + "fieldtype": "Int", + "label": "Number of DB Backups" + }, + { + "default": "1", + "fieldname": "file_backup", + "fieldtype": "Check", + "label": "File Backup" + }, + { + "fieldname": "app_access_key", + "fieldtype": "Data", + "label": "App Access Key" + }, + { + "fieldname": "app_secret_key", + "fieldtype": "Password", + "label": "App Secret Key" + }, + { + "fieldname": "allow_dropbox_access", + "fieldtype": "Button", + "label": "Allow Dropbox Access" + }, + { + "fieldname": "dropbox_refresh_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Dropbox Refresh Token", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "dropbox_access_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Dropbox Access Token" + } + ], + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-03-20 14:20:19.180611", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Dropbox Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.py b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.py new file mode 100644 index 0000000..4e11769 --- /dev/null +++ b/xhiveframework/integrations/doctype/dropbox_settings/dropbox_settings.py @@ -0,0 +1,378 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import os +from urllib.parse import parse_qs, urlparse + +import dropbox +from rq.timeouts import JobTimeoutException + +import xhiveframework +from xhiveframework import _ +from xhiveframework.integrations.offsite_backup_utils import ( + get_chunk_site, + get_latest_backup_file, + send_email, + validate_file_size, +) +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, encode, get_backups_path, get_files_path, get_request_site_address +from xhiveframework.utils.background_jobs import enqueue +from xhiveframework.utils.backups import new_backup + +ignore_list = [".DS_Store"] + + +class DropboxSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + app_access_key: DF.Data | None + app_secret_key: DF.Password | None + backup_frequency: DF.Literal["", "Daily", "Weekly"] + dropbox_access_token: DF.Password | None + dropbox_refresh_token: DF.Password | None + enabled: DF.Check + file_backup: DF.Check + limit_no_of_backups: DF.Check + no_of_backups: DF.Int + send_email_for_successful_backup: DF.Check + send_notifications_to: DF.Data + + # end: auto-generated types + def onload(self): + if not self.app_access_key and xhiveframework.conf.dropbox_access_key: + self.set_onload("dropbox_setup_via_site_config", 1) + + def validate(self): + if self.enabled and self.limit_no_of_backups and self.no_of_backups < 1: + xhiveframework.throw(_("Number of DB backups cannot be less than 1")) + + +@xhiveframework.whitelist() +def take_backup(): + """Enqueue longjob for taking backup to dropbox""" + enqueue( + "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.take_backup_to_dropbox", + queue="long", + timeout=1500, + ) + xhiveframework.msgprint(_("Queued for backup. It may take a few minutes to an hour.")) + + +def take_backups_daily(): + take_backups_if("Daily") + + +def take_backups_weekly(): + take_backups_if("Weekly") + + +def take_backups_if(freq): + if xhiveframework.db.get_single_value("Dropbox Settings", "backup_frequency") == freq: + take_backup_to_dropbox() + + +def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): + did_not_upload, error_log = [], [] + try: + if cint(xhiveframework.db.get_single_value("Dropbox Settings", "enabled")): + validate_file_size() + + did_not_upload, error_log = backup_to_dropbox(upload_db_backup) + if did_not_upload: + raise Exception + + if cint(xhiveframework.db.get_single_value("Dropbox Settings", "send_email_for_successful_backup")): + send_email(True, "Dropbox", "Dropbox Settings", "send_notifications_to") + except JobTimeoutException: + if retry_count < 2: + args = { + "retry_count": retry_count + 1, + "upload_db_backup": False, # considering till worker timeout db backup is uploaded + } + enqueue( + "xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.take_backup_to_dropbox", + queue="long", + timeout=1500, + **args, + ) + except Exception: + if isinstance(error_log, str): + error_message = error_log + "\n" + xhiveframework.get_traceback() + else: + file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log, strict=False)] + error_message = "\n".join(file_and_error) + "\n" + xhiveframework.get_traceback() + + send_email(False, "Dropbox", "Dropbox Settings", "send_notifications_to", error_message) + + +def backup_to_dropbox(upload_db_backup=True): + # upload database + dropbox_settings = get_dropbox_settings() + dropbox_client = get_dropbox_client(dropbox_settings) + + if upload_db_backup: + if xhiveframework.flags.create_new_backup: + backup = new_backup(ignore_files=True) + filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf)) + else: + filename, site_config = get_latest_backup_file() + + upload_file_to_dropbox(filename, "/database", dropbox_client) + upload_file_to_dropbox(site_config, "/database", dropbox_client) + + # delete older databases + if dropbox_settings["no_of_backups"]: + delete_older_backups(dropbox_client, "/database", dropbox_settings["no_of_backups"]) + + # upload files to files folder + did_not_upload = [] + error_log = [] + + if dropbox_settings["file_backup"]: + upload_from_folder(get_files_path(), 0, "/files", dropbox_client, did_not_upload, error_log) + upload_from_folder( + get_files_path(is_private=1), 1, "/private/files", dropbox_client, did_not_upload, error_log + ) + + return did_not_upload, list(set(error_log)) + + +def upload_from_folder(path, is_private, dropbox_folder, dropbox_client, did_not_upload, error_log): + if not os.path.exists(path): + return + + if is_fresh_upload(): + response = get_uploaded_files_meta(dropbox_folder, dropbox_client) + else: + response = xhiveframework._dict({"entries": []}) + + path = str(path) + + for f in xhiveframework.get_all( + "File", + filters={"is_folder": 0, "is_private": is_private, "uploaded_to_dropbox": 0}, + fields=["file_url", "name", "file_name"], + ): + if not f.file_url: + continue + filename = f.file_url.rsplit("/", 1)[-1] + + filepath = os.path.join(path, filename) + + if filename in ignore_list: + continue + + found = False + for file_metadata in response.entries: + try: + if os.path.basename(filepath) == file_metadata.name and os.stat( + encode(filepath) + ).st_size == int(file_metadata.size): + found = True + update_file_dropbox_status(f.name) + break + except Exception: + error_log.append(xhiveframework.get_traceback()) + + if not found: + try: + upload_file_to_dropbox(filepath, dropbox_folder, dropbox_client) + update_file_dropbox_status(f.name) + except Exception: + did_not_upload.append(filepath) + error_log.append(xhiveframework.get_traceback()) + + +def upload_file_to_dropbox(filename, folder, dropbox_client): + """upload files with chunk of 15 mb to reduce session append calls""" + if not os.path.exists(filename): + return + + create_folder_if_not_exists(folder, dropbox_client) + file_size = os.path.getsize(encode(filename)) + chunk_size = get_chunk_site(file_size) + + mode = dropbox.files.WriteMode.overwrite + + f = open(encode(filename), "rb") + path = f"{folder}/{os.path.basename(filename)}" + + try: + if file_size <= chunk_size: + dropbox_client.files_upload(f.read(), path, mode) + else: + upload_session_start_result = dropbox_client.files_upload_session_start(f.read(chunk_size)) + cursor = dropbox.files.UploadSessionCursor( + session_id=upload_session_start_result.session_id, offset=f.tell() + ) + commit = dropbox.files.CommitInfo(path=path, mode=mode) + + while f.tell() < file_size: + if (file_size - f.tell()) <= chunk_size: + dropbox_client.files_upload_session_finish(f.read(chunk_size), cursor, commit) + else: + dropbox_client.files_upload_session_append( + f.read(chunk_size), cursor.session_id, cursor.offset + ) + cursor.offset = f.tell() + except dropbox.exceptions.ApiError as e: + if isinstance(e.error, dropbox.files.UploadError): + error = f"File Path: {path}\n" + error += xhiveframework.get_traceback() + xhiveframework.log_error(error) + else: + raise + + +def create_folder_if_not_exists(folder, dropbox_client): + try: + dropbox_client.files_get_metadata(folder) + except dropbox.exceptions.ApiError as e: + # folder not found + if isinstance(e.error, dropbox.files.GetMetadataError): + dropbox_client.files_create_folder(folder) + else: + raise + + +def update_file_dropbox_status(file_name): + xhiveframework.db.set_value("File", file_name, "uploaded_to_dropbox", 1, update_modified=False) + + +def is_fresh_upload(): + file_name = xhiveframework.db.get_value("File", {"uploaded_to_dropbox": 1}, "name") + return not file_name + + +def get_uploaded_files_meta(dropbox_folder, dropbox_client): + try: + return dropbox_client.files_list_folder(dropbox_folder) + except dropbox.exceptions.ApiError as e: + # folder not found + if isinstance(e.error, dropbox.files.ListFolderError): + return xhiveframework._dict({"entries": []}) + raise + + +def get_dropbox_client(dropbox_settings): + dropbox_client = dropbox.Dropbox( + oauth2_access_token=dropbox_settings["access_token"], + oauth2_refresh_token=dropbox_settings["refresh_token"], + app_key=dropbox_settings["app_key"], + app_secret=dropbox_settings["app_secret"], + timeout=None, + ) + + # checking if the access token has expired + dropbox_client.files_list_folder("") + if dropbox_settings["access_token"] != dropbox_client._oauth2_access_token: + set_dropbox_token(dropbox_client._oauth2_access_token) + + return dropbox_client + + +def get_dropbox_settings(redirect_uri=False): + # NOTE: access token is kept for legacy dropbox apps + settings = xhiveframework.get_doc("Dropbox Settings") + app_details = { + "app_key": settings.app_access_key or xhiveframework.conf.dropbox_access_key, + "app_secret": settings.get_password(fieldname="app_secret_key", raise_exception=False) + if settings.app_secret_key + else xhiveframework.conf.dropbox_secret_key, + "refresh_token": settings.get_password("dropbox_refresh_token", raise_exception=False), + "access_token": settings.get_password("dropbox_access_token", raise_exception=False), + "file_backup": settings.file_backup, + "no_of_backups": settings.no_of_backups if settings.limit_no_of_backups else None, + } + + if redirect_uri: + app_details.update( + { + "redirect_uri": get_request_site_address(True) + + "/api/method/xhiveframework.integrations.doctype.dropbox_settings.dropbox_settings.dropbox_auth_finish" + } + ) + + if not (app_details["app_key"] and app_details["app_secret"]): + raise Exception(_("Please set Dropbox access keys in site config or doctype")) + + return app_details + + +def delete_older_backups(dropbox_client, folder_path, to_keep): + res = dropbox_client.files_list_folder(path=folder_path) + files = [f for f in res.entries if isinstance(f, dropbox.files.FileMetadata) and "sql" in f.name] + + if len(files) <= to_keep: + return + + files.sort(key=lambda item: item.client_modified, reverse=True) + for f in files[to_keep:]: + dropbox_client.files_delete(os.path.join(folder_path, f.name)) + + +@xhiveframework.whitelist() +def get_dropbox_authorize_url(): + app_details = get_dropbox_settings(redirect_uri=True) + dropbox_oauth_flow = dropbox.DropboxOAuth2Flow( + consumer_key=app_details["app_key"], + redirect_uri=app_details["redirect_uri"], + session={}, + csrf_token_session_key="dropbox-auth-csrf-token", + consumer_secret=app_details["app_secret"], + token_access_type="offline", + ) + + auth_url = dropbox_oauth_flow.start() + + return {"auth_url": auth_url, "args": parse_qs(urlparse(auth_url).query)} + + +@xhiveframework.whitelist() +def dropbox_auth_finish(): + app_details = get_dropbox_settings(redirect_uri=True) + callback = xhiveframework.form_dict + close = '

      ' + _("Please close this window") + "

      " + + if not callback.state or not callback.code: + xhiveframework.respond_as_web_page( + _("Dropbox Setup"), + _("Illegal Access Token. Please try again") + close, + indicator_color="red", + http_status_code=xhiveframework.AuthenticationError.http_status_code, + ) + return + + dropbox_oauth_flow = dropbox.DropboxOAuth2Flow( + consumer_key=app_details["app_key"], + redirect_uri=app_details["redirect_uri"], + session={"dropbox-auth-csrf-token": callback.state}, + csrf_token_session_key="dropbox-auth-csrf-token", + consumer_secret=app_details["app_secret"], + ) + + token = dropbox_oauth_flow.finish({"state": callback.state, "code": callback.code}) + set_dropbox_token(token.access_token, token.refresh_token) + + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = "/app/dropbox-settings" + + +def set_dropbox_token(access_token, refresh_token=None): + # NOTE: used doc object instead of db.set_value so that password field is set properly + dropbox_settings = xhiveframework.get_single("Dropbox Settings") + dropbox_settings.dropbox_access_token = access_token + if refresh_token: + dropbox_settings.dropbox_refresh_token = refresh_token + + dropbox_settings.save() + + xhiveframework.db.commit() diff --git a/xhiveframework/integrations/doctype/dropbox_settings/test_dropbox_settings.py b/xhiveframework/integrations/doctype/dropbox_settings/test_dropbox_settings.py new file mode 100644 index 0000000..833e82f --- /dev/null +++ b/xhiveframework/integrations/doctype/dropbox_settings/test_dropbox_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestDropboxSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/google_calendar/__init__.py b/xhiveframework/integrations/doctype/google_calendar/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/google_calendar/google_calendar.js b/xhiveframework/integrations/doctype/google_calendar/google_calendar.js new file mode 100644 index 0000000..03c0dfb --- /dev/null +++ b/xhiveframework/integrations/doctype/google_calendar/google_calendar.js @@ -0,0 +1,67 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Google Calendar", { + refresh: function (frm) { + if (frm.is_new()) { + frm.dashboard.set_headline( + __("To use Google Calendar, enable {0}.", [ + `${__("Google Settings")}`, + ]) + ); + } + + xhiveframework.realtime.on("import_google_calendar", (data) => { + if (data.progress) { + frm.dashboard.show_progress( + "Syncing Google Calendar", + (data.progress / data.total) * 100, + __("Syncing {0} of {1}", [data.progress, data.total]) + ); + if (data.progress === data.total) { + frm.dashboard.hide_progress("Syncing Google Calendar"); + } + } + }); + + if (frm.doc.refresh_token) { + frm.add_custom_button(__("Sync Calendar"), function () { + xhiveframework.show_alert({ + indicator: "green", + message: __("Syncing"), + }); + xhiveframework + .call({ + method: "xhiveframework.integrations.doctype.google_calendar.google_calendar.sync", + args: { + g_calendar: frm.doc.name, + }, + }) + .then((r) => { + xhiveframework.hide_progress(); + xhiveframework.msgprint(r.message); + }); + }); + } + }, + authorize_google_calendar_access: function (frm) { + let reauthorize = 0; + if (frm.doc.authorization_code) { + reauthorize = 1; + } + + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.google_calendar.google_calendar.authorize_access", + args: { + g_calendar: frm.doc.name, + reauthorize: reauthorize, + }, + callback: function (r) { + if (!r.exc) { + frm.save(); + window.open(r.message.url); + } + }, + }); + }, +}); diff --git a/xhiveframework/integrations/doctype/google_calendar/google_calendar.json b/xhiveframework/integrations/doctype/google_calendar/google_calendar.json new file mode 100644 index 0000000..102569e --- /dev/null +++ b/xhiveframework/integrations/doctype/google_calendar/google_calendar.json @@ -0,0 +1,150 @@ +{ + "actions": [], + "autoname": "field:calendar_name", + "creation": "2019-07-06 17:54:09.450100", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable", + "sb_00", + "calendar_name", + "user", + "authorize_google_calendar_access", + "sb_01", + "pull_from_google_calendar", + "cb_01", + "push_to_google_calendar", + "section_break_3", + "google_calendar_id", + "refresh_token", + "authorization_code", + "next_sync_token" + ], + "fields": [ + { + "default": "1", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + }, + { + "depends_on": "eval: doc.enable", + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Google Calendar" + }, + { + "fieldname": "user", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User", + "options": "User", + "reqd": 1 + }, + { + "description": "The name that will appear in Google Calendar", + "fieldname": "calendar_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Calendar Name", + "reqd": 1, + "unique": 1 + }, + { + "depends_on": "eval: doc.enable", + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "fieldname": "refresh_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Refresh Token" + }, + { + "fieldname": "authorization_code", + "fieldtype": "Password", + "hidden": 1, + "label": "Authorization Code" + }, + { + "fieldname": "next_sync_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Next Sync Token" + }, + { + "fieldname": "google_calendar_id", + "fieldtype": "Data", + "label": "Google Calendar ID", + "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "authorize_google_calendar_access", + "fieldtype": "Button", + "label": "Authorize Google Calendar Access" + }, + { + "depends_on": "eval: doc.enable", + "fieldname": "sb_01", + "fieldtype": "Section Break", + "label": "Sync" + }, + { + "default": "1", + "fieldname": "pull_from_google_calendar", + "fieldtype": "Check", + "label": "Pull from Google Calendar" + }, + { + "fieldname": "cb_01", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "push_to_google_calendar", + "fieldtype": "Check", + "label": "Push to Google Calendar" + } + ], + "links": [], + "modified": "2023-08-28 22:21:44.238862", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Google Calendar", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/google_calendar/google_calendar.py b/xhiveframework/integrations/doctype/google_calendar/google_calendar.py new file mode 100644 index 0000000..6e72bef --- /dev/null +++ b/xhiveframework/integrations/doctype/google_calendar/google_calendar.py @@ -0,0 +1,871 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + + +from datetime import datetime, timedelta +from urllib.parse import quote +from zoneinfo import ZoneInfo + +import google.oauth2.credentials +import requests +from dateutil import parser +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +import xhiveframework +from xhiveframework import _ +from xhiveframework.integrations.google_oauth import GoogleOAuth +from xhiveframework.model.document import Document +from xhiveframework.utils import ( + add_days, + add_to_date, + get_datetime, + get_request_site_address, + get_system_timezone, + get_weekdays, + now_datetime, +) +from xhiveframework.utils.password import set_encrypted_password + +SCOPES = "https://www.googleapis.com/auth/calendar" + +google_calendar_frequencies = { + "RRULE:FREQ=DAILY": "Daily", + "RRULE:FREQ=WEEKLY": "Weekly", + "RRULE:FREQ=MONTHLY": "Monthly", + "RRULE:FREQ=YEARLY": "Yearly", +} + +google_calendar_days = { + "MO": "monday", + "TU": "tuesday", + "WE": "wednesday", + "TH": "thursday", + "FR": "friday", + "SA": "saturday", + "SU": "sunday", +} + +framework_frequencies = { + "Daily": "RRULE:FREQ=DAILY;", + "Weekly": "RRULE:FREQ=WEEKLY;", + "Monthly": "RRULE:FREQ=MONTHLY;", + "Yearly": "RRULE:FREQ=YEARLY;", +} + +framework_days = { + "monday": "MO", + "tuesday": "TU", + "wednesday": "WE", + "thursday": "TH", + "friday": "FR", + "saturday": "SA", + "sunday": "SU", +} + + +class GoogleCalendar(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + authorization_code: DF.Password | None + calendar_name: DF.Data + enable: DF.Check + google_calendar_id: DF.Data | None + next_sync_token: DF.Password | None + pull_from_google_calendar: DF.Check + push_to_google_calendar: DF.Check + refresh_token: DF.Password | None + user: DF.Link + + # end: auto-generated types + def validate(self): + google_settings = xhiveframework.get_single("Google Settings") + if not google_settings.enable: + xhiveframework.throw(_("Enable Google API in Google Settings.")) + + if not google_settings.client_id or not google_settings.client_secret: + xhiveframework.throw(_("Enter Client Id and Client Secret in Google Settings.")) + + return google_settings + + def get_access_token(self): + google_settings = self.validate() + + if not self.refresh_token: + button_label = xhiveframework.bold(_("Allow Google Calendar Access")) + raise xhiveframework.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + + data = { + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "refresh_token": self.get_password(fieldname="refresh_token", raise_exception=False), + "grant_type": "refresh_token", + "scope": SCOPES, + } + + try: + r = requests.post(GoogleOAuth.OAUTH_URL, data=data).json() + except requests.exceptions.HTTPError: + button_label = xhiveframework.bold(_("Allow Google Calendar Access")) + xhiveframework.throw( + _( + "Something went wrong during the token generation. Click on {0} to generate a new one." + ).format(button_label) + ) + + return r.get("access_token") + + +@xhiveframework.whitelist() +def authorize_access(g_calendar, reauthorize=None): + """ + If no Authorization code get it from Google and then request for Refresh Token. + Google Calendar Name is set to flags to set_value after Authorization Code is obtained. + """ + google_settings = xhiveframework.get_doc("Google Settings") + google_calendar = xhiveframework.get_doc("Google Calendar", g_calendar) + google_calendar.check_permission("write") + + redirect_uri = ( + get_request_site_address(True) + + "?cmd=xhiveframework.integrations.doctype.google_calendar.google_calendar.google_callback" + ) + + if not google_calendar.authorization_code or reauthorize: + xhiveframework.cache.hset("google_calendar", "google_calendar", google_calendar.name) + return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) + else: + try: + data = { + "code": google_calendar.get_password(fieldname="authorization_code", raise_exception=False), + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password( + fieldname="client_secret", raise_exception=False + ), + "redirect_uri": redirect_uri, + "grant_type": "authorization_code", + } + r = requests.post(GoogleOAuth.OAUTH_URL, data=data).json() + + if "refresh_token" in r: + xhiveframework.db.set_value( + "Google Calendar", google_calendar.name, "refresh_token", r.get("refresh_token") + ) + xhiveframework.db.commit() + + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = "/app/Form/{}/{}".format( + quote("Google Calendar"), quote(google_calendar.name) + ) + + xhiveframework.msgprint(_("Google Calendar has been configured.")) + except Exception as e: + xhiveframework.throw(e) + + +def get_authentication_url(client_id=None, redirect_uri=None): + return { + "url": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&prompt=consent&client_id={}&include_granted_scopes=true&scope={}&redirect_uri={}".format( + client_id, SCOPES, redirect_uri + ) + } + + +@xhiveframework.whitelist() +def google_callback(code=None): + """ + Authorization code is sent to callback as per the API configuration + """ + google_calendar = xhiveframework.cache.hget("google_calendar", "google_calendar") + xhiveframework.db.set_value("Google Calendar", google_calendar, "authorization_code", code) + xhiveframework.db.commit() + + authorize_access(google_calendar) + + +@xhiveframework.whitelist() +def sync(g_calendar=None): + filters = {"enable": 1} + + if g_calendar: + filters.update({"name": g_calendar}) + + google_calendars = xhiveframework.get_list("Google Calendar", filters=filters) + + for g in google_calendars: + return sync_events_from_google_calendar(g.name) + + +def get_google_calendar_object(g_calendar): + """ + Returns an object of Google Calendar along with Google Calendar doc. + """ + google_settings = xhiveframework.get_doc("Google Settings") + account = xhiveframework.get_doc("Google Calendar", g_calendar) + + credentials_dict = { + "token": account.get_access_token(), + "refresh_token": account.get_password(fieldname="refresh_token", raise_exception=False), + "token_uri": GoogleOAuth.OAUTH_URL, + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "scopes": [SCOPES], + } + + credentials = google.oauth2.credentials.Credentials(**credentials_dict) + google_calendar = build( + serviceName="calendar", version="v3", credentials=credentials, static_discovery=False + ) + + check_google_calendar(account, google_calendar) + + account.load_from_db() + return google_calendar, account + + +def check_google_calendar(account, google_calendar): + """ + Checks if Google Calendar is present with the specified name. + If not, creates one. + """ + account.load_from_db() + try: + if account.google_calendar_id: + google_calendar.calendars().get(calendarId=account.google_calendar_id).execute() + else: + # If no Calendar ID create a new Calendar + calendar = { + "summary": account.calendar_name, + "timeZone": xhiveframework.db.get_single_value("System Settings", "time_zone"), + } + created_calendar = google_calendar.calendars().insert(body=calendar).execute() + xhiveframework.db.set_value( + "Google Calendar", account.name, "google_calendar_id", created_calendar.get("id") + ) + xhiveframework.db.commit() + except HttpError as err: + xhiveframework.throw( + _("Google Calendar - Could not create Calendar for {0}, error code {1}.").format( + account.name, err.resp.status + ) + ) + + +def sync_events_from_google_calendar(g_calendar, method=None): + """ + Syncs Events from Google Calendar in Framework Calendar. + Google Calendar returns nextSyncToken when all the events in Google Calendar are fetched. + nextSyncToken is returned at the very last page + https://developers.google.com/calendar/v3/sync + """ + google_calendar, account = get_google_calendar_object(g_calendar) + + if not account.pull_from_google_calendar: + return + + sync_token = account.get_password(fieldname="next_sync_token", raise_exception=False) or None + events = xhiveframework._dict() + results = [] + while True: + try: + # API Response listed at EOF + events = ( + google_calendar.events() + .list( + calendarId=account.google_calendar_id, + maxResults=2000, + pageToken=events.get("nextPageToken"), + singleEvents=False, + showDeleted=True, + syncToken=sync_token, + ) + .execute() + ) + except HttpError as err: + msg = _("Google Calendar - Could not fetch event from Google Calendar, error code {0}.").format( + err.resp.status + ) + + if err.resp.status == 410: + set_encrypted_password("Google Calendar", account.name, "", "next_sync_token") + xhiveframework.db.commit() + msg += " " + _("Sync token was invalid and has been reset, Retry syncing.") + xhiveframework.msgprint(msg, title="Invalid Sync Token", indicator="blue") + else: + xhiveframework.throw(msg) + + results.extend(event for event in events.get("items", [])) + if not events.get("nextPageToken"): + if events.get("nextSyncToken"): + account.next_sync_token = events.get("nextSyncToken") + account.save() + break + + for idx, event in enumerate(results): + xhiveframework.publish_realtime( + "import_google_calendar", dict(progress=idx + 1, total=len(results)), user=xhiveframework.session.user + ) + + # If Google Calendar Event if confirmed, then create an Event + if event.get("status") == "confirmed": + recurrence = None + if event.get("recurrence"): + try: + recurrence = event.get("recurrence")[0] + except IndexError: + pass + + if not xhiveframework.db.exists("Event", {"google_calendar_event_id": event.get("id")}): + insert_event_to_calendar(account, event, recurrence) + else: + update_event_in_calendar(account, event, recurrence) + elif event.get("status") == "cancelled": + # If any synced Google Calendar Event is cancelled, then close the Event + xhiveframework.db.set_value( + "Event", + { + "google_calendar_id": account.google_calendar_id, + "google_calendar_event_id": event.get("id"), + }, + "status", + "Closed", + ) + xhiveframework.get_doc( + { + "doctype": "Comment", + "comment_type": "Info", + "reference_doctype": "Event", + "reference_name": xhiveframework.db.get_value( + "Event", + { + "google_calendar_id": account.google_calendar_id, + "google_calendar_event_id": event.get("id"), + }, + "name", + ), + "content": " - Event deleted from Google Calendar.", + } + ).insert(ignore_permissions=True) + else: + pass + + if not results: + return _("No Google Calendar Event to sync.") + elif len(results) == 1: + return _("1 Google Calendar Event synced.") + else: + return _("{0} Google Calendar Events synced.").format(len(results)) + + +def insert_event_to_calendar(account, event, recurrence=None): + """ + Inserts event in XhiveFramework Calendar during Sync + """ + calendar_event = { + "doctype": "Event", + "subject": event.get("summary"), + "description": event.get("description"), + "google_calendar_event": 1, + "google_calendar": account.name, + "google_calendar_id": account.google_calendar_id, + "google_calendar_event_id": event.get("id"), + "google_meet_link": event.get("hangoutLink"), + "pulled_from_google_calendar": 1, + } + calendar_event.update( + google_calendar_to_repeat_on(recurrence=recurrence, start=event.get("start"), end=event.get("end")) + ) + xhiveframework.get_doc(calendar_event).insert(ignore_permissions=True) + + +def update_event_in_calendar(account, event, recurrence=None): + """ + Updates Event in XhiveFramework Calendar if any existing Google Calendar Event is updated + """ + calendar_event = xhiveframework.get_doc("Event", {"google_calendar_event_id": event.get("id")}) + calendar_event.subject = event.get("summary") + calendar_event.description = event.get("description") + calendar_event.google_meet_link = event.get("hangoutLink") + calendar_event.update( + google_calendar_to_repeat_on(recurrence=recurrence, start=event.get("start"), end=event.get("end")) + ) + calendar_event.save(ignore_permissions=True) + + +def insert_event_in_google_calendar(doc, method=None): + """ + Insert Events in Google Calendar if sync_with_google_calendar is checked. + """ + if ( + not doc.sync_with_google_calendar + or doc.pulled_from_google_calendar + or not xhiveframework.db.exists("Google Calendar", {"name": doc.google_calendar}) + ): + return + + google_calendar, account = get_google_calendar_object(doc.google_calendar) + + if not account.push_to_google_calendar: + return + + event = {"summary": doc.subject, "description": doc.description, "google_calendar_event": 1} + event.update( + format_date_according_to_google_calendar( + doc.all_day, get_datetime(doc.starts_on), get_datetime(doc.ends_on) if doc.ends_on else None + ) + ) + + if doc.repeat_on: + event.update({"recurrence": repeat_on_to_google_calendar_recurrence_rule(doc)}) + + event.update({"attendees": get_attendees(doc)}) + + conference_data_version = 0 + + if doc.add_video_conferencing: + event.update({"conferenceData": get_conference_data(doc)}) + conference_data_version = 1 + + try: + event = ( + google_calendar.events() + .insert( + calendarId=doc.google_calendar_id, + body=event, + conferenceDataVersion=conference_data_version, + sendUpdates="all", + ) + .execute() + ) + + xhiveframework.db.set_value( + "Event", + doc.name, + {"google_calendar_event_id": event.get("id"), "google_meet_link": event.get("hangoutLink")}, + update_modified=False, + ) + + xhiveframework.msgprint(_("Event Synced with Google Calendar.")) + except HttpError as err: + xhiveframework.throw( + _("Google Calendar - Could not insert event in Google Calendar {0}, error code {1}.").format( + account.name, err.resp.status + ) + ) + + +def update_event_in_google_calendar(doc, method=None): + """ + Updates Events in Google Calendar if any existing event is modified in XhiveFramework Calendar + """ + # Workaround to avoid triggering updation when Event is being inserted since + # creation and modified are same when inserting doc + if ( + not doc.sync_with_google_calendar + or doc.modified == doc.creation + or not xhiveframework.db.exists("Google Calendar", {"name": doc.google_calendar}) + ): + return + + if doc.sync_with_google_calendar and not doc.google_calendar_event_id: + # If sync_with_google_calendar is checked later, then insert the event rather than updating it. + insert_event_in_google_calendar(doc) + return + + google_calendar, account = get_google_calendar_object(doc.google_calendar) + + if not account.push_to_google_calendar: + return + + try: + event = ( + google_calendar.events() + .get(calendarId=doc.google_calendar_id, eventId=doc.google_calendar_event_id) + .execute() + ) + + event["summary"] = doc.subject + event["description"] = doc.description + event["recurrence"] = repeat_on_to_google_calendar_recurrence_rule(doc) + event["status"] = ( + "cancelled" if doc.status == "Cancelled" or doc.status == "Closed" else event.get("status") + ) + event.update( + format_date_according_to_google_calendar( + doc.all_day, get_datetime(doc.starts_on), get_datetime(doc.ends_on) if doc.ends_on else None + ) + ) + + conference_data_version = 0 + + if doc.add_video_conferencing: + event.update({"conferenceData": get_conference_data(doc)}) + conference_data_version = 1 + elif doc.get_doc_before_save().add_video_conferencing or event.get("hangoutLink"): + # remove google meet from google calendar event, if turning off add_video_conferencing + event.update({"conferenceData": None}) + conference_data_version = 1 + + event.update({"attendees": get_attendees(doc)}) + + event = ( + google_calendar.events() + .update( + calendarId=doc.google_calendar_id, + eventId=doc.google_calendar_event_id, + body=event, + conferenceDataVersion=conference_data_version, + sendUpdates="all", + ) + .execute() + ) + + # if add_video_conferencing enabled or disabled during update, overwrite + xhiveframework.db.set_value( + "Event", + doc.name, + {"google_meet_link": event.get("hangoutLink")}, + update_modified=False, + ) + doc.notify_update() + + xhiveframework.msgprint(_("Event Synced with Google Calendar.")) + except HttpError as err: + xhiveframework.throw( + _("Google Calendar - Could not update Event {0} in Google Calendar, error code {1}.").format( + doc.name, err.resp.status + ) + ) + + +def delete_event_from_google_calendar(doc, method=None): + """ + Delete Events from Google Calendar if XhiveFramework Event is deleted. + """ + + if not xhiveframework.db.exists("Google Calendar", {"name": doc.google_calendar}): + return + + google_calendar, account = get_google_calendar_object(doc.google_calendar) + + if not account.push_to_google_calendar: + return + + try: + event = ( + google_calendar.events() + .get(calendarId=doc.google_calendar_id, eventId=doc.google_calendar_event_id) + .execute() + ) + event["recurrence"] = None + event["status"] = "cancelled" + + google_calendar.events().update( + calendarId=doc.google_calendar_id, eventId=doc.google_calendar_event_id, body=event + ).execute() + except HttpError as err: + xhiveframework.msgprint( + _("Google Calendar - Could not delete Event {0} from Google Calendar, error code {1}.").format( + doc.name, err.resp.status + ) + ) + + +def google_calendar_to_repeat_on(start, end, recurrence=None): + """ + recurrence is in the form ['RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH'] + has the frequency and then the days on which the event recurs + + Both have been mapped in a dict for easier mapping. + """ + repeat_on = { + "starts_on": ( + get_datetime(start.get("date")) + if start.get("date") + else parser.parse(start.get("dateTime")) + .astimezone(ZoneInfo(get_system_timezone())) + .replace(tzinfo=None) + ), + "ends_on": ( + get_datetime(end.get("date")) + if end.get("date") + else parser.parse(end.get("dateTime")) + .astimezone(ZoneInfo(get_system_timezone())) + .replace(tzinfo=None) + ), + "all_day": 1 if start.get("date") else 0, + "repeat_this_event": 1 if recurrence else 0, + "repeat_on": None, + "repeat_till": None, + "sunday": 0, + "monday": 0, + "tuesday": 0, + "wednesday": 0, + "thursday": 0, + "friday": 0, + "saturday": 0, + } + + # recurrence rule "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH" + if recurrence: + # google_calendar_frequency = RRULE:FREQ=WEEKLY, byday = BYDAY=MO,TU,TH, until = 20191028 + google_calendar_frequency, until, byday = get_recurrence_parameters(recurrence) + repeat_on["repeat_on"] = google_calendar_frequencies.get(google_calendar_frequency) + + if repeat_on["repeat_on"] == "Daily": + repeat_on["ends_on"] = None + repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + + if byday and repeat_on["repeat_on"] == "Weekly": + repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + byday = byday.split("=")[1].split(",") + for repeat_day in byday: + repeat_on[google_calendar_days[repeat_day]] = 1 + + if byday and repeat_on["repeat_on"] == "Monthly": + byday = byday.split("=")[1] + repeat_day_week_number, repeat_day_name = None, None + + for num in ["-2", "-1", "1", "2", "3", "4", "5"]: + if num in byday: + repeat_day_week_number = num + break + + for day in ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]: + if day in byday: + repeat_day_name = google_calendar_days.get(day) + break + + # Only Set starts_on for the event to repeat monthly + start_date = parse_google_calendar_recurrence_rule(int(repeat_day_week_number), repeat_day_name) + repeat_on["starts_on"] = start_date + repeat_on["ends_on"] = add_to_date(start_date, minutes=5) + repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + + if repeat_on["repeat_till"] == "Yearly": + repeat_on["ends_on"] = None + repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + + return repeat_on + + +def format_date_according_to_google_calendar(all_day, starts_on, ends_on=None): + if not ends_on: + ends_on = starts_on + timedelta(minutes=10) + + date_format = { + "start": { + "dateTime": starts_on.isoformat(), + "timeZone": get_system_timezone(), + }, + "end": { + "dateTime": ends_on.isoformat(), + "timeZone": get_system_timezone(), + }, + } + + if all_day: + # If all_day event, Google Calendar takes date as a parameter and not dateTime + date_format["start"].pop("dateTime") + date_format["end"].pop("dateTime") + + date_format["start"].update({"date": starts_on.date().isoformat()}) + date_format["end"].update({"date": ends_on.date().isoformat()}) + + return date_format + + +def parse_google_calendar_recurrence_rule(repeat_day_week_number, repeat_day_name): + """ + Returns (repeat_on) exact date for combination eg 4TH viz. 4th thursday of a month + """ + if repeat_day_week_number < 0: + # Consider a month with 5 weeks and event is to be repeated in last week of every month, google caledar considers + # a month has 4 weeks and hence itll return -1 for a month with 5 weeks. + repeat_day_week_number = 4 + + weekdays = get_weekdays() + current_date = now_datetime() + isset_day_name, isset_day_number = False, False + + # Set the proper day ie if recurrence is 4TH, then align the day to Thursday + while not isset_day_name: + isset_day_name = True if weekdays[current_date.weekday()].lower() == repeat_day_name else False + current_date = add_days(current_date, 1) if not isset_day_name else current_date + + # One the day is set to Thursday, now set the week number ie 4 + while not isset_day_number: + week_number = get_week_number(current_date) + isset_day_number = True if week_number == repeat_day_week_number else False + # check if current_date week number is greater or smaller than repeat_day week number + weeks = 1 if week_number < repeat_day_week_number else -1 + current_date = add_to_date(current_date, weeks=weeks) if not isset_day_number else current_date + + return current_date + + +def repeat_on_to_google_calendar_recurrence_rule(doc): + """ + Returns event (repeat_on) in Google Calendar format ie RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH + """ + recurrence = framework_frequencies.get(doc.repeat_on) + weekdays = get_weekdays() + + if doc.repeat_on == "Weekly": + byday = [framework_days.get(day.lower()) for day in weekdays if doc.get(day.lower())] + recurrence = recurrence + "BYDAY=" + ",".join(byday) + elif doc.repeat_on == "Monthly": + week_number = str(get_week_number(get_datetime(doc.starts_on))) + week_day = weekdays[get_datetime(doc.starts_on).weekday()].lower() + recurrence = recurrence + "BYDAY=" + week_number + framework_days.get(week_day) + + return [recurrence] + + +def get_week_number(dt): + """ + Returns the week number of the month for the specified date. + https://stackoverflow.com/questions/3806473/python-week-number-of-the-month/16804556 + """ + from math import ceil + + first_day = dt.replace(day=1) + + dom = dt.day + adjusted_dom = dom + first_day.weekday() + + return int(ceil(adjusted_dom / 7.0)) + + +def get_recurrence_parameters(recurrence): + recurrence = recurrence.split(";") + frequency, until, byday = None, None, None + + for r in recurrence: + if "RRULE:FREQ" in r: + frequency = r + elif "UNTIL" in r: + until = r + elif "BYDAY" in r: + byday = r + else: + pass + + return frequency, until, byday + + +def get_conference_data(doc): + return { + "createRequest": {"requestId": doc.name, "conferenceSolutionKey": {"type": "hangoutsMeet"}}, + "notes": doc.description, + } + + +def get_attendees(doc): + """ + Returns a list of dicts with attendee emails, if available in event_participants table + """ + attendees, email_not_found = [], [] + + for participant in doc.event_participants: + if participant.get("email"): + attendees.append({"email": participant.email}) + else: + email_not_found.append({"dt": participant.reference_doctype, "dn": participant.reference_docname}) + + if email_not_found: + xhiveframework.msgprint( + _("Google Calendar - Contact / email not found. Did not add attendee for -
      {0}").format( + "
      ".join(f"{d.get('dt')} {d.get('dn')}" for d in email_not_found) + ), + alert=True, + indicator="yellow", + ) + + return attendees + + +"""API Response + { + 'kind': 'calendar#events', + 'etag': '"etag"', + 'summary': 'Test Calendar', + 'updated': '2019-07-25T06:09:34.681Z', + 'timeZone': 'Asia/Kolkata', + 'accessRole': 'owner', + 'defaultReminders': [], + 'nextSyncToken': 'token', + 'items': [ + { + 'kind': 'calendar#event', + 'etag': '"etag"', + 'id': 'id', + 'status': 'confirmed' or 'cancelled', + 'htmlLink': 'link', + 'created': '2019-07-25T06:08:21.000Z', + 'updated': '2019-07-25T06:09:34.681Z', + 'summary': 'asdf', + 'creator': { + 'email': 'email' + }, + 'organizer': { + 'email': 'email', + 'displayName': 'Test Calendar', + 'self': True + }, + 'start': { + 'dateTime': '2019-07-27T12:00:00+05:30', (if all day event the its 'date' instead of 'dateTime') + 'timeZone': 'Asia/Kolkata' + }, + 'end': { + 'dateTime': '2019-07-27T13:00:00+05:30', (if all day event the its 'date' instead of 'dateTime') + 'timeZone': 'Asia/Kolkata' + }, + 'recurrence': *recurrence, + 'iCalUID': 'uid', + 'sequence': 1, + 'hangoutLink': 'https://meet.google.com/mee-ting-uri', + 'conferenceData': { + 'createRequest': { + 'requestId': 'EV00001', + 'conferenceSolutionKey': { + 'type': 'hangoutsMeet' + }, + 'status': { + 'statusCode': 'success' + } + }, + 'entryPoints': [ + { + 'entryPointType': 'video', + 'uri': 'https://meet.google.com/mee-ting-uri', + 'label': 'meet.google.com/mee-ting-uri' + } + ], + 'conferenceSolution': { + 'key': { + 'type': 'hangoutsMeet' + }, + 'name': 'Google Meet', + 'iconUri': 'https://fonts.gstatic.com/s/i/productlogos/meet_2020q4/v6/web-512dp/logo_meet_2020q4_color_2x_web_512dp.png' + }, + 'conferenceId': 'mee-ting-uri' + 'reminders': { + 'useDefault': True + } + } + ] + } + *recurrence + - Daily Event: ['RRULE:FREQ=DAILY'] + - Weekly Event: ['RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH'] + - Monthly Event: ['RRULE:FREQ=MONTHLY;BYDAY=4TH'] + - BYDAY: -2, -1, 1, 2, 3, 4 with weekdays (-2 edge case for April 2017 had 6 weeks in a month) + - Yearly Event: ['RRULE:FREQ=YEARLY;'] + - Custom Event: ['RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20191028;BYDAY=MO,WE']""" diff --git a/xhiveframework/integrations/doctype/google_contacts/__init__.py b/xhiveframework/integrations/doctype/google_contacts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/google_contacts/google_contacts.js b/xhiveframework/integrations/doctype/google_contacts/google_contacts.js new file mode 100644 index 0000000..0efe776 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_contacts/google_contacts.js @@ -0,0 +1,63 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Google Contacts", { + refresh: function (frm) { + if (!frm.doc.enable) { + frm.dashboard.set_headline( + __("To use Google Contacts, enable {0}.", [ + `${__("Google Settings")}`, + ]) + ); + } + + xhiveframework.realtime.on("import_google_contacts", (data) => { + if (data.progress) { + frm.dashboard.show_progress( + "Import Google Contacts", + (data.progress / data.total) * 100, + __("Importing {0} of {1}", [data.progress, data.total]) + ); + if (data.progress === data.total) { + frm.dashboard.hide_progress("Import Google Contacts"); + } + } + }); + + if (frm.doc.refresh_token) { + let sync_button = frm.add_custom_button(__("Sync Contacts"), function () { + xhiveframework.show_alert({ + indicator: "green", + message: __("Syncing"), + }); + xhiveframework + .call({ + method: "xhiveframework.integrations.doctype.google_contacts.google_contacts.sync", + args: { + g_contact: frm.doc.name, + }, + btn: sync_button, + }) + .then((r) => { + xhiveframework.hide_progress(); + xhiveframework.msgprint(r.message); + }); + }); + } + }, + authorize_google_contacts_access: function (frm) { + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.google_contacts.google_contacts.authorize_access", + args: { + g_contact: frm.doc.name, + reauthorize: frm.doc.authorization_code ? 1 : 0, + }, + callback: function (r) { + if (!r.exc) { + frm.save(); + window.open(r.message.url); + } + }, + }); + }, +}); diff --git a/xhiveframework/integrations/doctype/google_contacts/google_contacts.json b/xhiveframework/integrations/doctype/google_contacts/google_contacts.json new file mode 100644 index 0000000..3858217 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_contacts/google_contacts.json @@ -0,0 +1,132 @@ +{ + "actions": [], + "autoname": "format:GC-{email_id}", + "creation": "2019-06-14 00:09:39.441961", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "enable", + "sb_00", + "email_id", + "authorize_google_contacts_access", + "cb_00", + "last_sync_on", + "authorization_code", + "refresh_token", + "next_sync_token", + "sync", + "pull_from_google_contacts", + "column_break_12", + "push_to_google_contacts" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + }, + { + "fieldname": "authorization_code", + "fieldtype": "Password", + "hidden": 1, + "label": "Authorization Code" + }, + { + "fieldname": "refresh_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Refresh Token" + }, + { + "fieldname": "last_sync_on", + "fieldtype": "Datetime", + "label": "Last Sync On", + "read_only": 1 + }, + { + "description": "Email Address whose Google Contacts are to be synced.", + "fieldname": "email_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email Address", + "options": "Email", + "reqd": 1 + }, + { + "depends_on": "enable", + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Google Contacts" + }, + { + "fieldname": "cb_00", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "authorize_google_contacts_access", + "fieldtype": "Button", + "label": "Authorize Google Contacts Access" + }, + { + "fieldname": "next_sync_token", + "fieldtype": "Password", + "hidden": 1, + "label": "Next Sync Token" + }, + { + "depends_on": "enable", + "fieldname": "sync", + "fieldtype": "Section Break", + "label": "Sync" + }, + { + "default": "0", + "fieldname": "pull_from_google_contacts", + "fieldtype": "Check", + "label": "Pull from Google Contacts" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "push_to_google_contacts", + "fieldtype": "Check", + "label": "Push to Google Contacts" + } + ], + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Google Contacts", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "if_owner": 1, + "read": 1, + "role": "Desk User", + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/google_contacts/google_contacts.py b/xhiveframework/integrations/doctype/google_contacts/google_contacts.py new file mode 100644 index 0000000..10b1a39 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_contacts/google_contacts.py @@ -0,0 +1,309 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + + +from urllib.parse import quote + +from googleapiclient.errors import HttpError + +import xhiveframework +from xhiveframework import _ +from xhiveframework.integrations.google_oauth import GoogleOAuth +from xhiveframework.model.document import Document + + +class GoogleContacts(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + authorization_code: DF.Password | None + email_id: DF.Data + enable: DF.Check + last_sync_on: DF.Datetime | None + next_sync_token: DF.Password | None + pull_from_google_contacts: DF.Check + push_to_google_contacts: DF.Check + refresh_token: DF.Password | None + + # end: auto-generated types + def validate(self): + if not xhiveframework.db.get_single_value("Google Settings", "enable"): + xhiveframework.throw(_("Enable Google API in Google Settings.")) + + def get_access_token(self): + if not self.refresh_token: + button_label = xhiveframework.bold(_("Allow Google Contacts Access")) + raise xhiveframework.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + + oauth_obj = GoogleOAuth("contacts") + r = oauth_obj.refresh_access_token( + self.get_password(fieldname="refresh_token", raise_exception=False) + ) + + return r.get("access_token") + + +@xhiveframework.whitelist(methods=["POST"]) +def authorize_access(g_contact, reauthorize=False, code=None): + """ + If no Authorization code get it from Google and then request for Refresh Token. + Google Contact Name is set to flags to set_value after Authorization Code is obtained. + """ + contact = xhiveframework.get_doc("Google Contacts", g_contact) + contact.check_permission("write") + + oauth_code = code or contact.get_password("authorization_code") + oauth_obj = GoogleOAuth("contacts") + + if not oauth_code or reauthorize: + return oauth_obj.get_authentication_url( + { + "g_contact": g_contact, + "redirect": f"/app/Form/{quote('Google Contacts')}/{quote(g_contact)}", + }, + ) + + r = oauth_obj.authorize(oauth_code) + contact.authorization_code = oauth_code + contact.refresh_token = r.get("refresh_token") + contact.save() + + +def get_google_contacts_object(g_contact): + """ + Returns an object of Google Calendar along with Google Calendar doc. + """ + account = xhiveframework.get_doc("Google Contacts", g_contact) + oauth_obj = GoogleOAuth("contacts") + + google_contacts = oauth_obj.get_google_service_object( + account.get_access_token(), + account.get_password(fieldname="indexing_refresh_token", raise_exception=False), + ) + + return google_contacts, account + + +@xhiveframework.whitelist() +def sync(g_contact=None): + filters = {"enable": 1} + + if g_contact: + filters.update({"name": g_contact}) + + google_contacts = xhiveframework.get_list("Google Contacts", filters=filters) + + for g in google_contacts: + return sync_contacts_from_google_contacts(g.name) + + +def sync_contacts_from_google_contacts(g_contact): + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/people.connections/list + """ + google_contacts, account = get_google_contacts_object(g_contact) + + if not account.pull_from_google_contacts: + return + + results = [] + contacts_updated = 0 + + sync_token = account.get_password(fieldname="next_sync_token", raise_exception=False) or None + contacts = xhiveframework._dict() + + while True: + try: + contacts = ( + google_contacts.people() + .connections() + .list( + resourceName="people/me", + pageToken=contacts.get("nextPageToken"), + syncToken=sync_token, + pageSize=2000, + requestSyncToken=True, + personFields="names,emailAddresses,organizations,phoneNumbers", + ) + .execute() + ) + + except HttpError as err: + xhiveframework.throw( + _( + "Google Contacts - Could not sync contacts from Google Contacts {0}, error code {1}." + ).format(account.name, err.resp.status) + ) + + results.extend(contact for contact in contacts.get("connections", [])) + if not contacts.get("nextPageToken"): + if contacts.get("nextSyncToken"): + xhiveframework.db.set_value( + "Google Contacts", account.name, "next_sync_token", contacts.get("nextSyncToken") + ) + xhiveframework.db.commit() + break + + xhiveframework.db.set_value("Google Contacts", account.name, "last_sync_on", xhiveframework.utils.now_datetime()) + + for idx, connection in enumerate(results): + xhiveframework.publish_realtime( + "import_google_contacts", dict(progress=idx + 1, total=len(results)), user=xhiveframework.session.user + ) + # Work-around to fix + # https://lab.membtech.com/xhiveframework/xhiveframework15/issues/22648 + if not connection.get("names"): + continue + + for name in connection.get("names"): + if name.get("metadata").get("primary"): + contacts_updated += 1 + contact = xhiveframework.get_doc( + { + "doctype": "Contact", + "first_name": name.get("givenName") or "", + "middle_name": name.get("middleName") or "", + "last_name": name.get("familyName") or "", + "designation": get_indexed_value(connection.get("organizations"), 0, "title"), + "pulled_from_google_contacts": 1, + "google_contacts": account.name, + "company_name": get_indexed_value(connection.get("organizations"), 0, "name"), + } + ) + + for email in connection.get("emailAddresses", []): + contact.add_email( + email_id=email.get("value"), + is_primary=1 if email.get("metadata").get("primary") else 0, + ) + + for phone in connection.get("phoneNumbers", []): + contact.add_phone( + phone=phone.get("value"), + is_primary_phone=1 if phone.get("metadata").get("primary") else 0, + ) + + contact.insert(ignore_permissions=True) + + return ( + _("{0} Google Contacts synced.").format(contacts_updated) + if contacts_updated > 0 + else _("No new Google Contacts synced.") + ) + + +def insert_contacts_to_google_contacts(doc, method=None): + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/people/createContact + """ + if ( + not xhiveframework.db.exists("Google Contacts", {"name": doc.google_contacts}) + or doc.pulled_from_google_contacts + or not doc.sync_with_google_contacts + ): + return + + google_contacts, account = get_google_contacts_object(doc.google_contacts) + + if not account.push_to_google_contacts: + return + + names = {"givenName": doc.first_name, "middleName": doc.middle_name, "familyName": doc.last_name} + + phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos] + emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids] + + try: + contact = ( + google_contacts.people() + .createContact( + body={"names": [names], "phoneNumbers": phoneNumbers, "emailAddresses": emailAddresses} + ) + .execute() + ) + xhiveframework.db.set_value("Contact", doc.name, "google_contacts_id", contact.get("resourceName")) + except HttpError as err: + xhiveframework.msgprint( + _("Google Calendar - Could not insert contact in Google Contacts {0}, error code {1}.").format( + account.name, err.resp.status + ) + ) + + +def update_contacts_to_google_contacts(doc, method=None): + """ + Syncs Contacts from Google Contacts. + https://developers.google.com/people/api/rest/v1/people/updateContact + """ + # Workaround to avoid triggering updation when Event is being inserted since + # creation and modified are same when inserting doc + if ( + not xhiveframework.db.exists("Google Contacts", {"name": doc.google_contacts}) + or doc.modified == doc.creation + or not doc.sync_with_google_contacts + ): + return + + if doc.sync_with_google_contacts and not doc.google_contacts_id: + # If sync_with_google_contacts is checked later, then insert the contact rather than updating it. + insert_contacts_to_google_contacts(doc) + return + + google_contacts, account = get_google_contacts_object(doc.google_contacts) + + if not account.push_to_google_contacts: + return + + names = {"givenName": doc.first_name, "middleName": doc.middle_name, "familyName": doc.last_name} + + phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos] + emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids] + + try: + contact = ( + google_contacts.people() + .get( + resourceName=doc.google_contacts_id, + personFields="names,emailAddresses,organizations,phoneNumbers", + ) + .execute() + ) + + contact["names"] = [names] + contact["phoneNumbers"] = phoneNumbers + contact["emailAddresses"] = emailAddresses + + google_contacts.people().updateContact( + resourceName=doc.google_contacts_id, + body={ + "names": [names], + "phoneNumbers": phoneNumbers, + "emailAddresses": emailAddresses, + "etag": contact.get("etag"), + }, + updatePersonFields="names,emailAddresses,organizations,phoneNumbers", + ).execute() + xhiveframework.msgprint(_("Contact Synced with Google Contacts.")) + except HttpError as err: + xhiveframework.msgprint( + _("Google Contacts - Could not update contact in Google Contacts {0}, error code {1}.").format( + account.name, err.resp.status + ) + ) + + +def get_indexed_value(d, index, key): + if not d: + return "" + + try: + return d[index].get(key) + except IndexError: + return "" diff --git a/xhiveframework/integrations/doctype/google_contacts/test_google_contacts.py b/xhiveframework/integrations/doctype/google_contacts/test_google_contacts.py new file mode 100644 index 0000000..04a33a1 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_contacts/test_google_contacts.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestGoogleContacts(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/google_drive/__init__.py b/xhiveframework/integrations/doctype/google_drive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/google_drive/google_drive.js b/xhiveframework/integrations/doctype/google_drive/google_drive.js new file mode 100644 index 0000000..f50a07f --- /dev/null +++ b/xhiveframework/integrations/doctype/google_drive/google_drive.js @@ -0,0 +1,71 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Google Drive", { + refresh: function (frm) { + if (!frm.doc.enable) { + frm.dashboard.set_headline( + __("To use Google Drive, enable {0}.", [ + `${__("Google Settings")}`, + ]) + ); + } + + xhiveframework.realtime.on("upload_to_google_drive", (data) => { + if (data.progress) { + const progress_title = __("Uploading to Google Drive"); + frm.dashboard.show_progress( + progress_title, + (data.progress / data.total) * 100, + data.message + ); + if (data.progress === data.total) { + frm.dashboard.hide_progress(progress_title); + } + } + }); + + if (frm.doc.enable && frm.doc.refresh_token) { + let sync_button = frm.add_custom_button(__("Take Backup"), function () { + xhiveframework.show_alert({ + indicator: "green", + message: __("Backing up to Google Drive."), + }); + xhiveframework + .call({ + method: "xhiveframework.integrations.doctype.google_drive.google_drive.take_backup", + btn: sync_button, + }) + .then((r) => { + xhiveframework.msgprint(r.message); + }); + }); + } + + if (frm.doc.enable && frm.doc.backup_folder_name && !frm.doc.refresh_token) { + frm.dashboard.set_headline( + __( + "Click on Authorize Google Drive Access to authorize Google Drive Access." + ) + ); + } + + if (frm.doc.enable && frm.doc.refresh_token && frm.doc.authorization_code) { + frm.page.set_indicator("Authorized", "green"); + } + }, + authorize_google_drive_access: function (frm) { + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.google_drive.google_drive.authorize_access", + args: { + reauthorize: frm.doc.authorization_code ? 1 : 0, + }, + callback: function (r) { + if (!r.exc) { + frm.save(); + window.open(r.message.url); + } + }, + }); + }, +}); diff --git a/xhiveframework/integrations/doctype/google_drive/google_drive.json b/xhiveframework/integrations/doctype/google_drive/google_drive.json new file mode 100644 index 0000000..7bc967a --- /dev/null +++ b/xhiveframework/integrations/doctype/google_drive/google_drive.json @@ -0,0 +1,126 @@ +{ + "actions": [], + "creation": "2019-08-13 17:24:05.470876", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "enable", + "google_drive_section", + "backup_folder_name", + "frequency", + "email", + "send_email_for_successful_backup", + "file_backup", + "authorize_google_drive_access", + "column_break_5", + "backup_folder_id", + "last_backup_on", + "refresh_token", + "authorization_code" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + }, + { + "fieldname": "backup_folder_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Backup Folder Name", + "reqd": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "authorize_google_drive_access", + "fieldtype": "Button", + "label": "Authorize Google Drive Access" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "backup_folder_id", + "fieldtype": "Data", + "label": "Backup Folder ID", + "read_only": 1 + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "\nDaily\nWeekly", + "reqd": 1 + }, + { + "fieldname": "refresh_token", + "fieldtype": "Data", + "hidden": 1, + "label": "Refresh Token" + }, + { + "fieldname": "authorization_code", + "fieldtype": "Data", + "hidden": 1, + "label": "Authorization Code" + }, + { + "fieldname": "last_backup_on", + "fieldtype": "Datetime", + "label": "Last Backup On", + "read_only": 1 + }, + { + "default": "0", + "description": "Note: By default emails for failed backups are sent.", + "fieldname": "send_email_for_successful_backup", + "fieldtype": "Check", + "label": "Send Email for Successful backup" + }, + { + "default": "0", + "fieldname": "file_backup", + "fieldtype": "Check", + "label": "File Backup" + }, + { + "depends_on": "enable", + "fieldname": "google_drive_section", + "fieldtype": "Section Break", + "label": "Google Drive" + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Send Notification To", + "options": "Email", + "reqd": 1 + } + ], + "issingle": 1, + "links": [], + "modified": "2023-12-08 15:52:37.525003", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Google Drive", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/google_drive/google_drive.py b/xhiveframework/integrations/doctype/google_drive/google_drive.py new file mode 100644 index 0000000..de95a8e --- /dev/null +++ b/xhiveframework/integrations/doctype/google_drive/google_drive.py @@ -0,0 +1,231 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import os +from urllib.parse import quote + +from apiclient.http import MediaFileUpload +from googleapiclient.errors import HttpError + +import xhiveframework +from xhiveframework import _ +from xhiveframework.integrations.google_oauth import GoogleOAuth +from xhiveframework.integrations.offsite_backup_utils import ( + get_latest_backup_file, + send_email, + validate_file_size, +) +from xhiveframework.model.document import Document +from xhiveframework.utils import get_backups_path, get_bench_path +from xhiveframework.utils.background_jobs import enqueue +from xhiveframework.utils.backups import new_backup + + +class GoogleDrive(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + authorization_code: DF.Data | None + backup_folder_id: DF.Data | None + backup_folder_name: DF.Data + email: DF.Data + enable: DF.Check + file_backup: DF.Check + frequency: DF.Literal["", "Daily", "Weekly"] + last_backup_on: DF.Datetime | None + refresh_token: DF.Data | None + send_email_for_successful_backup: DF.Check + + # end: auto-generated types + def validate(self): + doc_before_save = self.get_doc_before_save() + if doc_before_save and doc_before_save.backup_folder_name != self.backup_folder_name: + self.backup_folder_id = "" + + def get_access_token(self): + if not self.refresh_token: + button_label = xhiveframework.bold(_("Allow Google Drive Access")) + raise xhiveframework.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + + oauth_obj = GoogleOAuth("drive") + r = oauth_obj.refresh_access_token( + self.get_password(fieldname="refresh_token", raise_exception=False) + ) + + return r.get("access_token") + + +@xhiveframework.whitelist(methods=["POST"]) +def authorize_access(reauthorize=False, code=None): + """ + If no Authorization code get it from Google and then request for Refresh Token. + Google Contact Name is set to flags to set_value after Authorization Code is obtained. + """ + + oauth_code = xhiveframework.db.get_single_value("Google Drive", "authorization_code") if not code else code + oauth_obj = GoogleOAuth("drive") + + if not oauth_code or reauthorize: + if reauthorize: + xhiveframework.db.set_single_value("Google Drive", "backup_folder_id", "") + return oauth_obj.get_authentication_url( + { + "redirect": f"/app/Form/{quote('Google Drive')}", + }, + ) + + r = oauth_obj.authorize(oauth_code) + xhiveframework.db.set_single_value( + "Google Drive", + {"authorization_code": oauth_code, "refresh_token": r.get("refresh_token")}, + ) + + +def get_google_drive_object(): + """ + Returns an object of Google Drive. + """ + account = xhiveframework.get_doc("Google Drive") + oauth_obj = GoogleOAuth("drive") + + google_drive = oauth_obj.get_google_service_object( + account.get_access_token(), + account.get_password(fieldname="indexing_refresh_token", raise_exception=False), + ) + + return google_drive, account + + +def check_for_folder_in_google_drive(): + """Checks if folder exists in Google Drive else create it.""" + + def _create_folder_in_google_drive(google_drive, account): + file_metadata = { + "name": account.backup_folder_name, + "mimeType": "application/vnd.google-apps.folder", + } + + try: + folder = google_drive.files().create(body=file_metadata, fields="id").execute() + xhiveframework.db.set_single_value("Google Drive", "backup_folder_id", folder.get("id")) + xhiveframework.db.commit() + except HttpError as e: + xhiveframework.throw( + _("Google Drive - Could not create folder in Google Drive - Error Code {0}").format(e) + ) + + google_drive, account = get_google_drive_object() + + if account.backup_folder_id: + return + + backup_folder_exists = False + + try: + google_drive_folders = ( + google_drive.files().list(q="mimeType='application/vnd.google-apps.folder'").execute() + ) + except HttpError as e: + xhiveframework.throw(_("Google Drive - Could not find folder in Google Drive - Error Code {0}").format(e)) + + for f in google_drive_folders.get("files"): + if f.get("name") == account.backup_folder_name: + xhiveframework.db.set_single_value("Google Drive", "backup_folder_id", f.get("id")) + xhiveframework.db.commit() + backup_folder_exists = True + break + + if not backup_folder_exists: + _create_folder_in_google_drive(google_drive, account) + + +@xhiveframework.whitelist() +def take_backup(): + """Enqueue longjob for taking backup to Google Drive""" + enqueue( + "xhiveframework.integrations.doctype.google_drive.google_drive.upload_system_backup_to_google_drive", + queue="long", + timeout=1500, + ) + xhiveframework.msgprint(_("Queued for backup. It may take a few minutes to an hour.")) + + +def upload_system_backup_to_google_drive(): + """ + Upload system backup to Google Drive + """ + # Get Google Drive Object + google_drive, account = get_google_drive_object() + + # Check if folder exists in Google Drive + check_for_folder_in_google_drive() + account.load_from_db() + + validate_file_size() + + if xhiveframework.flags.create_new_backup: + set_progress(1, _("Backing up Data.")) + backup = new_backup() + file_urls = [] + file_urls.append(backup.backup_path_db) + file_urls.append(backup.backup_path_conf) + + if account.file_backup: + file_urls.append(backup.backup_path_files) + file_urls.append(backup.backup_path_private_files) + else: + file_urls = get_latest_backup_file(with_files=account.file_backup) + + for fileurl in file_urls: + if not fileurl: + continue + + file_metadata = {"name": os.path.basename(fileurl), "parents": [account.backup_folder_id]} + + try: + media = MediaFileUpload( + get_absolute_path(filename=fileurl), mimetype="application/gzip", resumable=True + ) + except OSError as e: + xhiveframework.throw(_("Google Drive - Could not locate - {0}").format(e)) + + try: + set_progress(2, _("Uploading backup to Google Drive.")) + google_drive.files().create(body=file_metadata, media_body=media, fields="id").execute() + except HttpError as e: + send_email(False, "Google Drive", "Google Drive", "email", error_status=e) + + set_progress(3, _("Uploading successful.")) + xhiveframework.db.set_single_value("Google Drive", "last_backup_on", xhiveframework.utils.now_datetime()) + send_email(True, "Google Drive", "Google Drive", "email") + return _("Google Drive Backup Successful.") + + +def daily_backup(): + drive_settings = xhiveframework.db.get_singles_dict("Google Drive", cast=True) + if drive_settings.enable and drive_settings.frequency == "Daily": + upload_system_backup_to_google_drive() + + +def weekly_backup(): + drive_settings = xhiveframework.db.get_singles_dict("Google Drive", cast=True) + if drive_settings.enable and drive_settings.frequency == "Weekly": + upload_system_backup_to_google_drive() + + +def get_absolute_path(filename): + file_path = os.path.join(get_backups_path()[2:], os.path.basename(filename)) + return f"{get_bench_path()}/sites/{file_path}" + + +def set_progress(progress, message): + xhiveframework.publish_realtime( + "upload_to_google_drive", + dict(progress=progress, total=3, message=message), + user=xhiveframework.session.user, + ) diff --git a/xhiveframework/integrations/doctype/google_drive/test_google_drive.py b/xhiveframework/integrations/doctype/google_drive/test_google_drive.py new file mode 100644 index 0000000..77dddbd --- /dev/null +++ b/xhiveframework/integrations/doctype/google_drive/test_google_drive.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestGoogleDrive(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/google_settings/__init__.py b/xhiveframework/integrations/doctype/google_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/google_settings/google_settings.js b/xhiveframework/integrations/doctype/google_settings/google_settings.js new file mode 100644 index 0000000..015a8c8 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_settings/google_settings.js @@ -0,0 +1,14 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Google Settings", { + refresh: function (frm) { + frm.dashboard.set_headline( + __("For more information, {0}.", [ + `${__( + "Click here" + )}`, + ]) + ); + }, +}); diff --git a/xhiveframework/integrations/doctype/google_settings/google_settings.json b/xhiveframework/integrations/doctype/google_settings/google_settings.json new file mode 100644 index 0000000..ee48fde --- /dev/null +++ b/xhiveframework/integrations/doctype/google_settings/google_settings.json @@ -0,0 +1,100 @@ +{ + "actions": [], + "creation": "2019-06-14 00:08:37.255003", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "enable", + "sb_00", + "client_id", + "client_secret", + "sb_01", + "api_key", + "section_break_7", + "google_drive_picker_enabled", + "app_id" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + }, + { + "description": "The Client ID obtained from the Google Cloud Console under \n\"APIs & Services\" > \"Credentials\"\n", + "fieldname": "client_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Client ID", + "mandatory_depends_on": "google_drive_picker_enabled" + }, + { + "fieldname": "client_secret", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Client Secret" + }, + { + "description": "The browser API key obtained from the Google Cloud Console under \n\"APIs & Services\" > \"Credentials\"\n", + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key" + }, + { + "depends_on": "enable", + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "OAuth Client ID" + }, + { + "depends_on": "enable", + "fieldname": "sb_01", + "fieldtype": "Section Break", + "label": "API Key" + }, + { + "depends_on": "google_drive_picker_enabled", + "description": "The project number obtained from Google Cloud Console under \n\"IAM & Admin\" > \"Settings\"\n", + "fieldname": "app_id", + "fieldtype": "Data", + "label": "App ID", + "mandatory_depends_on": "google_drive_picker_enabled" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Google Drive Picker" + }, + { + "default": "0", + "fieldname": "google_drive_picker_enabled", + "fieldtype": "Check", + "label": "Google Drive Picker Enabled" + } + ], + "issingle": 1, + "links": [], + "modified": "2024-01-16 13:19:22.365362", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Google Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/google_settings/google_settings.py b/xhiveframework/integrations/doctype/google_settings/google_settings.py new file mode 100644 index 0000000..d141ddd --- /dev/null +++ b/xhiveframework/integrations/doctype/google_settings/google_settings.py @@ -0,0 +1,39 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class GoogleSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + api_key: DF.Data | None + app_id: DF.Data | None + client_id: DF.Data | None + client_secret: DF.Password | None + enable: DF.Check + google_drive_picker_enabled: DF.Check + # end: auto-generated types + + pass + + +@xhiveframework.whitelist() +def get_file_picker_settings(): + """Return all the data FileUploader needs to start the Google Drive Picker.""" + google_settings = xhiveframework.get_single("Google Settings") + if not (google_settings.enable and google_settings.google_drive_picker_enabled): + return {} + + return { + "enabled": True, + "appId": google_settings.app_id, + "clientId": google_settings.client_id, + } diff --git a/xhiveframework/integrations/doctype/google_settings/test_google_settings.py b/xhiveframework/integrations/doctype/google_settings/test_google_settings.py new file mode 100644 index 0000000..6fc6358 --- /dev/null +++ b/xhiveframework/integrations/doctype/google_settings/test_google_settings.py @@ -0,0 +1,42 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +from .google_settings import get_file_picker_settings + + +class TestGoogleSettings(XhiveFrameworkTestCase): + def setUp(self): + settings = xhiveframework.get_single("Google Settings") + settings.client_id = "test_client_id" + settings.app_id = "test_app_id" + settings.api_key = "test_api_key" + settings.save() + + def test_picker_disabled(self): + """Google Drive Picker should be disabled if it is not enabled in Google Settings.""" + xhiveframework.db.set_single_value("Google Settings", "enable", 1) + xhiveframework.db.set_single_value("Google Settings", "google_drive_picker_enabled", 0) + settings = get_file_picker_settings() + + self.assertEqual(settings, {}) + + def test_google_disabled(self): + """Google Drive Picker should be disabled if Google integration is not enabled.""" + xhiveframework.db.set_single_value("Google Settings", "enable", 0) + xhiveframework.db.set_single_value("Google Settings", "google_drive_picker_enabled", 1) + settings = get_file_picker_settings() + + self.assertEqual(settings, {}) + + def test_picker_enabled(self): + """If picker is enabled, get_file_picker_settings should return the credentials.""" + xhiveframework.db.set_single_value("Google Settings", "enable", 1) + xhiveframework.db.set_single_value("Google Settings", "google_drive_picker_enabled", 1) + settings = get_file_picker_settings() + + self.assertEqual(True, settings.get("enabled", False)) + self.assertEqual("test_client_id", settings.get("clientId", "")) + self.assertEqual("test_app_id", settings.get("appId", "")) diff --git a/xhiveframework/integrations/doctype/integration_request/__init__.py b/xhiveframework/integrations/doctype/integration_request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/integration_request/integration_request.js b/xhiveframework/integrations/doctype/integration_request/integration_request.js new file mode 100644 index 0000000..df401a8 --- /dev/null +++ b/xhiveframework/integrations/doctype/integration_request/integration_request.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Integration Request", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/integration_request/integration_request.json b/xhiveframework/integrations/doctype/integration_request/integration_request.json new file mode 100644 index 0000000..8565b2a --- /dev/null +++ b/xhiveframework/integrations/doctype/integration_request/integration_request.json @@ -0,0 +1,154 @@ +{ + "actions": [], + "creation": "2022-03-28 12:25:29.929952", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "request_id", + "integration_request_service", + "is_remote_request", + "column_break_5", + "request_description", + "status", + "section_break_8", + "url", + "request_headers", + "data", + "response_section", + "output", + "error", + "reference_section", + "reference_doctype", + "column_break_16", + "reference_docname" + ], + "fields": [ + { + "fieldname": "integration_request_service", + "fieldtype": "Data", + "label": "Service", + "read_only": 1 + }, + { + "default": "Queued", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed", + "read_only": 1 + }, + { + "fieldname": "data", + "fieldtype": "Code", + "label": "Request Data", + "read_only": 1 + }, + { + "fieldname": "output", + "fieldtype": "Code", + "label": "Output", + "read_only": 1 + }, + { + "fieldname": "error", + "fieldtype": "Code", + "label": "Error", + "read_only": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "label": "Reference Document Name", + "options": "reference_doctype", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_remote_request", + "fieldtype": "Check", + "label": "Is Remote Request?", + "read_only": 1 + }, + { + "fieldname": "request_description", + "fieldtype": "Data", + "label": "Request Description", + "read_only": 1 + }, + { + "fieldname": "request_id", + "fieldtype": "Data", + "label": "Request ID", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "fieldname": "url", + "fieldtype": "Small Text", + "label": "URL", + "read_only": 1 + }, + { + "fieldname": "response_section", + "fieldtype": "Section Break", + "label": "Response" + }, + { + "depends_on": "eval:doc.reference_doctype", + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "fieldname": "request_headers", + "fieldtype": "Code", + "label": "Request Headers", + "read_only": 1 + } + ], + "in_create": 1, + "links": [], + "modified": "2023-10-09 09:36:23.856188", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Integration Request", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "integration_request_service", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/integration_request/integration_request.py b/xhiveframework/integrations/doctype/integration_request/integration_request.py new file mode 100644 index 0000000..23a2c00 --- /dev/null +++ b/xhiveframework/integrations/doctype/integration_request/integration_request.py @@ -0,0 +1,66 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework.integrations.utils import json_handler +from xhiveframework.model.document import Document + + +class IntegrationRequest(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data: DF.Code | None + error: DF.Code | None + integration_request_service: DF.Data | None + is_remote_request: DF.Check + output: DF.Code | None + reference_docname: DF.DynamicLink | None + reference_doctype: DF.Link | None + request_description: DF.Data | None + request_headers: DF.Code | None + request_id: DF.Data | None + status: DF.Literal["", "Queued", "Authorized", "Completed", "Cancelled", "Failed"] + url: DF.SmallText | None + + # end: auto-generated types + def autoname(self): + if self.flags._name: + self.name = self.flags._name + + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Integration Request") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) + + def update_status(self, params, status): + data = json.loads(self.data) + data.update(params) + + self.data = json.dumps(data) + self.status = status + self.save(ignore_permissions=True) + xhiveframework.db.commit() + + def handle_success(self, response): + """update the output field with the response along with the relevant status""" + if isinstance(response, str): + response = json.loads(response) + self.db_set("status", "Completed") + self.db_set("output", json.dumps(response, default=json_handler)) + + def handle_failure(self, response): + """update the error field with the response along with the relevant status""" + if isinstance(response, str): + response = json.loads(response) + self.db_set("status", "Failed") + self.db_set("error", json.dumps(response, default=json_handler)) diff --git a/xhiveframework/integrations/doctype/integration_request/integration_request_list.js b/xhiveframework/integrations/doctype/integration_request/integration_request_list.js new file mode 100644 index 0000000..289a2ff --- /dev/null +++ b/xhiveframework/integrations/doctype/integration_request/integration_request_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Integration Request"] = { + onload: function (list_view) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(list_view.doctype); + }); + }, +}; diff --git a/xhiveframework/integrations/doctype/integration_request/test_integration_request.py b/xhiveframework/integrations/doctype/integration_request/test_integration_request.py new file mode 100644 index 0000000..92848ff --- /dev/null +++ b/xhiveframework/integrations/doctype/integration_request/test_integration_request.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('Integration Request') + + +class TestIntegrationRequest(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/ldap_group_mapping/__init__.py b/xhiveframework/integrations/doctype/ldap_group_mapping/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json b/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json new file mode 100644 index 0000000..95c7dc2 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "creation": "2019-05-29 01:24:29.585060", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ldap_group", + "xhiveerp_role" + ], + "fields": [ + { + "fieldname": "ldap_group", + "fieldtype": "Data", + "in_list_view": 1, + "label": "LDAP Group", + "reqd": 1 + }, + { + "fieldname": "xhiveerp_role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "User Role", + "options": "Role", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2022-07-07 16:28:44.828514", + "modified_by": "Administrator", + "module": "Integrations", + "name": "LDAP Group Mapping", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py b/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py new file mode 100644 index 0000000..47aa1ee --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class LDAPGroupMapping(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + xhiveerp_role: DF.Link + ldap_group: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/ldap_settings/__init__.py b/xhiveframework/integrations/doctype/ldap_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.js b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.js new file mode 100644 index 0000000..2ccd434 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("LDAP Settings", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.json b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.json new file mode 100644 index 0000000..0b3bf06 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.json @@ -0,0 +1,327 @@ +{ + "actions": [], + "creation": "2016-09-22 04:16:48.829658", + "doctype": "DocType", + "document_type": "System", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "ldap_server_settings_section", + "ldap_directory_server", + "column_break_4", + "ldap_server_url", + "ldap_auth_section", + "base_dn", + "column_break_8", + "password", + "ldap_search_and_paths_section", + "ldap_search_path_user", + "ldap_search_string", + "column_break_12", + "ldap_search_path_group", + "ldap_user_creation_and_mapping_section", + "ldap_email_field", + "ldap_username_field", + "ldap_first_name_field", + "do_not_create_new_user", + "column_break_19", + "ldap_middle_name_field", + "ldap_last_name_field", + "ldap_phone_field", + "ldap_mobile_field", + "ldap_security", + "ssl_tls_mode", + "require_trusted_certificate", + "column_break_27", + "local_private_key_file", + "local_server_certificate_file", + "local_ca_certs_file", + "ldap_custom_settings_section", + "ldap_group_objectclass", + "ldap_custom_group_search", + "column_break_33", + "ldap_group_member_attribute", + "ldap_group_mappings_section", + "default_user_type", + "column_break_38", + "default_role", + "section_break_40", + "ldap_groups", + "ldap_group_field" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "fieldname": "ldap_server_url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "LDAP Server Url", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_dn", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Base Distinguished Name (DN)", + "reqd": 1 + }, + { + "fieldname": "password", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Password for Base DN", + "reqd": 1 + }, + { + "depends_on": "eval: doc.default_user_type == \"System User\"", + "fieldname": "default_role", + "fieldtype": "Link", + "label": "Default User Role", + "mandatory_depends_on": "eval: doc.default_user_type == \"System User\"", + "options": "Role" + }, + { + "description": "Must be enclosed in '()' and include '{0}', which is a placeholder for the user/login name. i.e. (&(objectclass=user)(uid={0}))", + "fieldname": "ldap_search_string", + "fieldtype": "Data", + "label": "LDAP Search String", + "reqd": 1 + }, + { + "fieldname": "ldap_email_field", + "fieldtype": "Data", + "label": "LDAP Email Field", + "reqd": 1 + }, + { + "fieldname": "ldap_username_field", + "fieldtype": "Data", + "label": "LDAP Username Field", + "reqd": 1 + }, + { + "fieldname": "ldap_first_name_field", + "fieldtype": "Data", + "label": "LDAP First Name Field", + "reqd": 1 + }, + { + "fieldname": "ldap_middle_name_field", + "fieldtype": "Data", + "label": "LDAP Middle Name Field" + }, + { + "fieldname": "ldap_last_name_field", + "fieldtype": "Data", + "label": "LDAP Last Name Field" + }, + { + "fieldname": "ldap_phone_field", + "fieldtype": "Data", + "label": "LDAP Phone Field" + }, + { + "fieldname": "ldap_mobile_field", + "fieldtype": "Data", + "label": "LDAP Mobile Field" + }, + { + "fieldname": "ldap_security", + "fieldtype": "Section Break", + "label": "LDAP Security" + }, + { + "default": "Off", + "fieldname": "ssl_tls_mode", + "fieldtype": "Select", + "label": "SSL/TLS Mode", + "options": "Off\nStartTLS" + }, + { + "default": "No", + "fieldname": "require_trusted_certificate", + "fieldtype": "Select", + "label": "Require Trusted Certificate", + "options": "No\nYes", + "reqd": 1 + }, + { + "fieldname": "local_private_key_file", + "fieldtype": "Data", + "label": "Path to private Key File" + }, + { + "fieldname": "local_server_certificate_file", + "fieldtype": "Data", + "label": "Path to Server Certificate" + }, + { + "fieldname": "local_ca_certs_file", + "fieldtype": "Data", + "label": "Path to CA Certs File" + }, + { + "fieldname": "ldap_group_mappings_section", + "fieldtype": "Section Break", + "label": "LDAP Group Mappings" + }, + { + "description": "NOTE: This box is due for depreciation. Please re-setup LDAP to work with the newer settings", + "fieldname": "ldap_group_field", + "fieldtype": "Data", + "label": "LDAP Group Field" + }, + { + "fieldname": "ldap_groups", + "fieldtype": "Table", + "label": "LDAP Group Mappings", + "options": "LDAP Group Mapping" + }, + { + "fieldname": "ldap_server_settings_section", + "fieldtype": "Section Break", + "label": "LDAP Server Settings" + }, + { + "fieldname": "ldap_auth_section", + "fieldtype": "Section Break", + "label": "LDAP Auth" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "ldap_search_and_paths_section", + "fieldtype": "Section Break", + "label": "LDAP Search and Paths" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "ldap_user_creation_and_mapping_section", + "fieldtype": "Section Break", + "label": "LDAP User Creation and Mapping" + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "description": "These settings are required if 'Custom' LDAP Directory is used", + "fieldname": "ldap_custom_settings_section", + "fieldtype": "Section Break", + "label": "LDAP Custom Settings" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, + { + "description": "string value, i.e. member", + "fieldname": "ldap_group_member_attribute", + "fieldtype": "Data", + "label": "LDAP Group Member attribute" + }, + { + "description": "Please select the LDAP Directory being used", + "fieldname": "ldap_directory_server", + "fieldtype": "Select", + "label": "Directory Server", + "options": "\nActive Directory\nOpenLDAP\nCustom", + "reqd": 1 + }, + { + "description": "string value, i.e. group", + "fieldname": "ldap_group_objectclass", + "fieldtype": "Data", + "label": "Group Object Class" + }, + { + "description": "string value, i.e. {0} or uid={0},ou=users,dc=example,dc=com", + "fieldname": "ldap_custom_group_search", + "fieldtype": "Data", + "label": "Custom Group Search" + }, + { + "description": "Requires any valid fdn path. i.e. ou=users,dc=example,dc=com", + "fieldname": "ldap_search_path_user", + "fieldtype": "Data", + "in_list_view": 1, + "label": "LDAP search path for Users", + "reqd": 1 + }, + { + "description": "Requires any valid fdn path. i.e. ou=groups,dc=example,dc=com", + "fieldname": "ldap_search_path_group", + "fieldtype": "Data", + "label": "LDAP search path for Groups", + "reqd": 1 + }, + { + "fieldname": "default_user_type", + "fieldtype": "Link", + "label": "Default User Type", + "options": "User Type", + "reqd": 1 + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_40", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "default": "0", + "description": "Do not create new user if user with email does not exist in the system", + "fieldname": "do_not_create_new_user", + "fieldtype": "Check", + "label": "Do Not Create New User " + } + ], + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-01-24 11:20:06.049708", + "modified_by": "Administrator", + "module": "Integrations", + "name": "LDAP Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.py b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.py new file mode 100644 index 0000000..eabad69 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/ldap_settings.py @@ -0,0 +1,426 @@ +# Copyright (c) 2022, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import ssl +from typing import TYPE_CHECKING + +import ldap3 +from ldap3 import AUTO_BIND_TLS_BEFORE_BIND, HASHED_SALTED_SHA, MODIFY_REPLACE +from ldap3.abstract.entry import Entry +from ldap3.core.exceptions import ( + LDAPAttributeError, + LDAPInvalidCredentialsResult, + LDAPInvalidFilterError, + LDAPNoSuchObjectResult, +) +from ldap3.utils.hashed import hashed + +import xhiveframework +from xhiveframework import _, safe_encode +from xhiveframework.model.document import Document +from xhiveframework.twofactor import authenticate_for_2factor, confirm_otp_token, should_run_2fa + +if TYPE_CHECKING: + from xhiveframework.core.doctype.user.user import User + + +class LDAPSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.integrations.doctype.ldap_group_mapping.ldap_group_mapping import LDAPGroupMapping + from xhiveframework.types import DF + + base_dn: DF.Data + default_role: DF.Link | None + default_user_type: DF.Link + do_not_create_new_user: DF.Check + enabled: DF.Check + ldap_custom_group_search: DF.Data | None + ldap_directory_server: DF.Literal["", "Active Directory", "OpenLDAP", "Custom"] + ldap_email_field: DF.Data + ldap_first_name_field: DF.Data + ldap_group_field: DF.Data | None + ldap_group_member_attribute: DF.Data | None + ldap_group_objectclass: DF.Data | None + ldap_groups: DF.Table[LDAPGroupMapping] + ldap_last_name_field: DF.Data | None + ldap_middle_name_field: DF.Data | None + ldap_mobile_field: DF.Data | None + ldap_phone_field: DF.Data | None + ldap_search_path_group: DF.Data + ldap_search_path_user: DF.Data + ldap_search_string: DF.Data + ldap_server_url: DF.Data + ldap_username_field: DF.Data + local_ca_certs_file: DF.Data | None + local_private_key_file: DF.Data | None + local_server_certificate_file: DF.Data | None + password: DF.Password + require_trusted_certificate: DF.Literal["No", "Yes"] + ssl_tls_mode: DF.Literal["Off", "StartTLS"] + + # end: auto-generated types + def validate(self): + self.default_user_type = self.default_user_type or "Website User" + + if not self.enabled: + return + + if not self.flags.ignore_mandatory: + if ( + self.ldap_search_string.count("(") == self.ldap_search_string.count(")") + and self.ldap_search_string.startswith("(") + and self.ldap_search_string.endswith(")") + and self.ldap_search_string + and "{0}" in self.ldap_search_string + ): + conn = self.connect_to_ldap( + base_dn=self.base_dn, password=self.get_password(raise_exception=False) + ) + + try: + if conn.result["type"] == "bindResponse" and self.base_dn: + conn.search( + search_base=self.ldap_search_path_user, + search_filter="(objectClass=*)", + attributes=self.get_ldap_attributes(), + ) + + conn.search( + search_base=self.ldap_search_path_group, + search_filter="(objectClass=*)", + attributes=["cn"], + ) + + except LDAPAttributeError as ex: + xhiveframework.throw( + _("LDAP settings incorrect. validation response was: {0}").format(ex), + title=_("Misconfigured"), + ) + + except LDAPNoSuchObjectResult: + xhiveframework.throw( + _("Ensure the user and group search paths are correct."), title=_("Misconfigured") + ) + + if self.ldap_directory_server.lower() == "custom": + if not self.ldap_group_member_attribute or not self.ldap_group_objectclass: + xhiveframework.throw( + _( + "Custom LDAP Directoy Selected, please ensure 'LDAP Group Member attribute' and 'Group Object Class' are entered" + ), + title=_("Misconfigured"), + ) + + if self.ldap_custom_group_search and "{0}" not in self.ldap_custom_group_search: + xhiveframework.throw( + _( + "Custom Group Search if filled needs to contain the user placeholder {0}, eg uid={0},ou=users,dc=example,dc=com" + ), + title=_("Misconfigured"), + ) + + else: + xhiveframework.throw( + _( + "LDAP Search String must be enclosed in '()' and needs to contian the user placeholder {0}, eg sAMAccountName={0}" + ) + ) + + def connect_to_ldap(self, base_dn, password, read_only=True) -> ldap3.Connection: + try: + if self.require_trusted_certificate == "Yes": + tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLS_CLIENT) + else: + tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLS_CLIENT) + + if self.local_private_key_file: + tls_configuration.private_key_file = self.local_private_key_file + if self.local_server_certificate_file: + tls_configuration.certificate_file = self.local_server_certificate_file + if self.local_ca_certs_file: + tls_configuration.ca_certs_file = self.local_ca_certs_file + + server = ldap3.Server(host=self.ldap_server_url, tls=tls_configuration) + bind_type = AUTO_BIND_TLS_BEFORE_BIND if self.ssl_tls_mode == "StartTLS" else True + + return ldap3.Connection( + server=server, + user=base_dn, + password=password, + auto_bind=bind_type, + read_only=read_only, + raise_exceptions=True, + ) + + except ImportError: + msg = _("Please Install the ldap3 library via pip to use ldap functionality.") + xhiveframework.throw(msg, title=_("LDAP Not Installed")) + except LDAPInvalidCredentialsResult: + xhiveframework.throw(_("Invalid username or password")) + except Exception as ex: + xhiveframework.throw(_(str(ex))) + + @staticmethod + def get_ldap_client_settings() -> dict: + # return the settings to be used on the client side. + result = {"enabled": False} + ldap = xhiveframework.get_cached_doc("LDAP Settings") + if ldap.enabled: + result["enabled"] = True + result["method"] = "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.login" + return result + + @classmethod + def update_user_fields(cls, user: "User", user_data: dict): + updatable_data = {key: value for key, value in user_data.items() if key != "email"} + + for key, value in updatable_data.items(): + setattr(user, key, value) + user.save(ignore_permissions=True) + + def sync_roles(self, user: "User", additional_groups: list | None = None): + current_roles = {d.role for d in user.get("roles")} + if self.default_user_type == "System User": + needed_roles = {self.default_role} + else: + needed_roles = set() + lower_groups = [g.lower() for g in additional_groups or []] + + all_mapped_roles = {r.xhiveerp_role for r in self.ldap_groups} + matched_roles = {r.xhiveerp_role for r in self.ldap_groups if r.ldap_group.lower() in lower_groups} + unmatched_roles = all_mapped_roles.difference(matched_roles) + needed_roles.update(matched_roles) + roles_to_remove = current_roles.intersection(unmatched_roles) + + if not needed_roles.issubset(current_roles): + missing_roles = needed_roles.difference(current_roles) + user.add_roles(*missing_roles) + + user.remove_roles(*roles_to_remove) + + def create_or_update_user(self, user_data: dict, groups: list | None = None): + user: "User" = None + role: str = None + + if xhiveframework.db.exists("User", user_data["email"]): + user = xhiveframework.get_doc("User", user_data["email"]) + LDAPSettings.update_user_fields(user=user, user_data=user_data) + elif not self.do_not_create_new_user: + doc = user_data | { + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": self.default_user_type, + } + user = xhiveframework.get_doc(doc) + user.insert(ignore_permissions=True) + else: + xhiveframework.throw( + _( + "User with email: {0} does not exist in the system. Please ask 'System Administrator' to create the user for you." + ).format(user_data["email"]) + ) + + if self.default_user_type == "System User": + role = self.default_role + else: + role = xhiveframework.db.get_value("User Type", user.user_type, "role") + + if role: + user.add_roles(role) + + self.sync_roles(user, groups) + + return user + + def get_ldap_attributes(self): + ldap_attributes = [self.ldap_email_field, self.ldap_username_field, self.ldap_first_name_field] + + if self.ldap_group_field: + ldap_attributes.append(self.ldap_group_field) + + if self.ldap_middle_name_field: + ldap_attributes.append(self.ldap_middle_name_field) + + if self.ldap_last_name_field: + ldap_attributes.append(self.ldap_last_name_field) + + if self.ldap_phone_field: + ldap_attributes.append(self.ldap_phone_field) + + if self.ldap_mobile_field: + ldap_attributes.append(self.ldap_mobile_field) + + return ldap_attributes + + def fetch_ldap_groups(self, user: Entry, conn: ldap3.Connection) -> list: + if not isinstance(user, Entry): + raise TypeError("Invalid type, attribute 'user' must be of type 'ldap3.abstract.entry.Entry'") + + if not isinstance(conn, ldap3.Connection): + raise TypeError("Invalid type, attribute 'conn' must be of type 'ldap3.Connection'") + + fetch_ldap_groups = None + ldap_object_class = None + ldap_group_members_attribute = None + + if self.ldap_directory_server.lower() == "active directory": + ldap_object_class = "Group" + ldap_group_members_attribute = "member" + user_search_str = user.entry_dn + + elif self.ldap_directory_server.lower() == "openldap": + ldap_object_class = "posixgroup" + ldap_group_members_attribute = "memberuid" + user_search_str = getattr(user, self.ldap_username_field).value + + elif self.ldap_directory_server.lower() == "custom": + ldap_object_class = self.ldap_group_objectclass + ldap_group_members_attribute = self.ldap_group_member_attribute + ldap_custom_group_search = self.ldap_custom_group_search or "{0}" + user_search_str = ldap_custom_group_search.format(getattr(user, self.ldap_username_field).value) + + else: + # NOTE: depreciate this else path + # this path will be hit for everyone with preconfigured ldap settings. this must be taken into account so as not to break ldap for those users. + + if self.ldap_group_field: + fetch_ldap_groups = getattr(user, self.ldap_group_field).values + + if ldap_object_class is not None: + conn.search( + search_base=self.ldap_search_path_group, + search_filter=f"(&(objectClass={ldap_object_class})({ldap_group_members_attribute}={user_search_str}))", + attributes=["cn"], + ) # Build search query + + if len(conn.entries) >= 1: + fetch_ldap_groups = [group["cn"].value for group in conn.entries] + return fetch_ldap_groups + + def authenticate(self, username: str, password: str): + if not self.enabled: + xhiveframework.throw(_("LDAP is not enabled.")) + + user_filter = self.ldap_search_string.format(username) + ldap_attributes = self.get_ldap_attributes() + conn = self.connect_to_ldap(self.base_dn, self.get_password(raise_exception=False)) + + try: + conn.search( + search_base=self.ldap_search_path_user, + search_filter=f"{user_filter}", + attributes=ldap_attributes, + ) + + if len(conn.entries) == 1 and conn.entries[0]: + user = conn.entries[0] + groups = self.fetch_ldap_groups(user, conn) + + # only try and connect as the user, once we have their fqdn entry. + if user.entry_dn and password and conn.rebind(user=user.entry_dn, password=password): + return self.create_or_update_user(self.convert_ldap_entry_to_dict(user), groups=groups) + + raise LDAPInvalidCredentialsResult # even though nothing foundor failed authentication raise invalid credentials + + except LDAPInvalidFilterError: + xhiveframework.throw(_("Please use a valid LDAP search filter"), title=_("Misconfigured")) + + except LDAPInvalidCredentialsResult: + xhiveframework.throw(_("Invalid username or password")) + + def reset_password(self, user, password, logout_sessions=False): + search_filter = f"({self.ldap_email_field}={user})" + + conn = self.connect_to_ldap(self.base_dn, self.get_password(raise_exception=False), read_only=False) + + if conn.search( + search_base=self.ldap_search_path_user, + search_filter=search_filter, + attributes=self.get_ldap_attributes(), + ): + if conn.entries and conn.entries[0]: + entry_dn = conn.entries[0].entry_dn + hashed_password = hashed(HASHED_SALTED_SHA, safe_encode(password)) + changes = {"userPassword": [(MODIFY_REPLACE, [hashed_password])]} + if conn.modify(entry_dn, changes=changes): + if logout_sessions: + from xhiveframework.sessions import clear_sessions + + clear_sessions(user=user, force=True) + xhiveframework.msgprint(_("Password changed successfully.")) + else: + xhiveframework.throw(_("Failed to change password.")) + else: + xhiveframework.throw(_("No Entry for the User {0} found within LDAP!").format(user)) + else: + xhiveframework.throw(_("No LDAP User found for email: {0}").format(user)) + + def convert_ldap_entry_to_dict(self, user_entry: Entry): + # support multiple email values + email = user_entry[self.ldap_email_field].value + + if isinstance(email, list): + # check if any of the email in the list already exist + for e in email: + if xhiveframework.db.exists("User", e): + email = e + break + else: + # if none of the email exist, use the first email + email = email[0] + + data = { + "username": user_entry[self.ldap_username_field].value, + "email": email, + "first_name": user_entry[self.ldap_first_name_field].value, + } + + # optional fields + if self.ldap_middle_name_field: + data["middle_name"] = user_entry[self.ldap_middle_name_field].value + + if self.ldap_last_name_field: + data["last_name"] = user_entry[self.ldap_last_name_field].value + + if self.ldap_phone_field: + data["phone"] = user_entry[self.ldap_phone_field].value + + if self.ldap_mobile_field: + data["mobile_no"] = user_entry[self.ldap_mobile_field].value + + return data + + +@xhiveframework.whitelist(allow_guest=True) +def login(): + # LDAP LOGIN LOGIC + args = xhiveframework.form_dict + ldap: LDAPSettings = xhiveframework.get_doc("LDAP Settings") + + user = ldap.authenticate(xhiveframework.as_unicode(args.usr), xhiveframework.as_unicode(args.pwd)) + + xhiveframework.local.login_manager.user = user.name + if should_run_2fa(user.name): + authenticate_for_2factor(user.name) + if not confirm_otp_token(xhiveframework.local.login_manager): + return False + + xhiveframework.form_dict.pop("pwd", None) + xhiveframework.local.login_manager.post_login() + + # because of a GET request! + xhiveframework.db.commit() + + +@xhiveframework.whitelist() +def reset_password(user, password, logout): + ldap: LDAPSettings = xhiveframework.get_doc("LDAP Settings") + if not ldap.enabled: + xhiveframework.throw(_("LDAP is not enabled.")) + ldap.reset_password(user, password, logout_sessions=int(logout)) diff --git a/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_activedirectory.json b/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_activedirectory.json new file mode 100644 index 0000000..9777452 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_activedirectory.json @@ -0,0 +1,338 @@ +{ + "entries": [ + { + "attributes": { + "cn": "base_dn_user", + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Enterprise Administrators,ou=Groups,dc=unit,dc=testing" + ], + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": "cn=base_dn_user,dc=unit,dc=testing", + "sn": "user_sn", + "userPassword": [ + "my_password" + ] + }, + "dn": "cn=base_dn_user,dc=unit,dc=testing", + "raw": { + "cn": [ + "base_dn_user" + ], + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Enterprise Administrators,ou=Groups,dc=unit,dc=testing" + ], + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": [ + "cn=base_dn_user,dc=unit,dc=testing" + ], + "sn": [ + "user_sn" + ], + "userPassword": [ + "my_password" + ] + } + }, + { + "attributes": { + "cn": "Posix User1", + "description": [ + "ACCESS:test1,ACCESS:test2" + ], + "givenname": "Posix", + "mail": "posix.user1@unit.testing", + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Domain Administrators,ou=Groups,dc=unit,dc=testing" + ], + "mobile": "0421 123 456", + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": "posix.user", + "sn": "User1", + "telephonenumber": "08 8912 3456", + "userpassword": [ + "posix_user_password" + ] + }, + "dn": "cn=Posix User1,ou=Users,dc=unit,dc=testing", + "raw": { + "cn": [ + "Posix User1" + ], + "description": [ + "ACCESS:test1,ACCESS:test2" + ], + "givenname": [ + "Posix" + ], + "mail": [ + "posix.user1@unit.testing" + ], + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Domain Administrators,ou=Groups,dc=unit,dc=testing" + ], + "mobile": [ + "0421 123 456" + ], + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": [ + "posix.user" + ], + "sn": [ + "User1" + ], + "telephonenumber": [ + "08 8912 3456" + ], + "userpassword": [ + "posix_user_password" + ] + } + }, + { + "attributes": { + "cn": "Posix User2", + "description": [ + "ACCESS:test1,ACCESS:test3" + ], + "givenname": "Posix", + "homedirectory": "/home/users/posix.user2", + "mail": "posix.user2@unit.testing", + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Enterprise Administrators,ou=Groups,dc=unit,dc=testing" + ], + "mobile": "0421 456 789", + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": "posix.user2", + "sn": "User2", + "telephonenumber": "08 8978 1234", + "userpassword": [ + "posix_user2_password" + ] + }, + "dn": "cn=Posix User2,ou=Users,dc=unit,dc=testing", + "raw": { + "cn": [ + "Posix User2" + ], + "description": [ + "ACCESS:test1,ACCESS:test3" + ], + "givenname": [ + "Posix" + ], + "homedirectory": [ + "/home/users/posix.user2" + ], + "mail": [ + "posix.user2@unit.testing" + ], + "memberOf": [ + "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "cn=Enterprise Administrators,ou=Groups,dc=unit,dc=testing" + ], + "mobile": [ + "0421 456 789" + ], + "objectClass": [ + "user", + "top", + "person", + "organizationalPerson" + ], + "samaccountname": [ + "posix.user2" + ], + "sn": [ + "User2" + ], + "telephonenumber": [ + "08 8978 1234" + ], + "userpassword": [ + "posix_user2_password" + ] + } + }, + { + "attributes": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users" + ] + }, + "dn": "ou=Users,dc=unit,dc=testing", + "raw": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users" + ] + } + }, + { + "attributes": { + "Member": [ + "cn=Posix User2,ou=Users,dc=unit,dc=testing" + ], + "cn": "Enterprise Administrators", + "description": [ + "group contains only posix.user2" + ], + "groupType": 2147483652, + "objectClass": [ + "top", + "group" + ] + }, + "dn": "cn=Enterprise Administrators,ou=Groups,dc=unit,dc=testing", + "raw": { + "Member": [ + "cn=Posix User2,ou=Users,dc=unit,dc=testing" + ], + "cn": [ + "Enterprise Administrators" + ], + "description": [ + "group contains only posix.user2" + ], + "groupType": [ + "2147483652" + ], + "objectClass": [ + "top", + "group" + ] + } + }, + { + "attributes": { + "Member": [ + "cn=Posix User1,ou=Users,dc=unit,dc=testing", + "cn=Posix User2,ou=Users,dc=unit,dc=testing" + ], + "cn": "Domain Users", + "description": [ + "group2 Users contains only posix.user and posix.user2" + ], + "groupType": 2147483652, + "objectClass": [ + "top", + "group" + ] + }, + "dn": "cn=Domain Users,ou=Groups,dc=unit,dc=testing", + "raw": { + "Member": [ + "cn=Posix User1,ou=Users,dc=unit,dc=testing", + "cn=Posix User2,ou=Users,dc=unit,dc=testing" + ], + "cn": [ + "Domain Users" + ], + "description": [ + "group2 Users contains only posix.user and posix.user2" + ], + "groupType": [ + "2147483652" + ], + "objectClass": [ + "top", + "group" + ] + } + }, + { + "attributes": { + "Member": [ + "cn=Posix User1,ou=Users,dc=unit,dc=testing", + "cn=base_dn_user,dc=unit,dc=testing" + ], + "cn": "Domain Administrators", + "description": [ + "group1 Administrators contains only posix.user only" + ], + "groupType": 2147483652, + "objectClass": [ + "top", + "group" + ] + }, + "dn": "cn=Domain Administrators,ou=Groups,dc=unit,dc=testing", + "raw": { + "Member": [ + "cn=Posix User1,ou=Users,dc=unit,dc=testing", + "cn=base_dn_user,dc=unit,dc=testing" + ], + "cn": [ + "Domain Administrators" + ], + "description": [ + "group1 Administrators contains only posix.user only" + ], + "groupType": [ + "2147483652" + ], + "objectClass": [ + "top", + "group" + ] + } + }, + { + "attributes": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Groups" + ] + }, + "dn": "ou=Groups,dc=unit,dc=testing", + "raw": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Groups" + ] + } + } + ] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_openldap.json b/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_openldap.json new file mode 100644 index 0000000..86a76c1 --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/test_data_ldif_openldap.json @@ -0,0 +1,400 @@ +{ + "entries": [ + { + "attributes": { + "cn": [ + "base_dn_user" + ], + "objectClass": [ + "simpleSecurityObject", + "organizationalRole", + "top" + ], + "sn": [ + "user_sn" + ], + "userPassword": [ + "my_password" + ] + }, + "dn": "cn=base_dn_user,dc=unit,dc=testing", + "raw": { + "cn": [ + "base_dn_user" + ], + "objectClass": [ + "simpleSecurityObject", + "organizationalRole", + "top" + ], + "sn": [ + "user_sn" + ], + "userPassword": [ + "my_password" + ] + } + }, + { + "attributes": { + "cn": [ + "Posix User2" + ], + "description": [ + "ACCESS:test1,ACCESS:test3" + ], + "gidnumber": 501, + "givenname": [ + "Posix2" + ], + "homedirectory": "/home/users/posix.user2", + "mail": [ + "posix.user2@unit.testing" + ], + "mobile": [ + "0421 456 789" + ], + "objectClass": [ + "posixAccount", + "top", + "inetOrgPerson", + "person", + "organizationalPerson" + ], + "sn": [ + "User2" + ], + "telephonenumber": [ + "08 8978 1234" + ], + "uid": [ + "posix.user2" + ], + "uidnumber": 1000, + "userpassword": [ + "posix_user2_password" + ] + }, + "dn": "cn=Posix User2,ou=users,dc=unit,dc=testing", + "raw": { + "cn": [ + "Posix User2" + ], + "description": [ + "ACCESS:test1,ACCESS:test3" + ], + "gidnumber": [ + "501" + ], + "givenname": [ + "Posix2" + ], + "homedirectory": [ + "/home/users/posix.user2" + ], + "mail": [ + "posix.user2@unit.testing" + ], + "mobile": [ + "0421 456 789" + ], + "objectClass": [ + "posixAccount", + "top", + "inetOrgPerson", + "person", + "organizationalPerson" + ], + "sn": [ + "User2" + ], + "telephonenumber": [ + "08 8978 1234" + ], + "uid": [ + "posix.user2" + ], + "uidnumber": [ + "1000" + ], + "userpassword": [ + "posix_user2_password" + ] + } + }, + { + "attributes": { + "cn": [ + "Posix User1" + ], + "description": [ + "ACCESS:test1,ACCESS:test2" + ], + "gidnumber": 501, + "givenname": [ + "Posix" + ], + "homedirectory": "/home/users/posix.user", + "mail": [ + "posix.user1@unit.testing" + ], + "mobile": [ + "0421 123 456" + ], + "objectClass": [ + "posixAccount", + "top", + "inetOrgPerson", + "person", + "organizationalPerson" + ], + "sn": [ + "User1" + ], + "telephonenumber": [ + "08 8912 3456" + ], + "uid": [ + "posix.user" + ], + "uidnumber": 1000, + "userpassword": [ + "posix_user_password" + ] + }, + "dn": "cn=Posix User1,ou=users,dc=unit,dc=testing", + "raw": { + "cn": [ + "Posix User1" + ], + "description": [ + "ACCESS:test1,ACCESS:test2" + ], + "gidnumber": [ + "501" + ], + "givenname": [ + "Posix" + ], + "homedirectory": [ + "/home/users/posix.user" + ], + "mail": [ + "posix.user1@unit.testing" + ], + "mobile": [ + "0421 123 456" + ], + "objectClass": [ + "posixAccount", + "top", + "inetOrgPerson", + "person", + "organizationalPerson" + ], + "sn": [ + "User1" + ], + "telephonenumber": [ + "08 8912 3456" + ], + "uid": [ + "posix.user" + ], + "uidnumber": [ + "1000" + ], + "userpassword": [ + "posix_user_password" + ] + } + }, + { + "attributes": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users", + "users" + ] + }, + "dn": "ou=users,dc=unit,dc=testing", + "raw": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users", + "users" + ] + } + }, + { + "attributes": { + "dc": "testing", + "o": [ + "Testing" + ], + "objectClass": [ + "top", + "organization", + "dcObject" + ] + }, + "dn": "dc=unit,dc=testing", + "raw": { + "dc": [ + "testing", + "unit" + ], + "o": [ + "Testing" + ], + "objectClass": [ + "top", + "organization", + "dcObject" + ] + } + }, + { + "attributes": { + "cn": [ + "Users" + ], + "description": [ + "group2 Users contains only posix.user and posix.user2" + ], + "gidnumber": 501, + "memberuid": [ + "posix.user2", + "posix.user" + ], + "objectClass": [ + "top", + "posixGroup" + ] + }, + "dn": "cn=Users,ou=groups,dc=unit,dc=testing", + "raw": { + "cn": [ + "Users" + ], + "description": [ + "group2 Users contains only posix.user and posix.user2" + ], + "gidnumber": [ + "501" + ], + "memberuid": [ + "posix.user2", + "posix.user" + ], + "objectClass": [ + "top", + "posixGroup" + ] + } + }, + { + "attributes": { + "cn": [ + "Administrators" + ], + "description": [ + "group1 Administrators contains only posix.user only" + ], + "gidnumber": 500, + "memberuid": [ + "posix.user" + ], + "objectClass": [ + "top", + "posixGroup" + ] + }, + "dn": "cn=Administrators,ou=groups,dc=unit,dc=testing", + "raw": { + "cn": [ + "Administrators" + ], + "description": [ + "group1 Administrators contains only posix.user only" + ], + "gidnumber": [ + "500" + ], + "memberuid": [ + "posix.user" + ], + "objectClass": [ + "top", + "posixGroup" + ] + } + }, + { + "attributes": { + "cn": [ + "Group3" + ], + "description": [ + "group3 Group3 contains only posix.user2 only" + ], + "gidnumber": 502, + "memberuid": [ + "posix.user2" + ], + "objectClass": [ + "top", + "posixGroup" + ] + }, + "dn": "cn=Group3,ou=groups,dc=unit,dc=testing", + "raw": { + "cn": [ + "Group3" + ], + "description": [ + "group3 Group3 contains only posix.user2 only" + ], + "gidnumber": [ + "502" + ], + "memberuid": [ + "posix.user2" + ], + "objectClass": [ + "top", + "posixGroup" + ] + } + }, + { + "attributes": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users", + "groups" + ] + }, + "dn": "ou=groups,dc=unit,dc=testing", + "raw": { + "objectClass": [ + "top", + "organizationalUnit" + ], + "ou": [ + "Users", + "groups" + ] + } + } + ] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/ldap_settings/test_ldap_settings.py b/xhiveframework/integrations/doctype/ldap_settings/test_ldap_settings.py new file mode 100644 index 0000000..d220d2a --- /dev/null +++ b/xhiveframework/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -0,0 +1,656 @@ +# Copyright (c) 2022, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import contextlib +import functools +import os +import ssl +import typing +from unittest import TestCase, mock + +import ldap3 +from ldap3 import MOCK_SYNC, OFFLINE_AD_2012_R2, OFFLINE_SLAPD_2_4, Connection, Server + +import xhiveframework +from xhiveframework.exceptions import MandatoryError, ValidationError +from xhiveframework.integrations.doctype.ldap_settings.ldap_settings import LDAPSettings + + +class LDAP_TestCase: + TEST_LDAP_SERVER = None # must match the 'LDAP Settings' field option + TEST_LDAP_SEARCH_STRING = None + LDAP_USERNAME_FIELD = None + DOCUMENT_GROUP_MAPPINGS: typing.ClassVar[list] = [] + LDAP_SCHEMA = None + LDAP_LDIF_JSON = None + TEST_VALUES_LDAP_COMPLEX_SEARCH_STRING = None + + # for adding type hints during development ^_^ + assertTrue = TestCase.assertTrue + assertEqual = TestCase.assertEqual + assertIn = TestCase.assertIn + + def mock_ldap_connection(f): + @functools.wraps(f) + def wrapped(self, *args, **kwargs): + with mock.patch( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.LDAPSettings.connect_to_ldap", + return_value=self.connection, + ): + self.test_class = LDAPSettings(self.doc) + + # Create a clean doc + localdoc = self.doc.copy() + xhiveframework.get_doc(localdoc).save() + + rv = f(self, *args, **kwargs) + + # Clean-up + self.test_class = None + + return rv + + return wrapped + + def clean_test_users(): + with contextlib.suppress(Exception): + xhiveframework.get_doc("User", "posix.user1@unit.testing").delete() + with contextlib.suppress(Exception): + xhiveframework.get_doc("User", "posix.user2@unit.testing").delete() + with contextlib.suppress(Exception): + xhiveframework.get_doc("User", "website_ldap_user@test.com").delete() + + @classmethod + def setUpClass(cls): + cls.clean_test_users() + # Save user data for restoration in tearDownClass() + cls.user_ldap_settings = xhiveframework.get_doc("LDAP Settings") + + # Create test user1 + cls.user1doc = { + "username": "posix.user", + "email": "posix.user1@unit.testing", + "first_name": "posix", + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": "System User", + } + + user = xhiveframework.get_doc(cls.user1doc) + user.insert(ignore_permissions=True) + + cls.user2doc = { + "username": "posix.user2", + "email": "posix.user2@unit.testing", + "first_name": "posix", + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": "System User", + } + user = xhiveframework.get_doc(cls.user2doc) + user.insert(ignore_permissions=True) + + # Setup Mock OpenLDAP Directory + cls.ldap_dc_path = "dc=unit,dc=testing" + cls.ldap_user_path = f"ou=users,{cls.ldap_dc_path}" + cls.ldap_group_path = f"ou=groups,{cls.ldap_dc_path}" + cls.base_dn = f"cn=base_dn_user,{cls.ldap_dc_path}" + cls.base_password = "my_password" + cls.ldap_server = "ldap://my_fake_server:389" + + cls.doc = { + "doctype": "LDAP Settings", + "enabled": True, + "ldap_directory_server": cls.TEST_LDAP_SERVER, + "ldap_server_url": cls.ldap_server, + "base_dn": cls.base_dn, + "password": cls.base_password, + "ldap_search_path_user": cls.ldap_user_path, + "ldap_search_string": cls.TEST_LDAP_SEARCH_STRING, + "ldap_search_path_group": cls.ldap_group_path, + "ldap_user_creation_and_mapping_section": "", + "ldap_email_field": "mail", + "ldap_username_field": cls.LDAP_USERNAME_FIELD, + "ldap_first_name_field": "givenname", + "ldap_middle_name_field": "", + "ldap_last_name_field": "sn", + "ldap_phone_field": "telephonenumber", + "ldap_mobile_field": "mobile", + "ldap_security": "", + "ssl_tls_mode": "", + "require_trusted_certificate": "No", + "local_private_key_file": "", + "local_server_certificate_file": "", + "local_ca_certs_file": "", + "ldap_group_objectclass": "", + "ldap_group_member_attribute": "", + "default_role": "Newsletter Manager", + "ldap_groups": cls.DOCUMENT_GROUP_MAPPINGS, + "ldap_group_field": "", + "default_user_type": "System User", + } + + cls.server = Server(host=cls.ldap_server, port=389, get_info=cls.LDAP_SCHEMA) + cls.connection = Connection( + cls.server, + user=cls.base_dn, + password=cls.base_password, + read_only=True, + client_strategy=MOCK_SYNC, + ) + cls.connection.strategy.entries_from_json( + f"{os.path.abspath(os.path.dirname(__file__))}/{cls.LDAP_LDIF_JSON}" + ) + cls.connection.bind() + + @classmethod + def tearDownClass(cls): + with contextlib.suppress(Exception): + xhiveframework.get_doc("LDAP Settings").delete() + + # return doc back to user data + with contextlib.suppress(Exception): + cls.user_ldap_settings.save() + + # Clean-up test users + cls.clean_test_users() + + # Clear OpenLDAP connection + cls.connection = None + + @mock_ldap_connection + def test_mandatory_fields(self): + mandatory_fields = [ + "ldap_server_url", + "ldap_directory_server", + "base_dn", + "password", + "ldap_search_path_user", + "ldap_search_path_group", + "ldap_search_string", + "ldap_email_field", + "ldap_username_field", + "ldap_first_name_field", + "require_trusted_certificate", + ] # fields that are required to have ldap functioning need to be mandatory + + for mandatory_field in mandatory_fields: + localdoc = self.doc.copy() + localdoc[mandatory_field] = "" + + with contextlib.suppress(MandatoryError, ValidationError): + xhiveframework.get_doc(localdoc).save() + self.fail(f"Document LDAP Settings field [{mandatory_field}] is not mandatory") + + for non_mandatory_field in self.doc: # Ensure remaining fields have not been made mandatory + if non_mandatory_field == "doctype" or non_mandatory_field in mandatory_fields: + continue + + localdoc = self.doc.copy() + localdoc[non_mandatory_field] = "" + + try: + xhiveframework.get_doc(localdoc).save() + except MandatoryError: + self.fail(f"Document LDAP Settings field [{non_mandatory_field}] should not be mandatory") + + @mock_ldap_connection + def test_validation_ldap_search_string(self): + invalid_ldap_search_strings = [ + "", + "uid={0}", + "(uid={0}", + "uid={0})", + "(&(objectclass=posixgroup)(uid={0})", + "&(objectclass=posixgroup)(uid={0}))", + "(uid=no_placeholder)", + ] # ldap search string must be enclosed in '()' for ldap search to work for finding user and have the same number of opening and closing brackets. + + for invalid_search_string in invalid_ldap_search_strings: + localdoc = self.doc.copy() + localdoc["ldap_search_string"] = invalid_search_string + + with contextlib.suppress(ValidationError): + xhiveframework.get_doc(localdoc).save() + self.fail(f"LDAP search string [{invalid_search_string}] should not validate") + + def test_connect_to_ldap(self): + # prevent these parameters for security or lack of the und user from being able to configure + prevent_connection_parameters = { + "mode": { + "IP_V4_ONLY": "Locks the user to IPv4 without xhiveframework providing a way to configure", + "IP_V6_ONLY": "Locks the user to IPv6 without xhiveframework providing a way to configure", + }, + "auto_bind": { + "NONE": "ldap3.Connection must autobind with base_dn", + "NO_TLS": "ldap3.Connection must have TLS", + "TLS_AFTER_BIND": "[Security] ldap3.Connection TLS bind must occur before bind", + }, + } + + # setup a clean doc with ldap disabled so no validation occurs (this is tested seperatly) + local_doc = self.doc.copy() + local_doc["enabled"] = False + self.test_class = LDAPSettings(self.doc) + + with mock.patch("ldap3.Server") as ldap3_server_method: + with mock.patch("ldap3.Connection", return_value=self.connection) as ldap3_connection_method: + with mock.patch("ldap3.Tls") as ldap3_Tls_method: + function_return = self.test_class.connect_to_ldap( + base_dn=self.base_dn, password=self.base_password + ) + args, kwargs = ldap3_connection_method.call_args + + for connection_arg in kwargs: + if ( + connection_arg in prevent_connection_parameters + and kwargs[connection_arg] in prevent_connection_parameters[connection_arg] + ): + self.fail( + f"ldap3.Connection was called with {kwargs[connection_arg]}, failed reason: [{prevent_connection_parameters[connection_arg][kwargs[connection_arg]]}]" + ) + + tls_version = ssl.PROTOCOL_TLS_CLIENT + if local_doc["require_trusted_certificate"] == "Yes": + tls_validate = ssl.CERT_REQUIRED + tls_configuration = ldap3.Tls(validate=tls_validate, version=tls_version) + + self.assertTrue( + kwargs["auto_bind"] == ldap3.AUTO_BIND_TLS_BEFORE_BIND, + "Security: [ldap3.Connection] autobind TLS before bind with value ldap3.AUTO_BIND_TLS_BEFORE_BIND", + ) + + else: + tls_validate = ssl.CERT_NONE + tls_configuration = ldap3.Tls(validate=tls_validate, version=tls_version) + + self.assertTrue(kwargs["auto_bind"], "ldap3.Connection must autobind") + + ldap3_Tls_method.assert_called_with(validate=tls_validate, version=tls_version) + + ldap3_server_method.assert_called_with( + host=self.doc["ldap_server_url"], tls=tls_configuration + ) + + self.assertTrue( + kwargs["password"] == self.base_password, + "ldap3.Connection password does not match provided password", + ) + + self.assertTrue( + kwargs["raise_exceptions"], + "ldap3.Connection must raise exceptions for error handling", + ) + + self.assertTrue( + kwargs["user"] == self.base_dn, "ldap3.Connection user does not match provided user" + ) + + ldap3_connection_method.assert_called_with( + server=ldap3_server_method.return_value, + auto_bind=True, + password=self.base_password, + raise_exceptions=True, + read_only=True, + user=self.base_dn, + ) + + self.assertTrue( + type(function_return) is Connection, + "The return type must be of ldap3.Connection", + ) + + function_return = self.test_class.connect_to_ldap( + base_dn=self.base_dn, password=self.base_password, read_only=False + ) + + args, kwargs = ldap3_connection_method.call_args + + self.assertFalse( + kwargs["read_only"], + "connect_to_ldap() read_only parameter supplied as False but does not match the ldap3.Connection() read_only named parameter", + ) + + @mock_ldap_connection + def test_get_ldap_client_settings(self): + result = self.test_class.get_ldap_client_settings() + + self.assertIsInstance(result, dict) + self.assertTrue(result["enabled"] == self.doc["enabled"]) # settings should match doc + + localdoc = self.doc.copy() + localdoc["enabled"] = False + xhiveframework.get_doc(localdoc).save() + result = self.test_class.get_ldap_client_settings() + + self.assertFalse(result["enabled"]) # must match the edited doc + + @mock_ldap_connection + def test_update_user_fields(self): + test_user_data = { + "username": "posix.user", + "email": "posix.user1@unit.testing", + "first_name": "posix", + "middle_name": "another", + "last_name": "user", + "phone": "08 1234 5678", + "mobile_no": "0421 123 456", + } + test_user = xhiveframework.get_doc("User", test_user_data["email"]) + self.test_class.update_user_fields(test_user, test_user_data) + updated_user = xhiveframework.get_doc("User", test_user_data["email"]) + + self.assertTrue(updated_user.middle_name == test_user_data["middle_name"]) + self.assertTrue(updated_user.last_name == test_user_data["last_name"]) + self.assertTrue(updated_user.phone == test_user_data["phone"]) + self.assertTrue(updated_user.mobile_no == test_user_data["mobile_no"]) + + self.assertEqual(updated_user.user_type, self.test_class.default_user_type) + self.assertIn(self.test_class.default_role, xhiveframework.get_roles(updated_user.name)) + + @mock_ldap_connection + def test_create_website_user(self): + new_test_user_data = { + "username": "website_ldap_user.test", + "email": "website_ldap_user@test.com", + "first_name": "Website User - LDAP Test", + } + self.test_class.default_user_type = "Website User" + self.test_class.create_or_update_user(user_data=new_test_user_data, groups=[]) + new_user = xhiveframework.get_doc("User", new_test_user_data["email"]) + self.assertEqual(new_user.user_type, "Website User") + + @mock_ldap_connection + def test_sync_roles(self): + if self.TEST_LDAP_SERVER.lower() == "openldap": + test_user_data = { + "posix.user1": [ + "Users", + "Administrators", + "default_role", + "xhiveframework_default_all", + "xhiveframework_default_guest", + "xhiveframework_default_desk_user", + ], + "posix.user2": [ + "Users", + "Group3", + "default_role", + "xhiveframework_default_all", + "xhiveframework_default_guest", + "xhiveframework_default_desk_user", + ], + } + + elif self.TEST_LDAP_SERVER.lower() == "active directory": + test_user_data = { + "posix.user1": [ + "Domain Users", + "Domain Administrators", + "default_role", + "xhiveframework_default_all", + "xhiveframework_default_guest", + "xhiveframework_default_desk_user", + ], + "posix.user2": [ + "Domain Users", + "Enterprise Administrators", + "default_role", + "xhiveframework_default_all", + "xhiveframework_default_guest", + "xhiveframework_default_desk_user", + ], + } + + role_to_group_map = { + self.doc["ldap_groups"][0]["xhiveerp_role"]: self.doc["ldap_groups"][0]["ldap_group"], + self.doc["ldap_groups"][1]["xhiveerp_role"]: self.doc["ldap_groups"][1]["ldap_group"], + self.doc["ldap_groups"][2]["xhiveerp_role"]: self.doc["ldap_groups"][2]["ldap_group"], + "Newsletter Manager": "default_role", + "All": "xhiveframework_default_all", + "Guest": "xhiveframework_default_guest", + "Desk User": "xhiveframework_default_desk_user", + } + + # re-create user1 to ensure clean + xhiveframework.get_doc("User", "posix.user1@unit.testing").delete() + user = xhiveframework.get_doc(self.user1doc) + user.insert(ignore_permissions=True) + + for test_user in test_user_data: + test_user_doc = xhiveframework.get_doc("User", f"{test_user}@unit.testing") + test_user_roles = xhiveframework.get_roles(f"{test_user}@unit.testing") + + self.assertTrue( + len(test_user_roles) == 2, "User should only be a part of the All and Guest roles" + ) # check default xhiveframework roles + + self.test_class.sync_roles(test_user_doc, test_user_data[test_user]) # update user roles + + xhiveframework.get_doc("User", f"{test_user}@unit.testing") + updated_user_roles = xhiveframework.get_roles(f"{test_user}@unit.testing") + + self.assertTrue( + len(updated_user_roles) == len(test_user_data[test_user]), + f"syncing of the user roles failed. {len(updated_user_roles)} != {len(test_user_data[test_user])} for user {test_user}", + ) + + for user_role in updated_user_roles: # match each users role mapped to ldap groups + self.assertTrue( + role_to_group_map[user_role] in test_user_data[test_user], + f"during sync_roles(), the user was given role {user_role} which should not have occured", + ) + + @mock_ldap_connection + def test_create_or_update_user(self): + test_user_data = { + "posix.user1": [ + "Users", + "Administrators", + "default_role", + "xhiveframework_default_all", + "xhiveframework_default_guest", + ], + } + test_user = "posix.user1" + + xhiveframework.get_doc("User", f"{test_user}@unit.testing").delete() + + with self.assertRaises( + xhiveframework.exceptions.DoesNotExistError + ): # ensure user deleted so function can be tested + xhiveframework.get_doc("User", f"{test_user}@unit.testing") + + with mock.patch( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.LDAPSettings.update_user_fields" + ) as update_user_fields_method: + with mock.patch( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.LDAPSettings.sync_roles" + ) as sync_roles_method: + # New user + self.test_class.create_or_update_user(self.user1doc, test_user_data[test_user]) + + self.assertTrue(sync_roles_method.called, "User roles need to be updated for a new user") + self.assertFalse( + update_user_fields_method.called, + "User roles are not required to be updated for a new user, this will occur during logon", + ) + + # Existing user + self.test_class.create_or_update_user(self.user1doc, test_user_data[test_user]) + + self.assertTrue( + sync_roles_method.called, "User roles need to be updated for an existing user" + ) + self.assertTrue( + update_user_fields_method.called, "User fields need to be updated for an existing user" + ) + + @mock_ldap_connection + def test_get_ldap_attributes(self): + method_return = self.test_class.get_ldap_attributes() + self.assertTrue(isinstance(method_return, list)) + + @mock_ldap_connection + def test_fetch_ldap_groups(self): + if self.TEST_LDAP_SERVER.lower() == "openldap": + test_users = {"posix.user": ["Users", "Administrators"], "posix.user2": ["Users", "Group3"]} + elif self.TEST_LDAP_SERVER.lower() == "active directory": + test_users = { + "posix.user": ["Domain Users", "Domain Administrators"], + "posix.user2": ["Domain Users", "Enterprise Administrators"], + } + + for test_user in test_users: + self.connection.search( + search_base=self.ldap_user_path, + search_filter=self.TEST_LDAP_SEARCH_STRING.format(test_user), + attributes=self.test_class.get_ldap_attributes(), + ) + + method_return = self.test_class.fetch_ldap_groups(self.connection.entries[0], self.connection) + + self.assertIsInstance(method_return, list) + self.assertTrue(len(method_return) == len(test_users[test_user])) + + for returned_group in method_return: + self.assertTrue(returned_group in test_users[test_user]) + + @mock_ldap_connection + def test_authenticate(self): + with mock.patch( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.LDAPSettings.fetch_ldap_groups" + ) as fetch_ldap_groups_function: + self.assertTrue(self.test_class.authenticate("posix.user", "posix_user_password")) + + self.assertTrue( + fetch_ldap_groups_function.called, + "As part of authentication function fetch_ldap_groups_function needs to be called", + ) + + invalid_users = [ + {"prefix_posix.user": "posix_user_password"}, + {"posix.user_postfix": "posix_user_password"}, + {"posix.user": "posix_user_password_postfix"}, + {"posix.user": "prefix_posix_user_password"}, + {"posix.user": ""}, + {"": "posix_user_password"}, + {"": ""}, + ] # All invalid users should return 'invalid username or password' + + for username, password in enumerate(invalid_users): + with self.assertRaises(xhiveframework.exceptions.ValidationError) as display_massage: + self.test_class.authenticate(username, password) + + self.assertTrue( + str(display_massage.exception).lower() == "invalid username or password", + f"invalid credentials passed authentication [user: {username}, password: {password}]", + ) + + @mock_ldap_connection + def test_complex_ldap_search_filter(self): + ldap_search_filters = self.TEST_VALUES_LDAP_COMPLEX_SEARCH_STRING + + for search_filter in ldap_search_filters: + self.test_class.ldap_search_string = search_filter + + if ( + "ACCESS:test3" in search_filter + ): # posix.user does not have str in ldap.description auth should fail + with self.assertRaises(xhiveframework.exceptions.ValidationError) as display_massage: + self.test_class.authenticate("posix.user", "posix_user_password") + + self.assertTrue(str(display_massage.exception).lower() == "invalid username or password") + + else: + self.assertTrue(self.test_class.authenticate("posix.user", "posix_user_password")) + + def test_reset_password(self): + self.test_class = LDAPSettings(self.doc) + + # Create a clean doc + localdoc = self.doc.copy() + localdoc["enabled"] = False + xhiveframework.get_doc(localdoc).save() + + with mock.patch( + "xhiveframework.integrations.doctype.ldap_settings.ldap_settings.LDAPSettings.connect_to_ldap", + return_value=self.connection, + ) as connect_to_ldap: + with self.assertRaises( + xhiveframework.exceptions.ValidationError + ) as validation: # Fail if username string used + self.test_class.reset_password("posix.user", "posix_user_password") + self.assertTrue(str(validation.exception) == "No LDAP User found for email: posix.user") + + with contextlib.suppress(Exception): + self.test_class.reset_password( + "posix.user1@unit.testing", "posix_user_password" + ) # Change Password + connect_to_ldap.assert_called_with(self.base_dn, self.base_password, read_only=False) + + @mock_ldap_connection + def test_convert_ldap_entry_to_dict(self): + self.connection.search( + search_base=self.ldap_user_path, + search_filter=self.TEST_LDAP_SEARCH_STRING.format("posix.user"), + attributes=self.test_class.get_ldap_attributes(), + ) + test_ldap_entry = self.connection.entries[0] + method_return = self.test_class.convert_ldap_entry_to_dict(test_ldap_entry) + + self.assertTrue(isinstance(method_return, dict)) # must be dict + self.assertTrue(len(method_return) == 6) # there are 6 fields in mock_ldap for use + + +class Test_OpenLDAP(LDAP_TestCase, TestCase): + TEST_LDAP_SERVER = "OpenLDAP" + TEST_LDAP_SEARCH_STRING = "(uid={0})" + DOCUMENT_GROUP_MAPPINGS: typing.ClassVar[list] = [ + { + "doctype": "LDAP Group Mapping", + "ldap_group": "Administrators", + "xhiveerp_role": "System Manager", + }, + {"doctype": "LDAP Group Mapping", "ldap_group": "Users", "xhiveerp_role": "Blogger"}, + {"doctype": "LDAP Group Mapping", "ldap_group": "Group3", "xhiveerp_role": "Accounts User"}, + ] + LDAP_USERNAME_FIELD = "uid" + LDAP_SCHEMA = OFFLINE_SLAPD_2_4 + LDAP_LDIF_JSON = "test_data_ldif_openldap.json" + + TEST_VALUES_LDAP_COMPLEX_SEARCH_STRING: typing.ClassVar[list] = [ + "(uid={0})", + "(&(objectclass=posixaccount)(uid={0}))", + "(&(description=*ACCESS:test1*)(uid={0}))", # OpenLDAP has no member of group, use description to filter posix.user has equivilent of AD 'memberOf' + "(&(objectclass=posixaccount)(description=*ACCESS:test3*)(uid={0}))", # OpenLDAP has no member of group, use description to filter posix.user doesn't have. equivilent of AD 'memberOf' + ] + + +class Test_ActiveDirectory(LDAP_TestCase, TestCase): + TEST_LDAP_SERVER = "Active Directory" + TEST_LDAP_SEARCH_STRING = "(samaccountname={0})" + DOCUMENT_GROUP_MAPPINGS: typing.ClassVar[list] = [ + { + "doctype": "LDAP Group Mapping", + "ldap_group": "Domain Administrators", + "xhiveerp_role": "System Manager", + }, + {"doctype": "LDAP Group Mapping", "ldap_group": "Domain Users", "xhiveerp_role": "Blogger"}, + { + "doctype": "LDAP Group Mapping", + "ldap_group": "Enterprise Administrators", + "xhiveerp_role": "Accounts User", + }, + ] + LDAP_USERNAME_FIELD = "samaccountname" + LDAP_SCHEMA = OFFLINE_AD_2012_R2 + LDAP_LDIF_JSON = "test_data_ldif_activedirectory.json" + + TEST_VALUES_LDAP_COMPLEX_SEARCH_STRING: typing.ClassVar[dict] = [ + "(samaccountname={0})", + "(&(objectclass=user)(samaccountname={0}))", + "(&(description=*ACCESS:test1*)(samaccountname={0}))", # OpenLDAP has no member of group, use description to filter posix.user has equivilent of AD 'memberOf' + "(&(objectclass=user)(description=*ACCESS:test3*)(samaccountname={0}))", # OpenLDAP has no member of group, use description to filter posix.user doesn't have. equivilent of AD 'memberOf' + ] diff --git a/xhiveframework/integrations/doctype/oauth_authorization_code/__init__.py b/xhiveframework/integrations/doctype/oauth_authorization_code/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.js b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.js new file mode 100644 index 0000000..9f1998b --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("OAuth Authorization Code", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json new file mode 100644 index 0000000..2cd21bc --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.json @@ -0,0 +1,112 @@ +{ + "actions": [], + "autoname": "field:authorization_code", + "creation": "2016-08-24 14:12:13.647159", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "client", + "user", + "scopes", + "authorization_code", + "expiration_time", + "redirect_uri_bound_to_authorization_code", + "validity", + "nonce", + "code_challenge", + "code_challenge_method" + ], + "fields": [ + { + "fieldname": "client", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Client", + "options": "OAuth Client", + "read_only": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "scopes", + "fieldtype": "Text", + "label": "Scopes", + "read_only": 1 + }, + { + "fieldname": "authorization_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Authorization Code", + "read_only": 1, + "unique": 1 + }, + { + "fieldname": "expiration_time", + "fieldtype": "Datetime", + "label": "Expiration time", + "read_only": 1 + }, + { + "fieldname": "redirect_uri_bound_to_authorization_code", + "fieldtype": "Data", + "label": "Redirect URI Bound To Auth Code", + "read_only": 1 + }, + { + "fieldname": "validity", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Validity", + "options": "Valid\nInvalid", + "read_only": 1 + }, + { + "fieldname": "nonce", + "fieldtype": "Data", + "label": "nonce", + "read_only": 1 + }, + { + "fieldname": "code_challenge", + "fieldtype": "Data", + "label": "Code Challenge", + "read_only": 1 + }, + { + "fieldname": "code_challenge_method", + "fieldtype": "Select", + "label": "Code challenge method", + "options": "\ns256\nplain", + "read_only": 1 + } + ], + "links": [], + "modified": "2021-04-26 07:23:02.980612", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Authorization Code", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py new file mode 100644 index 0000000..e50c8b8 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_authorization_code/oauth_authorization_code.py @@ -0,0 +1,28 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class OAuthAuthorizationCode(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + authorization_code: DF.Data | None + client: DF.Link | None + code_challenge: DF.Data | None + code_challenge_method: DF.Literal["", "s256", "plain"] + expiration_time: DF.Datetime | None + nonce: DF.Data | None + redirect_uri_bound_to_authorization_code: DF.Data | None + scopes: DF.Text | None + user: DF.Link | None + validity: DF.Literal["Valid", "Invalid"] + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py b/xhiveframework/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py new file mode 100644 index 0000000..4999a50 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_authorization_code/test_oauth_authorization_code.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('OAuth Authorization Code') + + +class TestOAuthAuthorizationCode(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/oauth_bearer_token/__init__.py b/xhiveframework/integrations/doctype/oauth_bearer_token/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.js b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.js new file mode 100644 index 0000000..82cb952 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("OAuth Bearer Token", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json new file mode 100644 index 0000000..2060c48 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.json @@ -0,0 +1,99 @@ +{ + "actions": [], + "autoname": "field:access_token", + "creation": "2016-08-24 14:10:17.471264", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "client", + "user", + "scopes", + "access_token", + "refresh_token", + "expiration_time", + "expires_in", + "status" + ], + "fields": [ + { + "fieldname": "client", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Client", + "options": "OAuth Client", + "read_only": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "scopes", + "fieldtype": "Text", + "label": "Scopes", + "read_only": 1 + }, + { + "fieldname": "access_token", + "fieldtype": "Data", + "label": "Access Token", + "read_only": 1, + "unique": 1 + }, + { + "fieldname": "refresh_token", + "fieldtype": "Data", + "label": "Refresh Token", + "read_only": 1 + }, + { + "fieldname": "expiration_time", + "fieldtype": "Datetime", + "label": "Expiration time", + "read_only": 1 + }, + { + "fieldname": "expires_in", + "fieldtype": "Int", + "label": "Expires In", + "read_only": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Active\nRevoked", + "read_only": 1 + } + ], + "links": [], + "modified": "2023-04-07 07:08:00.249740", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Bearer Token", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py new file mode 100644 index 0000000..1796eec --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_bearer_token/oauth_bearer_token.py @@ -0,0 +1,31 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class OAuthBearerToken(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + access_token: DF.Data | None + client: DF.Link | None + expiration_time: DF.Datetime | None + expires_in: DF.Int + refresh_token: DF.Data | None + scopes: DF.Text | None + status: DF.Literal["Active", "Revoked"] + user: DF.Link | None + + # end: auto-generated types + def validate(self): + if not self.expiration_time: + self.expiration_time = xhiveframework.utils.datetime.datetime.strptime( + self.creation, "%Y-%m-%d %H:%M:%S.%f" + ) + xhiveframework.utils.datetime.timedelta(seconds=self.expires_in) diff --git a/xhiveframework/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py b/xhiveframework/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py new file mode 100644 index 0000000..2f5da74 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_bearer_token/test_oauth_bearer_token.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('OAuth Bearer Token') + + +class TestOAuthBearerToken(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/oauth_client/__init__.py b/xhiveframework/integrations/doctype/oauth_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/oauth_client/oauth_client.js b/xhiveframework/integrations/doctype/oauth_client/oauth_client.js new file mode 100644 index 0000000..2318796 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_client/oauth_client.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("OAuth Client", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/oauth_client/oauth_client.json b/xhiveframework/integrations/doctype/oauth_client/oauth_client.json new file mode 100644 index 0000000..597cd06 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_client/oauth_client.json @@ -0,0 +1,144 @@ +{ + "actions": [], + "creation": "2016-08-24 14:07:21.955052", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "client_id", + "app_name", + "user", + "cb_1", + "client_secret", + "skip_authorization", + "sb_1", + "scopes", + "cb_3", + "redirect_uris", + "default_redirect_uri", + "sb_advanced", + "grant_type", + "cb_2", + "response_type" + ], + "fields": [ + { + "fieldname": "client_id", + "fieldtype": "Data", + "label": "App Client ID", + "read_only": 1 + }, + { + "fieldname": "app_name", + "fieldtype": "Data", + "label": "App Name", + "reqd": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "hidden": 1, + "label": "User", + "options": "User" + }, + { + "fieldname": "cb_1", + "fieldtype": "Column Break" + }, + { + "fieldname": "client_secret", + "fieldtype": "Data", + "label": "App Client Secret", + "read_only": 1 + }, + { + "default": "0", + "description": "If checked, users will not see the Confirm Access dialog.", + "fieldname": "skip_authorization", + "fieldtype": "Check", + "label": "Skip Authorization" + }, + { + "fieldname": "sb_1", + "fieldtype": "Section Break" + }, + { + "default": "all openid", + "description": "A list of resources which the Client App will have access to after the user allows it.
      e.g. project", + "fieldname": "scopes", + "fieldtype": "Text", + "label": "Scopes", + "reqd": 1 + }, + { + "fieldname": "cb_3", + "fieldtype": "Column Break" + }, + { + "description": "URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n
      e.g. http://hostname/api/method/xhiveframework.integrations.oauth2_logins.login_via_facebook", + "fieldname": "redirect_uris", + "fieldtype": "Text", + "label": "Redirect URIs" + }, + { + "fieldname": "default_redirect_uri", + "fieldtype": "Data", + "label": "Default Redirect URI", + "reqd": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "1", + "fieldname": "sb_advanced", + "fieldtype": "Section Break", + "label": "Advanced Settings" + }, + { + "fieldname": "grant_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Grant Type", + "options": "Authorization Code\nImplicit" + }, + { + "fieldname": "cb_2", + "fieldtype": "Column Break" + }, + { + "default": "Code", + "fieldname": "response_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Response Type", + "options": "Code\nToken" + } + ], + "links": [], + "modified": "2023-07-17 07:06:35.765981", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Client", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "app_name", + "track_changes": 1 +} diff --git a/xhiveframework/integrations/doctype/oauth_client/oauth_client.py b/xhiveframework/integrations/doctype/oauth_client/oauth_client.py new file mode 100644 index 0000000..7ceb15e --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_client/oauth_client.py @@ -0,0 +1,47 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class OAuthClient(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + app_name: DF.Data + client_id: DF.Data | None + client_secret: DF.Data | None + default_redirect_uri: DF.Data + grant_type: DF.Literal["Authorization Code", "Implicit"] + redirect_uris: DF.Text | None + response_type: DF.Literal["Code", "Token"] + scopes: DF.Text + skip_authorization: DF.Check + user: DF.Link | None + + # end: auto-generated types + def validate(self): + self.client_id = self.name + if not self.client_secret: + self.client_secret = xhiveframework.generate_hash(length=10) + self.validate_grant_and_response() + + def validate_grant_and_response(self): + if ( + self.grant_type == "Authorization Code" + and self.response_type != "Code" + or self.grant_type == "Implicit" + and self.response_type != "Token" + ): + xhiveframework.throw( + _( + "Combination of Grant Type ({0}) and Response Type ({1}) not allowed" + ).format(self.grant_type, self.response_type) + ) diff --git a/xhiveframework/integrations/doctype/oauth_client/test_oauth_client.py b/xhiveframework/integrations/doctype/oauth_client/test_oauth_client.py new file mode 100644 index 0000000..6952844 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_client/test_oauth_client.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +# test_records = xhiveframework.get_test_records('OAuth Client') + + +class TestOAuthClient(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/oauth_provider_settings/__init__.py b/xhiveframework/integrations/doctype/oauth_provider_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.js b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.js new file mode 100644 index 0000000..9093bbb --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.js @@ -0,0 +1,6 @@ +// Copyright (c) 2016, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("OAuth Provider Settings", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.json b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.json new file mode 100644 index 0000000..219a87f --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2016-09-03 11:42:42.575525", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "skip_authorization" + ], + "fields": [ + { + "fieldname": "skip_authorization", + "fieldtype": "Select", + "label": "Skip Authorization", + "options": "Force\nAuto" + } + ], + "issingle": 1, + "links": [], + "modified": "2022-08-03 12:20:52.328415", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Provider Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py new file mode 100644 index 0000000..4488334 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py @@ -0,0 +1,27 @@ +# Copyright (c) 2015, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class OAuthProviderSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + skip_authorization: DF.Literal["Force", "Auto"] + # end: auto-generated types + pass + + +def get_oauth_settings(): + """Returns oauth settings""" + return xhiveframework._dict( + {"skip_authorization": xhiveframework.db.get_single_value("OAuth Provider Settings", "skip_authorization")} + ) diff --git a/xhiveframework/integrations/doctype/oauth_scope/__init__.py b/xhiveframework/integrations/doctype/oauth_scope/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.json b/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.json new file mode 100644 index 0000000..3a6e528 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.json @@ -0,0 +1,30 @@ +{ + "actions": [], + "creation": "2020-07-15 22:08:14.616585", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "scope" + ], + "fields": [ + { + "fieldname": "scope", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Scope" + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-15 22:15:18.930632", + "modified_by": "Administrator", + "module": "Integrations", + "name": "OAuth Scope", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.py b/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.py new file mode 100644 index 0000000..fcb2e12 --- /dev/null +++ b/xhiveframework/integrations/doctype/oauth_scope/oauth_scope.py @@ -0,0 +1,22 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class OAuthScope(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + scope: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/push_notification_settings/__init__.py b/xhiveframework/integrations/doctype/push_notification_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.js b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.js new file mode 100644 index 0000000..a1ad92a --- /dev/null +++ b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +// xhiveframework.ui.form.on("Push Notification Settings", { +// refresh(frm) { + +// }, +// }); diff --git a/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.json b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.json new file mode 100644 index 0000000..da1d637 --- /dev/null +++ b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.json @@ -0,0 +1,69 @@ +{ + "actions": [], + "allow_rename": 1, + "beta": 1, + "creation": "2024-01-04 11:36:08.013039", + "description": "Enabling this will register your site on a central relay server to send push notifications for all installed apps through Firebase Cloud Messaging. This server only stores user tokens and error logs, and no messages are saved.", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_qgjr", + "enable_push_notification_relay", + "authentication_credential_section", + "api_key", + "api_secret" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable_push_notification_relay", + "fieldtype": "Check", + "label": "Enable Push Notification Relay" + }, + { + "description": "API Key and Secret to interact with the relay server. These will be auto-generated when the first push notification is sent from any of the apps installed on this site.", + "fieldname": "authentication_credential_section", + "fieldtype": "Section Break", + "label": "Authentication" + }, + { + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key" + }, + { + "fieldname": "api_secret", + "fieldtype": "Password", + "label": "API Secret" + }, + { + "description": "Enabling this will register your site on a central relay server to send push notifications for all installed apps through Firebase Cloud Messaging. This server only stores user tokens and error logs, and no messages are saved. ", + "fieldname": "section_break_qgjr", + "fieldtype": "Section Break", + "label": "Relay Settings" + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2024-02-28 11:03:30.518196", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Push Notification Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.py b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.py new file mode 100644 index 0000000..9d247f7 --- /dev/null +++ b/xhiveframework/integrations/doctype/push_notification_settings/push_notification_settings.py @@ -0,0 +1,31 @@ +# Copyright (c) 2024, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class PushNotificationSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + api_key: DF.Data | None + api_secret: DF.Password | None + enable_push_notification_relay: DF.Check + # end: auto-generated types + + def validate(self): + self.validate_relay_server_setup() + + def validate_relay_server_setup(self): + if self.enable_push_notification_relay and not xhiveframework.conf.get("push_relay_server_url"): + xhiveframework.throw( + _("The Push Relay Server URL key (`push_relay_server_url`) is missing in your site config"), + title=_("Relay Server URL missing"), + ) diff --git a/xhiveframework/integrations/doctype/push_notification_settings/test_push_notification_settings.py b/xhiveframework/integrations/doctype/push_notification_settings/test_push_notification_settings.py new file mode 100644 index 0000000..3a268bb --- /dev/null +++ b/xhiveframework/integrations/doctype/push_notification_settings/test_push_notification_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPushNotificationSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/query_parameters/__init__.py b/xhiveframework/integrations/doctype/query_parameters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/query_parameters/query_parameters.json b/xhiveframework/integrations/doctype/query_parameters/query_parameters.json new file mode 100644 index 0000000..de31c28 --- /dev/null +++ b/xhiveframework/integrations/doctype/query_parameters/query_parameters.json @@ -0,0 +1,37 @@ +{ + "actions": [], + "creation": "2020-11-16 14:54:37.226914", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "key", + "value" + ], + "fields": [ + { + "fieldname": "key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "reqd": 1 + }, + { + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Value", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-11-16 15:18:35.887149", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Query Parameters", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/query_parameters/query_parameters.py b/xhiveframework/integrations/doctype/query_parameters/query_parameters.py new file mode 100644 index 0000000..8c640b7 --- /dev/null +++ b/xhiveframework/integrations/doctype/query_parameters/query_parameters.py @@ -0,0 +1,23 @@ +# Copyright (c) 2020, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class QueryParameters(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + key: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + value: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/s3_backup_settings/__init__.py b/xhiveframework/integrations/doctype/s3_backup_settings/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.js b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.js new file mode 100755 index 0000000..af00001 --- /dev/null +++ b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.js @@ -0,0 +1,26 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("S3 Backup Settings", { + refresh: function (frm) { + frm.clear_custom_buttons(); + frm.events.take_backup(frm); + }, + + take_backup: function (frm) { + if (frm.doc.access_key_id && frm.doc.secret_access_key) { + frm.add_custom_button(__("Take Backup Now"), function () { + frm.dashboard.set_headline_alert("S3 Backup Started!"); + xhiveframework.call({ + method: "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", + callback: function (r) { + if (!r.exc) { + xhiveframework.msgprint(__("S3 Backup complete!")); + frm.dashboard.clear_headline(); + } + }, + }); + }).addClass("btn-primary"); + } + }, +}); diff --git a/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.json b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.json new file mode 100755 index 0000000..e225686 --- /dev/null +++ b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.json @@ -0,0 +1,155 @@ +{ + "actions": [], + "creation": "2017-09-04 20:57:20.129205", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enabled", + "api_access_section", + "access_key_id", + "column_break_4", + "secret_access_key", + "notification_section", + "notify_email", + "column_break_8", + "send_email_for_successful_backup", + "s3_bucket_details_section", + "bucket", + "endpoint_url", + "column_break_13", + "backup_details_section", + "frequency", + "backup_files" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enable Automatic Backup" + }, + { + "fieldname": "notify_email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Send Notifications To", + "mandatory_depends_on": "enabled", + "reqd": 1 + }, + { + "default": "1", + "description": "By default, emails are only sent for failed backups.", + "fieldname": "send_email_for_successful_backup", + "fieldtype": "Check", + "label": "Send Email for Successful Backup" + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Backup Frequency", + "mandatory_depends_on": "enabled", + "options": "Daily\nWeekly\nMonthly\nNone", + "reqd": 1 + }, + { + "fieldname": "access_key_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Access Key ID", + "mandatory_depends_on": "enabled", + "reqd": 1 + }, + { + "fieldname": "secret_access_key", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Access Key Secret", + "mandatory_depends_on": "enabled", + "reqd": 1 + }, + { + "default": "https://s3.amazonaws.com", + "description": "Only change this if you want to use other S3 compatible object storage backends.", + "fieldname": "endpoint_url", + "fieldtype": "Data", + "label": "Endpoint URL" + }, + { + "fieldname": "bucket", + "fieldtype": "Data", + "label": "Bucket Name", + "mandatory_depends_on": "enabled", + "reqd": 1 + }, + { + "depends_on": "enabled", + "fieldname": "api_access_section", + "fieldtype": "Section Break", + "label": "API Access" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "depends_on": "enabled", + "fieldname": "notification_section", + "fieldtype": "Section Break", + "label": "Notification" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "depends_on": "enabled", + "fieldname": "s3_bucket_details_section", + "fieldtype": "Section Break", + "label": "S3 Bucket Details" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "depends_on": "enabled", + "fieldname": "backup_details_section", + "fieldtype": "Section Break", + "label": "Backup Details" + }, + { + "default": "1", + "description": "Backup public and private files along with the database.", + "fieldname": "backup_files", + "fieldtype": "Check", + "label": "Backup Files" + } + ], + "hide_toolbar": 1, + "issingle": 1, + "links": [], + "modified": "2023-01-11 15:38:20.333833", + "modified_by": "Administrator", + "module": "Integrations", + "name": "S3 Backup Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.py new file mode 100755 index 0000000..1430932 --- /dev/null +++ b/xhiveframework/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -0,0 +1,196 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +import os +import os.path + +import boto3 +from botocore.exceptions import ClientError +from rq.timeouts import JobTimeoutException + +import xhiveframework +from xhiveframework import _ +from xhiveframework.integrations.offsite_backup_utils import ( + generate_files_backup, + get_latest_backup_file, + send_email, + validate_file_size, +) +from xhiveframework.model.document import Document +from xhiveframework.utils import cint +from xhiveframework.utils.background_jobs import enqueue + + +class S3BackupSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + access_key_id: DF.Data + backup_files: DF.Check + bucket: DF.Data + enabled: DF.Check + endpoint_url: DF.Data | None + frequency: DF.Literal["Daily", "Weekly", "Monthly", "None"] + notify_email: DF.Data + secret_access_key: DF.Password + send_email_for_successful_backup: DF.Check + + # end: auto-generated types + def validate(self): + if not self.enabled: + return + + if not self.endpoint_url: + self.endpoint_url = "https://s3.amazonaws.com" + + conn = boto3.client( + "s3", + aws_access_key_id=self.access_key_id, + aws_secret_access_key=self.get_password("secret_access_key"), + endpoint_url=self.endpoint_url, + ) + + try: + # Head_bucket returns a 200 OK if the bucket exists and have access to it. + # Requires ListBucket permission + conn.head_bucket(Bucket=self.bucket) + except ClientError as e: + error_code = e.response["Error"]["Code"] + bucket_name = xhiveframework.bold(self.bucket) + if error_code == "403": + msg = _("Do not have permission to access bucket {0}.").format(bucket_name) + elif error_code == "404": + msg = _("Bucket {0} not found.").format(bucket_name) + else: + msg = e.args[0] + + xhiveframework.throw(msg) + + +@xhiveframework.whitelist() +def take_backup(): + """Enqueue longjob for taking backup to s3""" + enqueue( + "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", + queue="long", + timeout=1500, + ) + xhiveframework.msgprint(_("Queued for backup. It may take a few minutes to an hour.")) + + +def take_backups_daily(): + take_backups_if("Daily") + + +def take_backups_weekly(): + take_backups_if("Weekly") + + +def take_backups_monthly(): + take_backups_if("Monthly") + + +def take_backups_if(freq): + if cint(xhiveframework.db.get_single_value("S3 Backup Settings", "enabled")): + if xhiveframework.db.get_single_value("S3 Backup Settings", "frequency") == freq: + take_backups_s3() + + +@xhiveframework.whitelist() +def take_backups_s3(retry_count=0): + try: + validate_file_size() + backup_to_s3() + send_email(True, "Amazon S3", "S3 Backup Settings", "notify_email") + except JobTimeoutException: + if retry_count < 2: + args = {"retry_count": retry_count + 1} + enqueue( + "xhiveframework.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", + queue="long", + timeout=1500, + **args, + ) + else: + notify() + except Exception: + notify() + + +def notify(): + error_message = xhiveframework.get_traceback() + send_email(False, "Amazon S3", "S3 Backup Settings", "notify_email", error_message) + + +def backup_to_s3(): + from xhiveframework.utils import get_backups_path + from xhiveframework.utils.backups import new_backup + + doc = xhiveframework.get_single("S3 Backup Settings") + bucket = doc.bucket + backup_files = cint(doc.backup_files) + + conn = boto3.client( + "s3", + aws_access_key_id=doc.access_key_id, + aws_secret_access_key=doc.get_password("secret_access_key"), + endpoint_url=doc.endpoint_url or "https://s3.amazonaws.com", + ) + + if xhiveframework.flags.create_new_backup: + backup = new_backup( + ignore_files=False, + backup_path_db=None, + backup_path_files=None, + backup_path_private_files=None, + force=True, + ) + db_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db)) + site_config = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_conf)) + if backup_files: + files_filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_files)) + private_files = os.path.join( + get_backups_path(), os.path.basename(backup.backup_path_private_files) + ) + else: + if backup_files: + db_filename, site_config, files_filename, private_files = get_latest_backup_file( + with_files=backup_files + ) + + if not files_filename or not private_files: + generate_files_backup() + db_filename, site_config, files_filename, private_files = get_latest_backup_file( + with_files=backup_files + ) + + else: + db_filename, site_config = get_latest_backup_file() + + folder = os.path.basename(db_filename)[:15] + "/" + # for adding datetime to folder name + + upload_file_to_s3(db_filename, folder, conn, bucket) + upload_file_to_s3(site_config, folder, conn, bucket) + + if backup_files: + if private_files: + upload_file_to_s3(private_files, folder, conn, bucket) + + if files_filename: + upload_file_to_s3(files_filename, folder, conn, bucket) + + +def upload_file_to_s3(filename, folder, conn, bucket): + destpath = os.path.join(folder, os.path.basename(filename)) + try: + print("Uploading file:", filename) + conn.upload_file(filename, bucket, destpath) # Requires PutObject permission + + except Exception as e: + xhiveframework.log_error() + print("Error uploading: %s" % (e)) diff --git a/xhiveframework/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py b/xhiveframework/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py new file mode 100755 index 0000000..ac58c6f --- /dev/null +++ b/xhiveframework/integrations/doctype/s3_backup_settings/test_s3_backup_settings.py @@ -0,0 +1,7 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestS3BackupSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/slack_webhook_url/__init__.py b/xhiveframework/integrations/doctype/slack_webhook_url/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.js b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.js new file mode 100644 index 0000000..655a064 --- /dev/null +++ b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.js @@ -0,0 +1,4 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Slack Webhook URL", {}); diff --git a/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.json b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.json new file mode 100644 index 0000000..56a76b9 --- /dev/null +++ b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "autoname": "field:webhook_name", + "creation": "2018-05-22 13:20:51.450815", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "webhook_name", + "webhook_url", + "show_document_link" + ], + "fields": [ + { + "fieldname": "webhook_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "webhook_url", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Webhook URL", + "reqd": 1 + }, + { + "allow_in_quick_entry": 1, + "default": "1", + "fieldname": "show_document_link", + "fieldtype": "Check", + "label": "Show link to document" + } + ], + "links": [], + "modified": "2021-05-12 18:24:37.810235", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Slack Webhook URL", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.py b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.py new file mode 100644 index 0000000..f44f3bc --- /dev/null +++ b/xhiveframework/integrations/doctype/slack_webhook_url/slack_webhook_url.py @@ -0,0 +1,67 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import requests + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import get_url_to_form + +error_messages = { + 400: "400: Invalid Payload or User not found", + 403: "403: Action Prohibited", + 404: "404: Channel not found", + 410: "410: The Channel is Archived", + 500: "500: Rollup Error, Slack seems to be down", +} + + +class SlackWebhookURL(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + show_document_link: DF.Check + webhook_name: DF.Data + webhook_url: DF.Data + # end: auto-generated types + pass + + +def send_slack_message(webhook_url, message, reference_doctype, reference_name): + data = {"text": message, "attachments": []} + + slack_url, show_link = xhiveframework.db.get_value( + "Slack Webhook URL", webhook_url, ["webhook_url", "show_document_link"] + ) + + if show_link: + doc_url = get_url_to_form(reference_doctype, reference_name) + link_to_doc = { + "fallback": _("See the document at {0}").format(doc_url), + "actions": [ + { + "type": "button", + "text": _("Go to the document"), + "url": doc_url, + "style": "primary", + } + ], + } + data["attachments"].append(link_to_doc) + + r = requests.post(slack_url, data=json.dumps(data)) + + if not r.ok: + message = error_messages.get(r.status_code, r.status_code) + xhiveframework.log_error(message, _("Slack Webhook Error")) + return "error" + + return "success" diff --git a/xhiveframework/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py b/xhiveframework/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py new file mode 100644 index 0000000..7f8ae1a --- /dev/null +++ b/xhiveframework/integrations/doctype/slack_webhook_url/test_slack_webhook_url.py @@ -0,0 +1,7 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestSlackWebhookURL(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/social_login_key/__init__.py b/xhiveframework/integrations/doctype/social_login_key/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/social_login_key/social_login_key.js b/xhiveframework/integrations/doctype/social_login_key/social_login_key.js new file mode 100644 index 0000000..4031e6d --- /dev/null +++ b/xhiveframework/integrations/doctype/social_login_key/social_login_key.js @@ -0,0 +1,84 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt +const fields = [ + "provider_name", + "base_url", + "custom_base_url", + "icon", + "authorize_url", + "access_token_url", + "redirect_url", + "api_endpoint", + "api_endpoint_args", + "auth_url_data", +]; + +xhiveframework.ui.form.on("Social Login Key", { + refresh(frm) { + frm.trigger("setup_fields"); + }, + + custom_base_url(frm) { + frm.trigger("setup_fields"); + }, + + social_login_provider(frm) { + if (frm.doc.social_login_provider != "Custom") { + xhiveframework + .call({ + doc: frm.doc, + method: "get_social_login_provider", + args: { + provider: frm.doc.social_login_provider, + }, + }) + .done((r) => { + const provider = r.message; + for (var field of fields) { + frm.set_value(field, provider[field]); + frm.set_df_property(field, "read_only", 1); + if (frm.doc.custom_base_url) { + frm.toggle_enable("base_url", 1); + } + } + }); + } else { + frm.trigger("clear_fields"); + frm.trigger("setup_fields"); + } + }, + + setup_fields(frm) { + // set custom_base_url to read only for "Custom" provider + if (frm.doc.social_login_provider == "Custom") { + frm.set_value("custom_base_url", 1); + frm.set_df_property("custom_base_url", "read_only", 1); + } + + // set fields to read only for providers from template + for (var f of fields) { + if (frm.doc.social_login_provider != "Custom") { + frm.set_df_property(f, "read_only", 1); + } + } + + // enable base_url for providers with custom_base_url + if (frm.doc.custom_base_url) { + frm.set_df_property("base_url", "read_only", 0); + frm.fields_dict["sb_identity_details"].collapse(false); + } + + // hide social_login_provider and provider_name for non local + if (!frm.doc.__islocal && (frm.doc.social_login_provider || frm.doc.provider_name)) { + frm.set_df_property("social_login_provider", "hidden", 1); + frm.set_df_property("provider_name", "hidden", 1); + } + }, + + clear_fields(frm) { + for (var field of fields) { + frm.set_value(field, ""); + frm.set_df_property(field, "read_only", 0); + } + }, +}); diff --git a/xhiveframework/integrations/doctype/social_login_key/social_login_key.json b/xhiveframework/integrations/doctype/social_login_key/social_login_key.json new file mode 100644 index 0000000..5b0c501 --- /dev/null +++ b/xhiveframework/integrations/doctype/social_login_key/social_login_key.json @@ -0,0 +1,203 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2017-11-18 15:36:09.676722", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable_social_login", + "client_credentials", + "social_login_provider", + "client_id", + "column_break_0", + "provider_name", + "client_secret", + "sb_identity_details", + "icon", + "column_break_1", + "base_url", + "configuration_section", + "sign_ups", + "client_urls", + "authorize_url", + "access_token_url", + "column_break_3", + "redirect_url", + "api_endpoint", + "custom_base_url", + "client_information", + "api_endpoint_args", + "auth_url_data", + "user_id_property" + ], + "fields": [ + { + "default": "0", + "fieldname": "enable_social_login", + "fieldtype": "Check", + "label": "Enable Social Login" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.enable_social_login", + "fieldname": "client_credentials", + "fieldtype": "Section Break", + "label": "Client Credentials" + }, + { + "default": "Custom", + "depends_on": "eval:doc.custom!=1", + "fieldname": "social_login_provider", + "fieldtype": "Select", + "label": "Social Login Provider", + "options": "Custom\nFacebook\nXhiveFramework\nGitHub\nGoogle\nOffice 365\nSalesforce\nfairlogin", + "set_only_once": 1 + }, + { + "fieldname": "client_id", + "fieldtype": "Data", + "label": "Client ID" + }, + { + "fieldname": "column_break_0", + "fieldtype": "Column Break" + }, + { + "fieldname": "provider_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Provider Name", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "client_secret", + "fieldtype": "Password", + "label": "Client Secret" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.custom_base_url", + "fieldname": "sb_identity_details", + "fieldtype": "Section Break", + "label": "Identity Details" + }, + { + "depends_on": "eval:doc.social_login_provider==\"Custom\"", + "fieldname": "icon", + "fieldtype": "Data", + "label": "Icon" + }, + { + "fieldname": "column_break_1", + "fieldtype": "Column Break" + }, + { + "fieldname": "base_url", + "fieldtype": "Data", + "label": "Base URL" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"", + "fieldname": "client_urls", + "fieldtype": "Section Break", + "label": "Client URLs" + }, + { + "fieldname": "authorize_url", + "fieldtype": "Data", + "label": "Authorize URL" + }, + { + "fieldname": "access_token_url", + "fieldtype": "Data", + "label": "Access Token URL" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "redirect_url", + "fieldtype": "Data", + "label": "Redirect URL" + }, + { + "fieldname": "api_endpoint", + "fieldtype": "Data", + "label": "API Endpoint" + }, + { + "default": "0", + "fieldname": "custom_base_url", + "fieldtype": "Check", + "hidden": 1, + "label": "Custom Base URL" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.social_login_provider===\"Custom\"", + "fieldname": "client_information", + "fieldtype": "Section Break", + "label": "Client Information" + }, + { + "fieldname": "api_endpoint_args", + "fieldtype": "Code", + "label": "API Endpoint Args" + }, + { + "fieldname": "auth_url_data", + "fieldtype": "Code", + "label": "Auth URL Data" + }, + { + "depends_on": "eval:doc.social_login_provider===\"Custom\"", + "fieldname": "user_id_property", + "fieldtype": "Data", + "label": "User ID Property" + }, + { + "collapsible": 1, + "fieldname": "configuration_section", + "fieldtype": "Section Break", + "label": "Configuration" + }, + { + "description": "Controls whether new users can sign up using this Social Login Key. If unset, Website Settings is respected. ", + "fieldname": "sign_ups", + "fieldtype": "Select", + "label": "Sign ups", + "options": "\nAllow\nDeny" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-10-14 12:22:23.601130", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Social Login Key", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "provider_name", + "track_changes": 1 +} diff --git a/xhiveframework/integrations/doctype/social_login_key/social_login_key.py b/xhiveframework/integrations/doctype/social_login_key/social_login_key.py new file mode 100644 index 0000000..7a5e22a --- /dev/null +++ b/xhiveframework/integrations/doctype/social_login_key/social_login_key.py @@ -0,0 +1,226 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class BaseUrlNotSetError(xhiveframework.ValidationError): + pass + + +class AuthorizeUrlNotSetError(xhiveframework.ValidationError): + pass + + +class AccessTokenUrlNotSetError(xhiveframework.ValidationError): + pass + + +class RedirectUrlNotSetError(xhiveframework.ValidationError): + pass + + +class ClientIDNotSetError(xhiveframework.ValidationError): + pass + + +class ClientSecretNotSetError(xhiveframework.ValidationError): + pass + + +class SocialLoginKey(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + access_token_url: DF.Data | None + api_endpoint: DF.Data | None + api_endpoint_args: DF.Code | None + auth_url_data: DF.Code | None + authorize_url: DF.Data | None + base_url: DF.Data | None + client_id: DF.Data | None + client_secret: DF.Password | None + custom_base_url: DF.Check + enable_social_login: DF.Check + icon: DF.Data | None + provider_name: DF.Data + redirect_url: DF.Data | None + sign_ups: DF.Literal["", "Allow", "Deny"] + social_login_provider: DF.Literal[ + "Custom", "Facebook", "XhiveFramework", "GitHub", "Google", "Office 365", "Salesforce", "fairlogin" + ] + user_id_property: DF.Data | None + + # end: auto-generated types + def autoname(self): + self.name = xhiveframework.scrub(self.provider_name) + + def validate(self): + self.set_icon() + if self.custom_base_url and not self.base_url: + xhiveframework.throw(_("Please enter Base URL"), exc=BaseUrlNotSetError) + if not self.authorize_url: + xhiveframework.throw(_("Please enter Authorize URL"), exc=AuthorizeUrlNotSetError) + if not self.access_token_url: + xhiveframework.throw(_("Please enter Access Token URL"), exc=AccessTokenUrlNotSetError) + if not self.redirect_url: + xhiveframework.throw(_("Please enter Redirect URL"), exc=RedirectUrlNotSetError) + if self.enable_social_login and not self.client_id: + xhiveframework.throw(_("Please enter Client ID before social login is enabled"), exc=ClientIDNotSetError) + if self.enable_social_login and not self.client_secret: + xhiveframework.throw( + _("Please enter Client Secret before social login is enabled"), exc=ClientSecretNotSetError + ) + + def set_icon(self): + icon_map = { + "Google": "google.svg", + "XhiveFramework": "xhiveframework.svg", + "Facebook": "facebook.svg", + "Office 365": "office_365.svg", + "GitHub": "github.svg", + "Salesforce": "salesforce.svg", + "fairlogin": "fair.svg", + } + + if self.provider_name in icon_map: + icon_file = icon_map[self.provider_name] + self.icon = f"/assets/xhiveframework/icons/social/{icon_file}" + + @xhiveframework.whitelist() + def get_social_login_provider(self, provider, initialize=False): + providers = {} + + providers["Office 365"] = { + "provider_name": "Office 365", + "enable_social_login": 1, + "base_url": "https://login.microsoftonline.com", + "custom_base_url": 0, + "icon": "fa fa-windows", + "authorize_url": "https://login.microsoftonline.com/common/oauth2/authorize", + "access_token_url": "https://login.microsoftonline.com/common/oauth2/token", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_office365", + "api_endpoint": None, + "api_endpoint_args": None, + "auth_url_data": json.dumps({"response_type": "code", "scope": "openid"}), + } + + providers["GitHub"] = { + "provider_name": "GitHub", + "enable_social_login": 1, + "base_url": "https://api.github.com/", + "custom_base_url": 0, + "icon": "fa fa-github", + "authorize_url": "https://github.com/login/oauth/authorize", + "access_token_url": "https://github.com/login/oauth/access_token", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_github", + "api_endpoint": "user", + "api_endpoint_args": None, + "auth_url_data": json.dumps({"scope": "user:email"}), + } + + providers["Google"] = { + "provider_name": "Google", + "enable_social_login": 1, + "base_url": "https://www.googleapis.com", + "custom_base_url": 0, + "icon": "fa fa-google", + "authorize_url": "https://accounts.google.com/o/oauth2/auth", + "access_token_url": "https://accounts.google.com/o/oauth2/token", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_google", + "api_endpoint": "oauth2/v2/userinfo", + "api_endpoint_args": None, + "auth_url_data": json.dumps( + { + "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email", + "response_type": "code", + } + ), + } + + providers["Facebook"] = { + "provider_name": "Facebook", + "enable_social_login": 1, + "base_url": "https://graph.facebook.com", + "custom_base_url": 0, + "icon": "fa fa-facebook", + "authorize_url": "https://www.facebook.com/dialog/oauth", + "access_token_url": "https://graph.facebook.com/oauth/access_token", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_facebook", + "api_endpoint": "/v2.5/me", + "api_endpoint_args": json.dumps( + {"fields": "first_name,last_name,email,gender,location,verified,picture"} + ), + "auth_url_data": json.dumps( + {"display": "page", "response_type": "code", "scope": "email,public_profile"} + ), + } + + providers["XhiveFramework"] = { + "provider_name": "XhiveFramework", + "enable_social_login": 1, + "custom_base_url": 1, + "icon": "/assets/xhiveframework/images/xhiveframework-favicon.svg", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_xhiveframework", + "api_endpoint": "/api/method/xhiveframework.integrations.oauth2.openid_profile", + "api_endpoint_args": None, + "authorize_url": "/api/method/xhiveframework.integrations.oauth2.authorize", + "access_token_url": "/api/method/xhiveframework.integrations.oauth2.get_token", + "auth_url_data": json.dumps({"response_type": "code", "scope": "openid"}), + } + + providers["Salesforce"] = { + "provider_name": "Salesforce", + "enable_social_login": 1, + "base_url": "https://login.salesforce.com", + "custom_base_url": 0, + "icon": "fa fa-cloud", # https://github.com/FortAwesome/Font-Awesome/issues/1744 + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_salesforce", + "api_endpoint": "https://login.salesforce.com/services/oauth2/userinfo", + "api_endpoint_args": None, + "authorize_url": "https://login.salesforce.com/services/oauth2/authorize", + "access_token_url": "https://login.salesforce.com/services/oauth2/token", + "auth_url_data": json.dumps({"response_type": "code", "scope": "openid"}), + } + + providers["fairlogin"] = { + "provider_name": "fairlogin", + "enable_social_login": 1, + "base_url": "https://id.fairkom.net/auth/realms/fairlogin/", + "custom_base_url": 0, + "icon": "fa fa-key", + "redirect_url": "/api/method/xhiveframework.integrations.oauth2_logins.login_via_fairlogin", + "api_endpoint": "https://id.fairkom.net/auth/realms/fairlogin/protocol/openid-connect/userinfo", + "api_endpoint_args": None, + "authorize_url": "https://id.fairkom.net/auth/realms/fairlogin/protocol/openid-connect/auth", + "access_token_url": "https://id.fairkom.net/auth/realms/fairlogin/protocol/openid-connect/token", + "auth_url_data": json.dumps({"response_type": "code", "scope": "openid"}), + } + + # Initialize the doc and return, used in patch + # Or can be used for creating key from controller + if initialize and provider: + for k, v in providers[provider].items(): + setattr(self, k, v) + return + + return providers.get(provider) if provider else providers + + +def provider_allows_signup(provider: str) -> bool: + from xhiveframework.website.utils import is_signup_disabled + + sign_up_config = xhiveframework.db.get_value("Social Login Key", provider, "sign_ups") + + if not sign_up_config: # fallback to global settings + return not is_signup_disabled() + return sign_up_config == "Allow" diff --git a/xhiveframework/integrations/doctype/social_login_key/test_social_login_key.py b/xhiveframework/integrations/doctype/social_login_key/test_social_login_key.py new file mode 100644 index 0000000..4648780 --- /dev/null +++ b/xhiveframework/integrations/doctype/social_login_key/test_social_login_key.py @@ -0,0 +1,172 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from unittest.mock import MagicMock, patch + +from rauth import OAuth2Service + +import xhiveframework +from xhiveframework.auth import CookieManager, LoginManager +from xhiveframework.integrations.doctype.social_login_key.social_login_key import BaseUrlNotSetError +from xhiveframework.tests.utils import XhiveFrameworkTestCase, change_settings +from xhiveframework.utils import set_request +from xhiveframework.utils.oauth import login_via_oauth2 + +TEST_GITHUB_USER = "githublogin@example.com" + + +class TestSocialLoginKey(XhiveFrameworkTestCase): + def setUp(self) -> None: + xhiveframework.set_user("Administrator") + xhiveframework.delete_doc("User", TEST_GITHUB_USER, force=True) + super().setUp() + xhiveframework.set_user("Guest") + + def test_adding_xhiveframework_social_login_provider(self): + xhiveframework.set_user("Administrator") + provider_name = "XhiveFramework" + social_login_key = make_social_login_key(social_login_provider=provider_name) + social_login_key.get_social_login_provider(provider_name, initialize=True) + self.assertRaises(BaseUrlNotSetError, social_login_key.insert) + + def test_github_login_with_private_email(self): + github_social_login_setup() + + mock_session = MagicMock() + mock_session.get.side_effect = github_response_for_private_email + + with patch.object(OAuth2Service, "get_auth_session", return_value=mock_session): + login_via_oauth2("github", "iwriu", {"token": "ewrwerwer"}) # Dummy code and state token + + def test_github_login_with_public_email(self): + github_social_login_setup() + + mock_session = MagicMock() + mock_session.get.side_effect = github_response_for_public_email + + with patch.object(OAuth2Service, "get_auth_session", return_value=mock_session): + login_via_oauth2("github", "iwriu", {"token": "ewrwerwer"}) # Dummy code and state token + + def test_normal_signup_and_github_login(self): + github_social_login_setup() + + if not xhiveframework.db.exists("User", TEST_GITHUB_USER): + user = xhiveframework.new_doc("User", email=TEST_GITHUB_USER, first_name="GitHub Login") + user.insert(ignore_permissions=True) + + mock_session = MagicMock() + mock_session.get.side_effect = github_response_for_login + + with patch.object(OAuth2Service, "get_auth_session", return_value=mock_session): + login_via_oauth2("github", "iwriu", {"token": "ewrwerwer"}) + self.assertEqual(xhiveframework.session.user, TEST_GITHUB_USER) + + def test_force_disabled_signups(self): + key = github_social_login_setup() + key.sign_ups = "Deny" + key.save(ignore_permissions=True) + + mock_session = MagicMock() + mock_session.get.side_effect = github_response_for_login + + with patch.object(OAuth2Service, "get_auth_session", return_value=mock_session): + login_via_oauth2("github", "iwriu", {"token": "ewrwerwer"}) + self.assertEqual(xhiveframework.session.user, "Guest") + + @change_settings("Website Settings", disable_signup=1) + def test_force_enabled_signups(self): + """Social login key can override website settings for disabled signups.""" + key = github_social_login_setup() + key.sign_ups = "Allow" + key.save(ignore_permissions=True) + + mock_session = MagicMock() + mock_session.get.side_effect = github_response_for_login + + with patch.object(OAuth2Service, "get_auth_session", return_value=mock_session): + login_via_oauth2("github", "iwriu", {"token": "ewrwerwer"}) + + self.assertEqual(xhiveframework.session.user, TEST_GITHUB_USER) + + +def make_social_login_key(**kwargs): + kwargs["doctype"] = "Social Login Key" + if "provider_name" not in kwargs: + kwargs["provider_name"] = "Test OAuth2 Provider" + return xhiveframework.get_doc(kwargs) + + +def create_or_update_social_login_key(): + # used in other tests (connected app, oauth20) + try: + social_login_key = xhiveframework.get_doc("Social Login Key", "xhiveframework") + except xhiveframework.DoesNotExistError: + social_login_key = xhiveframework.new_doc("Social Login Key") + social_login_key.get_social_login_provider("XhiveFramework", initialize=True) + social_login_key.base_url = xhiveframework.utils.get_url() + social_login_key.enable_social_login = 0 + social_login_key.save() + xhiveframework.db.commit() + + return social_login_key + + +def create_github_social_login_key(): + if xhiveframework.db.exists("Social Login Key", "github"): + return xhiveframework.get_doc("Social Login Key", "github") + else: + provider_name = "GitHub" + social_login_key = make_social_login_key(social_login_provider=provider_name) + social_login_key.get_social_login_provider(provider_name, initialize=True) + + social_login_key.client_id = "h6htd6q" + social_login_key.client_secret = "keoererk988ekkhf8w9e8ewrjhhkjer9889" + social_login_key.insert(ignore_permissions=True) + return social_login_key + + +def github_response_for_private_email(url, *args, **kwargs): + if url == "user": + return_value = { + "login": "dummy_username", + "id": "223342", + "email": None, + "first_name": "Github Private", + } + else: + return_value = [{"email": "github@example.com", "primary": True, "verified": True}] + + return MagicMock(status_code=200, json=MagicMock(return_value=return_value)) + + +def github_response_for_public_email(url, *args, **kwargs): + if url == "user": + return_value = { + "login": "dummy_username", + "id": "223343", + "email": "github_public@example.com", + "first_name": "Github Public", + } + + return MagicMock(status_code=200, json=MagicMock(return_value=return_value)) + + +def github_response_for_login(url, *args, **kwargs): + if url == "user": + return_value = { + "login": "dummy_username", + "id": "223346", + "email": None, + "first_name": "Github Login", + } + else: + return_value = [{"email": TEST_GITHUB_USER, "primary": True, "verified": True}] + + return MagicMock(status_code=200, json=MagicMock(return_value=return_value)) + + +def github_social_login_setup(): + set_request(path="/random") + xhiveframework.local.cookie_manager = CookieManager() + xhiveframework.local.login_manager = LoginManager() + + return create_github_social_login_key() diff --git a/xhiveframework/integrations/doctype/social_login_keys/__init__.py b/xhiveframework/integrations/doctype/social_login_keys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/social_login_keys/social_login_keys.py b/xhiveframework/integrations/doctype/social_login_keys/social_login_keys.py new file mode 100644 index 0000000..14ff019 --- /dev/null +++ b/xhiveframework/integrations/doctype/social_login_keys/social_login_keys.py @@ -0,0 +1,6 @@ +# see license +from xhiveframework.model.document import Document + + +class SocialLoginKeys(Document): + pass diff --git a/xhiveframework/integrations/doctype/token_cache/__init__.py b/xhiveframework/integrations/doctype/token_cache/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/token_cache/test_records.json b/xhiveframework/integrations/doctype/token_cache/test_records.json new file mode 100644 index 0000000..0584022 --- /dev/null +++ b/xhiveframework/integrations/doctype/token_cache/test_records.json @@ -0,0 +1,18 @@ +[ + { + "doctype": "Token Cache", + "user": "test@example.com", + "access_token": "test-access-token", + "refresh_token": "test-refresh-token", + "token_type": "Bearer", + "expires_in": 1000, + "scopes": [ + { + "scope": "all" + }, + { + "scope": "openid" + } + ] + } +] \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/token_cache/test_token_cache.py b/xhiveframework/integrations/doctype/token_cache/test_token_cache.py new file mode 100644 index 0000000..7c5313e --- /dev/null +++ b/xhiveframework/integrations/doctype/token_cache/test_token_cache.py @@ -0,0 +1,36 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +test_dependencies = ["User", "Connected App", "Token Cache"] + + +class TestTokenCache(XhiveFrameworkTestCase): + def setUp(self): + self.token_cache = xhiveframework.get_last_doc("Token Cache") + self.token_cache.update({"connected_app": xhiveframework.get_last_doc("Connected App").name}) + self.token_cache.save(ignore_permissions=True) + + def test_get_auth_header(self): + self.token_cache.get_auth_header() + + def test_update_data(self): + self.token_cache.update_data( + { + "access_token": "new-access-token", + "refresh_token": "new-refresh-token", + "token_type": "bearer", + "expires_in": 2000, + "scope": "new scope", + } + ) + + def test_get_expires_in(self): + self.token_cache.get_expires_in() + + def test_is_expired(self): + self.token_cache.is_expired() + + def get_json(self): + self.token_cache.get_json() diff --git a/xhiveframework/integrations/doctype/token_cache/token_cache.js b/xhiveframework/integrations/doctype/token_cache/token_cache.js new file mode 100644 index 0000000..d3c198b --- /dev/null +++ b/xhiveframework/integrations/doctype/token_cache/token_cache.js @@ -0,0 +1,7 @@ +// Copyright (c) 2019, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Token Cache", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/integrations/doctype/token_cache/token_cache.json b/xhiveframework/integrations/doctype/token_cache/token_cache.json new file mode 100644 index 0000000..0e6601f --- /dev/null +++ b/xhiveframework/integrations/doctype/token_cache/token_cache.json @@ -0,0 +1,111 @@ +{ + "actions": [], + "autoname": "format:{connected_app}-{user}", + "beta": 1, + "creation": "2019-01-24 16:56:55.631096", + "doctype": "DocType", + "document_type": "System", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "user", + "connected_app", + "provider_name", + "access_token", + "refresh_token", + "expires_in", + "state", + "scopes", + "success_uri", + "token_type" + ], + "fields": [ + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "connected_app", + "fieldtype": "Link", + "label": "Connected App", + "options": "Connected App", + "read_only": 1 + }, + { + "fieldname": "access_token", + "fieldtype": "Password", + "label": "Access Token", + "read_only": 1 + }, + { + "fieldname": "refresh_token", + "fieldtype": "Password", + "label": "Refresh Token", + "read_only": 1 + }, + { + "fieldname": "expires_in", + "fieldtype": "Int", + "label": "Expires In", + "read_only": 1 + }, + { + "fieldname": "state", + "fieldtype": "Data", + "label": "State", + "read_only": 1 + }, + { + "fieldname": "scopes", + "fieldtype": "Table", + "label": "Scopes", + "options": "OAuth Scope", + "read_only": 1 + }, + { + "fieldname": "success_uri", + "fieldtype": "Data", + "label": "Success URI", + "read_only": 1 + }, + { + "fieldname": "token_type", + "fieldtype": "Data", + "label": "Token Type", + "read_only": 1 + }, + { + "fetch_from": "connected_app.provider_name", + "fieldname": "provider_name", + "fieldtype": "Data", + "label": "Provider Name", + "read_only": 1 + } + ], + "links": [], + "modified": "2023-01-01 21:01:24.405729", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Token Cache", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "delete": 1, + "read": 1, + "role": "System Manager" + }, + { + "delete": 1, + "if_owner": 1, + "read": 1, + "role": "All" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/token_cache/token_cache.py b/xhiveframework/integrations/doctype/token_cache/token_cache.py new file mode 100644 index 0000000..e005831 --- /dev/null +++ b/xhiveframework/integrations/doctype/token_cache/token_cache.py @@ -0,0 +1,90 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +from datetime import datetime, timedelta + +import pytz + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import cint, cstr, get_system_timezone + + +class TokenCache(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.integrations.doctype.oauth_scope.oauth_scope import OAuthScope + from xhiveframework.types import DF + + access_token: DF.Password | None + connected_app: DF.Link | None + expires_in: DF.Int + provider_name: DF.Data | None + refresh_token: DF.Password | None + scopes: DF.Table[OAuthScope] + state: DF.Data | None + success_uri: DF.Data | None + token_type: DF.Data | None + user: DF.Link | None + + # end: auto-generated types + def get_auth_header(self): + if self.access_token: + return {"Authorization": "Bearer " + self.get_password("access_token")} + raise xhiveframework.exceptions.DoesNotExistError + + def update_data(self, data): + """ + Store data returned by authorization flow. + + Params: + data - Dict with access_token, refresh_token, expires_in and scope. + """ + token_type = cstr(data.get("token_type", "")).lower() + if token_type not in ["bearer", "mac"]: + xhiveframework.throw(_("Received an invalid token type.")) + # 'Bearer' or 'MAC' + token_type = token_type.title() if token_type == "bearer" else token_type.upper() + + self.token_type = token_type + self.access_token = cstr(data.get("access_token", "")) + self.refresh_token = cstr(data.get("refresh_token", "")) + self.expires_in = cint(data.get("expires_in", 0)) + + new_scopes = data.get("scope") + if new_scopes: + if isinstance(new_scopes, str): + new_scopes = new_scopes.split(" ") + if isinstance(new_scopes, list): + self.scopes = None + for scope in new_scopes: + self.append("scopes", {"scope": scope}) + + self.state = None + self.save(ignore_permissions=True) + xhiveframework.db.commit() + return self + + def get_expires_in(self): + system_timezone = pytz.timezone(get_system_timezone()) + modified = xhiveframework.utils.get_datetime(self.modified) + modified = system_timezone.localize(modified) + expiry_utc = modified.astimezone(pytz.utc) + timedelta(seconds=self.expires_in) + now_utc = datetime.utcnow().replace(tzinfo=pytz.utc) + return cint((expiry_utc - now_utc).total_seconds()) + + def is_expired(self): + return self.get_expires_in() < 0 + + def get_json(self): + return { + "access_token": self.get_password("access_token", False), + "refresh_token": self.get_password("refresh_token", False), + "expires_in": self.get_expires_in(), + "token_type": self.token_type, + } diff --git a/xhiveframework/integrations/doctype/webhook/__init__.py b/xhiveframework/integrations/doctype/webhook/__init__.py new file mode 100644 index 0000000..f586b76 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook/__init__.py @@ -0,0 +1,108 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def get_all_webhooks(): + # query webhooks + webhooks_list = xhiveframework.get_all( + "Webhook", + fields=["name", "condition", "webhook_docevent", "webhook_doctype", "background_jobs_queue"], + filters={"enabled": True}, + ) + + # make webhooks map + webhooks = {} + for w in webhooks_list: + webhooks.setdefault(w.webhook_doctype, []).append(w) + + return webhooks + + +def run_webhooks(doc, method): + """Run webhooks for this method""" + + xhiveframework_flags = xhiveframework.local.flags + + if xhiveframework_flags.in_import or xhiveframework_flags.in_patch or xhiveframework_flags.in_install or xhiveframework_flags.in_migrate: + return + + # load all webhooks from cache / DB + webhooks = xhiveframework.cache.get_value("webhooks", get_all_webhooks) + + # get webhooks for this doctype + webhooks_for_doc = webhooks.get(doc.doctype, None) + + if not webhooks_for_doc: + # no webhooks, quit + return + + event_list = ["on_update", "after_insert", "on_submit", "on_cancel", "on_trash"] + + if not doc.flags.in_insert: + # value change is not applicable in insert + event_list.append("on_change") + event_list.append("before_update_after_submit") + + from xhiveframework.integrations.doctype.webhook.webhook import get_context + + for webhook in webhooks_for_doc: + trigger_webhook = False + event = method if method in event_list else None + if not webhook.condition: + trigger_webhook = True + elif xhiveframework.safe_eval(webhook.condition, eval_locals=get_context(doc)): + trigger_webhook = True + + if trigger_webhook and event and webhook.webhook_docevent == event: + _add_webhook_to_queue(webhook, doc) + + +def _add_webhook_to_queue(webhook, doc): + # Maintain a queue and flush on commit + if not getattr(xhiveframework.local, "_webhook_queue", None): + xhiveframework.local._webhook_queue = [] + xhiveframework.db.after_commit.add(flush_webhook_execution_queue) + + xhiveframework.local._webhook_queue.append(xhiveframework._dict(doc=doc, webhook=webhook)) + + +def flush_webhook_execution_queue(): + """Enqueue all pending webhook executions. + + Each webhook can trigger multiple times on same document or even different instance of same + document. We assume that last enqueued version of document is the final document for this DB + transaction. + """ + if not getattr(xhiveframework.local, "_webhook_queue", None): + return + + uniq_hooks = set() + unique_last_instances = [] + + # reverse + xhiveframework.local._webhook_queue.reverse() + + # deduplicate on (doc.name, webhook.name) + # 'doc' holds the last instance values + for execution in xhiveframework.local._webhook_queue: + key = (execution.webhook.get("name"), execution.doc.get("name")) + if key not in uniq_hooks: + uniq_hooks.add(key) + unique_last_instances.append(execution) + + # Clear original queue so next enqueue computation happens correctly. + del xhiveframework.local._webhook_queue + + # reverse again, to get back the original order on which to execute webhooks + unique_last_instances.reverse() + + for instance in unique_last_instances: + xhiveframework.enqueue( + "xhiveframework.integrations.doctype.webhook.webhook.enqueue_webhook", + doc=instance.doc, + webhook=instance.webhook, + now=xhiveframework.flags.in_test, + queue=instance.webhook.background_jobs_queue or "default", + ) diff --git a/xhiveframework/integrations/doctype/webhook/test_webhook.py b/xhiveframework/integrations/doctype/webhook/test_webhook.py new file mode 100644 index 0000000..a7f23b7 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook/test_webhook.py @@ -0,0 +1,311 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import json +from contextlib import contextmanager + +import responses +from responses.matchers import json_params_matcher + +import xhiveframework +from xhiveframework.integrations.doctype.webhook import flush_webhook_execution_queue +from xhiveframework.integrations.doctype.webhook.webhook import ( + enqueue_webhook, + get_webhook_data, + get_webhook_headers, +) +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +@contextmanager +def get_test_webhook(config): + wh = xhiveframework.get_doc(config) + if not wh.name: + wh.name = xhiveframework.generate_hash() + wh.insert() + wh.reload() + try: + yield wh + finally: + wh.delete() + + +class TestWebhook(XhiveFrameworkTestCase): + @classmethod + def setUpClass(cls): + # delete any existing webhooks + xhiveframework.db.delete("Webhook") + # Delete existing logs if any + xhiveframework.db.delete("Webhook Request Log") + super().setUpClass() + # create test webhooks + cls.create_sample_webhooks() + + @classmethod + def create_sample_webhooks(cls): + samples_webhooks_data = [ + { + "name": xhiveframework.generate_hash(), + "webhook_doctype": "User", + "webhook_docevent": "after_insert", + "request_url": "https://httpbin.org/post", + "condition": "doc.email", + "enabled": True, + }, + { + "name": xhiveframework.generate_hash(), + "webhook_doctype": "User", + "webhook_docevent": "after_insert", + "request_url": "https://httpbin.org/post", + "condition": "doc.first_name", + "enabled": False, + }, + ] + + cls.sample_webhooks = [] + for wh_fields in samples_webhooks_data: + wh = xhiveframework.new_doc("Webhook") + wh.update(wh_fields) + wh.insert() + cls.sample_webhooks.append(wh) + + @classmethod + def tearDownClass(cls): + # delete any existing webhooks + xhiveframework.db.delete("Webhook") + + def setUp(self): + # retrieve or create a User webhook for `after_insert` + webhook_fields = { + "webhook_doctype": "User", + "webhook_docevent": "after_insert", + "request_url": "https://httpbin.org/post", + } + + if xhiveframework.db.exists("Webhook", webhook_fields): + self.webhook = xhiveframework.get_doc("Webhook", webhook_fields) + else: + self.webhook = xhiveframework.new_doc("Webhook") + self.webhook.update(webhook_fields) + + # create a User document + self.user = xhiveframework.new_doc("User") + self.user.first_name = xhiveframework.mock("name") + self.user.email = xhiveframework.mock("email") + self.user.save() + + # Create another test user specific to this test + self.test_user = xhiveframework.new_doc("User") + self.test_user.email = "user1@integration.webhooks.test.com" + self.test_user.first_name = "user1" + self.test_user.send_welcome_email = False + + self.responses = responses.RequestsMock() + self.responses.start() + + def tearDown(self) -> None: + self.user.delete() + self.test_user.delete() + + self.responses.stop() + self.responses.reset() + super().tearDown() + + def test_webhook_trigger_with_enabled_webhooks(self): + """Test webhook trigger for enabled webhooks""" + + xhiveframework.cache.delete_value("webhooks") + + # Insert the user to db + self.test_user.insert() + + webhooks = xhiveframework.cache.get_value("webhooks") + self.assertTrue("User" in webhooks) + self.assertEqual(len(webhooks.get("User")), 1) + + # only 1 hook (enabled) must be queued + self.assertEqual(len(xhiveframework.local._webhook_queue), 1) + execution = xhiveframework.local._webhook_queue[0] + self.assertEqual(execution.webhook.name, self.sample_webhooks[0].name) + self.assertEqual(execution.doc.name, self.test_user.name) + + def test_validate_doc_events(self): + "Test creating a submit-related webhook for a non-submittable DocType" + + self.webhook.webhook_docevent = "on_submit" + self.assertRaises(xhiveframework.ValidationError, self.webhook.save) + + def test_validate_request_url(self): + "Test validation for the webhook request URL" + + self.webhook.request_url = "httpbin.org?post" + self.assertRaises(xhiveframework.ValidationError, self.webhook.save) + + def test_validate_headers(self): + "Test validation for request headers" + + # test incomplete headers + self.webhook.set("webhook_headers", [{"key": "Content-Type"}]) + self.webhook.save() + headers = get_webhook_headers(doc=None, webhook=self.webhook) + self.assertEqual(headers, {}) + + # test complete headers + self.webhook.set("webhook_headers", [{"key": "Content-Type", "value": "application/json"}]) + self.webhook.save() + headers = get_webhook_headers(doc=None, webhook=self.webhook) + self.assertEqual(headers, {"Content-Type": "application/json"}) + + def test_validate_request_body_form(self): + "Test validation of Form URL-Encoded request body" + + self.webhook.request_structure = "Form URL-Encoded" + self.webhook.set("webhook_data", [{"fieldname": "name", "key": "name"}]) + self.webhook.webhook_json = """{ + "name": "{{ doc.name }}" + }""" + self.webhook.save() + self.assertEqual(self.webhook.webhook_json, None) + + data = get_webhook_data(doc=self.user, webhook=self.webhook) + self.assertEqual(data, {"name": self.user.name}) + + def test_validate_request_body_json(self): + "Test validation of JSON request body" + + self.webhook.request_structure = "JSON" + self.webhook.set("webhook_data", [{"fieldname": "name", "key": "name"}]) + self.webhook.webhook_json = """{ + "name": "{{ doc.name }}" + }""" + self.webhook.save() + self.assertEqual(self.webhook.webhook_data, []) + + data = get_webhook_data(doc=self.user, webhook=self.webhook) + self.assertEqual(data, {"name": self.user.name}) + + def test_webhook_req_log_creation(self): + self.responses.add( + responses.POST, + "https://httpbin.org/post", + status=200, + json={}, + ) + + if not xhiveframework.db.get_value("User", "user2@integration.webhooks.test.com"): + user = xhiveframework.get_doc( + {"doctype": "User", "email": "user2@integration.webhooks.test.com", "first_name": "user2"} + ).insert() + else: + user = xhiveframework.get_doc("User", "user2@integration.webhooks.test.com") + + webhook = xhiveframework.get_doc("Webhook", {"webhook_doctype": "User"}) + enqueue_webhook(user, webhook) + + self.assertTrue(xhiveframework.get_all("Webhook Request Log", pluck="name")) + + def test_webhook_with_array_body(self): + """Check if array request body are supported.""" + wh_config = { + "doctype": "Webhook", + "webhook_doctype": "Note", + "webhook_docevent": "on_change", + "enabled": 1, + "request_url": "https://httpbin.org/post", + "request_method": "POST", + "request_structure": "JSON", + "webhook_json": '[\r\n{% for n in range(3) %}\r\n {\r\n "title": "{{ doc.title }}" }\r\n {%- if not loop.last -%}\r\n , \r\n {%endif%}\r\n{%endfor%}\r\n]', + "meets_condition": "Yes", + "webhook_headers": [ + { + "key": "Content-Type", + "value": "application/json", + } + ], + } + + doc = xhiveframework.new_doc("Note") + doc.title = "Test Webhook Note" + final_title = xhiveframework.generate_hash() + + expected_req = [{"title": final_title} for _ in range(3)] + self.responses.add( + responses.POST, + "https://httpbin.org/post", + status=200, + json=expected_req, + match=[json_params_matcher(expected_req)], + ) + + with get_test_webhook(wh_config): + # It should only execute once in a transaction + doc.insert() + doc.reload() + doc.save() + doc = xhiveframework.get_doc(doc.doctype, doc.name) + doc.title = final_title + doc.save() + flush_webhook_execution_queue() + log = xhiveframework.get_last_doc("Webhook Request Log") + self.assertEqual(len(json.loads(log.response)), 3) + + def test_webhook_with_dynamic_url_enabled(self): + wh_config = { + "doctype": "Webhook", + "webhook_doctype": "Note", + "webhook_docevent": "after_insert", + "enabled": 1, + "request_url": "https://httpbin.org/anything/{{ doc.doctype }}", + "is_dynamic_url": 1, + "request_method": "POST", + "request_structure": "JSON", + "webhook_json": "{}", + "meets_condition": "Yes", + "webhook_headers": [ + { + "key": "Content-Type", + "value": "application/json", + } + ], + } + + self.responses.add( + responses.POST, + "https://httpbin.org/anything/Note", + status=200, + ) + + with get_test_webhook(wh_config) as wh: + doc = xhiveframework.new_doc("Note") + doc.title = "Test Webhook Note" + enqueue_webhook(doc, wh) + + def test_webhook_with_dynamic_url_disabled(self): + wh_config = { + "doctype": "Webhook", + "webhook_doctype": "Note", + "webhook_docevent": "after_insert", + "enabled": 1, + "request_url": "https://httpbin.org/anything/{{doc.doctype}}", + "is_dynamic_url": 0, + "request_method": "POST", + "request_structure": "JSON", + "webhook_json": "{}", + "meets_condition": "Yes", + "webhook_headers": [ + { + "key": "Content-Type", + "value": "application/json", + } + ], + } + + self.responses.add( + responses.POST, + "https://httpbin.org/anything/{{doc.doctype}}", + status=200, + ) + + with get_test_webhook(wh_config) as wh: + doc = xhiveframework.new_doc("Note") + doc.title = "Test Webhook Note" + enqueue_webhook(doc, wh) diff --git a/xhiveframework/integrations/doctype/webhook/webhook.js b/xhiveframework/integrations/doctype/webhook/webhook.js new file mode 100644 index 0000000..365c2fb --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook/webhook.js @@ -0,0 +1,129 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.webhook = { + set_fieldname_select: (frm) => { + if (frm.doc.webhook_doctype) { + xhiveframework.model.with_doctype(frm.doc.webhook_doctype, () => { + // get doctype fields + let fields = $.map( + xhiveframework.get_doc("DocType", frm.doc.webhook_doctype).fields, + (d) => { + if ( + xhiveframework.model.no_value_type.includes(d.fieldtype) && + !xhiveframework.model.table_fields.includes(d.fieldtype) + ) { + return null; + } else { + return { + label: `${__(d.label, null, d.parent)} (${__(d.fieldtype)})`, + value: d.fieldname, + }; + } + } + ); + + // add meta fields + for (let field of xhiveframework.model.std_fields) { + if (field.fieldname == "name") { + fields.unshift({ label: __("Name (Doc Name)"), value: "name" }); + } else { + fields.push({ + label: `${__(field.label, null, field.parent)} (${__( + field.fieldtype + )})`, + value: field.fieldname, + }); + } + } + + frm.fields_dict.webhook_data.grid.update_docfield_property( + "fieldname", + "options", + [""].concat(fields) + ); + }); + } + }, + + set_request_headers: (frm) => { + if (frm.doc.request_structure) { + let header_value; + if (frm.doc.request_structure == "Form URL-Encoded") { + header_value = "application/x-www-form-urlencoded"; + } else if (frm.doc.request_structure == "JSON") { + header_value = "application/json"; + } + + if (header_value) { + let header_row = (frm.doc.webhook_headers || []).find( + (row) => row.key === "Content-Type" + ); + if (header_row) { + xhiveframework.model.set_value( + header_row.doctype, + header_row.name, + "value", + header_value + ); + } else { + frm.add_child("webhook_headers", { + key: "Content-Type", + value: header_value, + }); + } + frm.refresh(); + } + } + }, +}; + +xhiveframework.ui.form.on("Webhook", { + refresh: (frm) => { + xhiveframework.webhook.set_fieldname_select(frm); + frm.set_query( + "background_jobs_queue", + "xhiveframework.integrations.doctype.webhook.webhook.get_all_queues" + ); + }, + + request_structure: (frm) => { + xhiveframework.webhook.set_request_headers(frm); + }, + + webhook_doctype: (frm) => { + xhiveframework.webhook.set_fieldname_select(frm); + }, + + enable_security: (frm) => { + frm.toggle_reqd("webhook_secret", frm.doc.enable_security); + }, + + preview_document: (frm) => { + xhiveframework.call({ + method: "generate_preview", + doc: frm.doc, + callback: (r) => { + frm.refresh_field("meets_condition"); + frm.refresh_field("preview_request_body"); + }, + }); + }, +}); + +xhiveframework.ui.form.on("Webhook Data", { + fieldname: (frm, cdt, cdn) => { + let row = locals[cdt][cdn]; + let df = xhiveframework + .get_meta(frm.doc.webhook_doctype) + .fields.filter((field) => field.fieldname == row.fieldname); + + if (!df.length) { + // check if field is a meta field + df = xhiveframework.model.std_fields.filter((field) => field.fieldname == row.fieldname); + } + + row.key = df.length ? df[0].fieldname : "name"; + frm.refresh_field("webhook_data"); + }, +}); diff --git a/xhiveframework/integrations/doctype/webhook/webhook.json b/xhiveframework/integrations/doctype/webhook/webhook.json new file mode 100644 index 0000000..a4d924f --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook/webhook.json @@ -0,0 +1,253 @@ +{ + "actions": [], + "autoname": "prompt", + "creation": "2017-09-08 16:16:13.060641", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "sb_doc_events", + "webhook_doctype", + "cb_doc_events", + "webhook_docevent", + "enabled", + "sb_condition", + "condition", + "cb_condition", + "html_condition", + "sb_webhook", + "request_url", + "is_dynamic_url", + "timeout", + "background_jobs_queue", + "cb_webhook", + "request_method", + "request_structure", + "sb_security", + "enable_security", + "webhook_secret", + "sb_webhook_headers", + "webhook_headers", + "sb_webhook_data", + "webhook_data", + "webhook_json", + "preview_tab", + "preview_document", + "column_break_26", + "meets_condition", + "section_break_28", + "preview_request_body" + ], + "fields": [ + { + "fieldname": "sb_doc_events", + "fieldtype": "Section Break", + "label": "Doc Events" + }, + { + "fieldname": "webhook_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "cb_doc_events", + "fieldtype": "Column Break" + }, + { + "fieldname": "webhook_docevent", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Doc Event", + "options": "after_insert\non_update\non_submit\non_cancel\non_trash\non_update_after_submit\non_change", + "set_only_once": 1 + }, + { + "fieldname": "sb_condition", + "fieldtype": "Section Break", + "label": "Webhook Trigger" + }, + { + "description": "The webhook will be triggered if this expression is true", + "fieldname": "condition", + "fieldtype": "Small Text", + "label": "Condition" + }, + { + "fieldname": "cb_condition", + "fieldtype": "Column Break" + }, + { + "fieldname": "html_condition", + "fieldtype": "HTML", + "options": "

      Condition Examples:

      \n
      doc.status==\"Open\"
      doc.due_date==nowdate()
      doc.total > 40000\n
      " + }, + { + "fieldname": "sb_webhook", + "fieldtype": "Section Break", + "label": "Webhook Request" + }, + { + "fieldname": "request_url", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Request URL", + "reqd": 1 + }, + { + "fieldname": "sb_webhook_headers", + "fieldtype": "Section Break", + "label": "Webhook Headers" + }, + { + "fieldname": "webhook_headers", + "fieldtype": "Table", + "label": "Headers", + "options": "Webhook Header" + }, + { + "fieldname": "sb_webhook_data", + "fieldtype": "Section Break", + "label": "Webhook Data" + }, + { + "depends_on": "eval: !doc.request_structure || doc.request_structure == \"Form URL-Encoded\"", + "fieldname": "webhook_data", + "fieldtype": "Table", + "label": "Data", + "options": "Webhook Data" + }, + { + "fieldname": "cb_webhook", + "fieldtype": "Column Break" + }, + { + "fieldname": "request_structure", + "fieldtype": "Select", + "label": "Request Structure", + "options": "\nForm URL-Encoded\nJSON" + }, + { + "depends_on": "eval: doc.request_structure == \"JSON\"", + "description": "To add dynamic values from the document, use jinja tags like\n\n
      \n
      { \"id\": \"{{ doc.name }}\" }\n
      \n
      ", + "fieldname": "webhook_json", + "fieldtype": "Code", + "label": "JSON Request Body", + "options": "JSON" + }, + { + "fieldname": "sb_security", + "fieldtype": "Section Break", + "label": "Webhook Security" + }, + { + "default": "0", + "fieldname": "enable_security", + "fieldtype": "Check", + "label": "Enable Security" + }, + { + "depends_on": "eval:doc.enable_security == 1", + "fieldname": "webhook_secret", + "fieldtype": "Password", + "label": "Webhook Secret" + }, + { + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "label": "Enabled" + }, + { + "default": "POST", + "fieldname": "request_method", + "fieldtype": "Select", + "label": "Request Method", + "options": "POST\nPUT\nDELETE", + "reqd": 1 + }, + { + "fieldname": "preview_tab", + "fieldtype": "Tab Break", + "label": "Preview" + }, + { + "fieldname": "preview_document", + "fieldtype": "Dynamic Link", + "label": "Select Document", + "options": "webhook_doctype" + }, + { + "fieldname": "preview_request_body", + "fieldtype": "Code", + "is_virtual": 1, + "label": "Request Body" + }, + { + "fieldname": "meets_condition", + "fieldtype": "Data", + "is_virtual": 1, + "label": "Meets Condition?" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_28", + "fieldtype": "Section Break" + }, + { + "default": "0", + "description": "On checking this option, URL will be treated like a jinja template string", + "fieldname": "is_dynamic_url", + "fieldtype": "Check", + "label": "Is Dynamic URL?" + }, + { + "default": "5", + "description": "The number of seconds until the request expires", + "fieldname": "timeout", + "fieldtype": "Int", + "label": "Request Timeout" + }, + { + "fieldname": "background_jobs_queue", + "fieldtype": "Autocomplete", + "label": "Background Jobs Queue" + } + ], + "links": [ + { + "link_doctype": "Webhook Request Log", + "link_fieldname": "webhook" + } + ], + "modified": "2024-02-19 11:40:58.387233", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Webhook", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/webhook/webhook.py b/xhiveframework/integrations/doctype/webhook/webhook.py new file mode 100644 index 0000000..eea219f --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook/webhook.py @@ -0,0 +1,264 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import base64 +import hashlib +import hmac +import json +from time import sleep +from urllib.parse import urlparse + +import requests + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils.background_jobs import get_queues_timeout +from xhiveframework.utils.jinja import validate_template +from xhiveframework.utils.safe_exec import get_safe_globals + +WEBHOOK_SECRET_HEADER = "X-XhiveFramework-Webhook-Signature" + + +class Webhook(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.integrations.doctype.webhook_data.webhook_data import WebhookData + from xhiveframework.integrations.doctype.webhook_header.webhook_header import WebhookHeader + from xhiveframework.types import DF + + background_jobs_queue: DF.Autocomplete | None + condition: DF.SmallText | None + enable_security: DF.Check + enabled: DF.Check + is_dynamic_url: DF.Check + meets_condition: DF.Data | None + preview_document: DF.DynamicLink | None + preview_request_body: DF.Code | None + request_method: DF.Literal["POST", "PUT", "DELETE"] + request_structure: DF.Literal["", "Form URL-Encoded", "JSON"] + request_url: DF.SmallText + timeout: DF.Int + webhook_data: DF.Table[WebhookData] + webhook_docevent: DF.Literal[ + "after_insert", + "on_update", + "on_submit", + "on_cancel", + "on_trash", + "on_update_after_submit", + "on_change", + ] + webhook_doctype: DF.Link + webhook_headers: DF.Table[WebhookHeader] + webhook_json: DF.Code | None + webhook_secret: DF.Password | None + # end: auto-generated types + + def validate(self): + self.validate_docevent() + self.validate_condition() + self.validate_request_url() + self.validate_request_body() + self.validate_repeating_fields() + self.validate_secret() + self.preview_document = None + + def on_update(self): + xhiveframework.cache.delete_value("webhooks") + + def validate_docevent(self): + if self.webhook_doctype: + is_submittable = xhiveframework.get_value("DocType", self.webhook_doctype, "is_submittable") + if not is_submittable and self.webhook_docevent in [ + "on_submit", + "on_cancel", + "on_update_after_submit", + ]: + xhiveframework.throw(_("DocType must be Submittable for the selected Doc Event")) + + def validate_condition(self): + temp_doc = xhiveframework.new_doc(self.webhook_doctype) + if self.condition: + try: + xhiveframework.safe_eval(self.condition, eval_locals=get_context(temp_doc)) + except Exception as e: + xhiveframework.throw(_("Invalid Condition: {}").format(e)) + + def validate_request_url(self): + try: + request_url = urlparse(self.request_url).netloc + if not request_url: + raise xhiveframework.ValidationError + except Exception as e: + xhiveframework.throw(_("Check Request URL"), exc=e) + + def validate_request_body(self): + if self.request_structure: + if self.request_structure == "Form URL-Encoded": + self.webhook_json = None + elif self.request_structure == "JSON": + validate_template(self.webhook_json) + self.webhook_data = [] + + def validate_repeating_fields(self): + """Error when Same Field is entered multiple times in webhook_data""" + webhook_data = [entry.fieldname for entry in self.webhook_data] + if len(webhook_data) != len(set(webhook_data)): + xhiveframework.throw(_("Same Field is entered more than once")) + + def validate_secret(self): + if self.enable_security: + try: + self.get_password("webhook_secret", False).encode("utf8") + except Exception: + xhiveframework.throw(_("Invalid Webhook Secret")) + + @xhiveframework.whitelist() + def generate_preview(self): + # This function doesn't need to do anything specific as virtual fields + # get evaluated automatically. + pass + + @property + def meets_condition(self): + if not self.condition: + return _("Yes") + + if not (self.preview_document and self.webhook_doctype): + return _("Select a document to check if it meets conditions.") + + try: + doc = xhiveframework.get_cached_doc(self.webhook_doctype, self.preview_document) + met_condition = xhiveframework.safe_eval(self.condition, eval_locals=get_context(doc)) + except Exception as e: + return _("Failed to evaluate conditions: {}").format(e) + return _("Yes") if met_condition else _("No") + + @property + def preview_request_body(self): + if not (self.preview_document and self.webhook_doctype): + return _("Select a document to preview request data") + + try: + doc = xhiveframework.get_cached_doc(self.webhook_doctype, self.preview_document) + return xhiveframework.as_json(get_webhook_data(doc, self)) + except Exception as e: + return _("Failed to compute request body: {}").format(e) + + +def get_context(doc): + return {"doc": doc, "utils": get_safe_globals().get("xhiveframework").get("utils")} + + +def enqueue_webhook(doc, webhook) -> None: + request_url = headers = data = None + try: + webhook: Webhook = xhiveframework.get_doc("Webhook", webhook.get("name")) + request_url = webhook.request_url + if webhook.is_dynamic_url: + request_url = xhiveframework.render_template(webhook.request_url, get_context(doc)) + headers = get_webhook_headers(doc, webhook) + data = get_webhook_data(doc, webhook) + + except Exception as e: + xhiveframework.logger().debug({"enqueue_webhook_error": e}) + log_request(webhook.name, doc.name, request_url, headers, data) + return + + for i in range(3): + try: + r = requests.request( + method=webhook.request_method, + url=request_url, + data=json.dumps(data, default=str), + headers=headers, + timeout=webhook.timeout or 5, + ) + r.raise_for_status() + xhiveframework.logger().debug({"webhook_success": r.text}) + log_request(webhook.name, doc.name, request_url, headers, data, r) + break + + except requests.exceptions.ReadTimeout as e: + xhiveframework.logger().debug({"webhook_error": e, "try": i + 1}) + log_request(webhook.name, doc.name, request_url, headers, data) + + except Exception as e: + xhiveframework.logger().debug({"webhook_error": e, "try": i + 1}) + log_request(webhook.name, doc.name, request_url, headers, data, r) + sleep(3 * i + 1) + if i != 2: + continue + + +def log_request( + webhook: str, + docname: str, + url: str, + headers: dict, + data: dict, + res: requests.Response | None = None, +): + request_log = xhiveframework.get_doc( + { + "doctype": "Webhook Request Log", + "webhook": webhook, + "reference_document": docname, + "user": xhiveframework.session.user if xhiveframework.session.user else None, + "url": url, + "headers": xhiveframework.as_json(headers) if headers else None, + "data": xhiveframework.as_json(data) if data else None, + "response": res and res.text, + "error": xhiveframework.get_traceback(), + } + ) + + request_log.save(ignore_permissions=True) + + +def get_webhook_headers(doc, webhook): + headers = {} + + if webhook.enable_security: + data = get_webhook_data(doc, webhook) + signature = base64.b64encode( + hmac.new( + webhook.get_password("webhook_secret").encode("utf8"), + json.dumps(data).encode("utf8"), + hashlib.sha256, + ).digest() + ) + headers[WEBHOOK_SECRET_HEADER] = signature + + if webhook.webhook_headers: + for h in webhook.webhook_headers: + if h.get("key") and h.get("value"): + headers[h.get("key")] = h.get("value") + + return headers + + +def get_webhook_data(doc, webhook): + data = {} + doc = doc.as_dict(convert_dates_to_str=True) + + if webhook.webhook_data: + data = {w.key: doc.get(w.fieldname) for w in webhook.webhook_data} + elif webhook.webhook_json: + data = xhiveframework.render_template(webhook.webhook_json, get_context(doc)) + data = json.loads(data) + + return data + + +@xhiveframework.whitelist() +def get_all_queues(): + """Fetches all workers and returns a list of available queue names.""" + xhiveframework.only_for("System Manager") + + return get_queues_timeout().keys() diff --git a/xhiveframework/integrations/doctype/webhook_data/__init__.py b/xhiveframework/integrations/doctype/webhook_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/webhook_data/webhook_data.json b/xhiveframework/integrations/doctype/webhook_data/webhook_data.json new file mode 100644 index 0000000..2ace6a9 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_data/webhook_data.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2017-09-14 12:08:50.302810", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "fieldname", + "cb_doc_data", + "key" + ], + "fields": [ + { + "fieldname": "fieldname", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Fieldname", + "reqd": 1 + }, + { + "fieldname": "cb_doc_data", + "fieldtype": "Column Break" + }, + { + "fieldname": "key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2022-08-03 12:20:52.208987", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Webhook Data", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/webhook_data/webhook_data.py b/xhiveframework/integrations/doctype/webhook_data/webhook_data.py new file mode 100644 index 0000000..2a7e8fd --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_data/webhook_data.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WebhookData(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + fieldname: DF.Literal[None] + key: DF.Data + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/webhook_header/__init__.py b/xhiveframework/integrations/doctype/webhook_header/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/webhook_header/webhook_header.json b/xhiveframework/integrations/doctype/webhook_header/webhook_header.json new file mode 100644 index 0000000..6a7e8f9 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_header/webhook_header.json @@ -0,0 +1,38 @@ +{ + "actions": [], + "creation": "2017-09-08 16:27:39.195379", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "key", + "value" + ], + "fields": [ + { + "fieldname": "key", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Key" + }, + { + "fieldname": "value", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Value" + } + ], + "istable": 1, + "links": [], + "modified": "2023-12-11 12:20:51.949422", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Webhook Header", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/webhook_header/webhook_header.py b/xhiveframework/integrations/doctype/webhook_header/webhook_header.py new file mode 100644 index 0000000..18c20c9 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_header/webhook_header.py @@ -0,0 +1,23 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.model.document import Document + + +class WebhookHeader(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + key: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + value: DF.Data | None + # end: auto-generated types + pass diff --git a/xhiveframework/integrations/doctype/webhook_request_log/__init__.py b/xhiveframework/integrations/doctype/webhook_request_log/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/integrations/doctype/webhook_request_log/test_webhook_request_log.py b/xhiveframework/integrations/doctype/webhook_request_log/test_webhook_request_log.py new file mode 100644 index 0000000..bb7150d --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_request_log/test_webhook_request_log.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestWebhookRequestLog(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.js b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.js new file mode 100644 index 0000000..bd3bbae --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Webhook Request Log", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.json b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.json new file mode 100644 index 0000000..b07197b --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.json @@ -0,0 +1,105 @@ +{ + "actions": [], + "autoname": "hash", + "creation": "2021-05-24 21:35:59.104776", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "webhook", + "reference_document", + "headers", + "data", + "column_break_4", + "user", + "url", + "response", + "error" + ], + "fields": [ + { + "fieldname": "url", + "fieldtype": "Data", + "label": "URL", + "read_only": 1 + }, + { + "fieldname": "headers", + "fieldtype": "Code", + "label": "Headers", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "response", + "fieldtype": "Code", + "label": "Response", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "data", + "fieldtype": "Code", + "label": "Data", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "user", + "fieldtype": "Link", + "label": "User", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "reference_document", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Document", + "read_only": 1 + }, + { + "fieldname": "error", + "fieldtype": "Text", + "label": "Error", + "read_only": 1 + }, + { + "fieldname": "webhook", + "fieldtype": "Link", + "label": "Webhook", + "options": "Webhook" + } + ], + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-05-21 15:50:10.414002", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Webhook Request Log", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.py b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.py new file mode 100644 index 0000000..7b79a42 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log.py @@ -0,0 +1,33 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class WebhookRequestLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + data: DF.Code | None + error: DF.Text | None + headers: DF.Code | None + reference_document: DF.Data | None + response: DF.Code | None + url: DF.Data | None + user: DF.Link | None + webhook: DF.Link | None + + # end: auto-generated types + @staticmethod + def clear_old_logs(days=30): + from xhiveframework.query_builder import Interval + from xhiveframework.query_builder.functions import Now + + table = xhiveframework.qb.DocType("Webhook Request Log") + xhiveframework.db.delete(table, filters=(table.modified < (Now() - Interval(days=days)))) diff --git a/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log_list.js b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log_list.js new file mode 100644 index 0000000..d3e5740 --- /dev/null +++ b/xhiveframework/integrations/doctype/webhook_request_log/webhook_request_log_list.js @@ -0,0 +1,7 @@ +xhiveframework.listview_settings["Webhook Request Log"] = { + onload: function (list_view) { + xhiveframework.require("logtypes.bundle.js", () => { + xhiveframework.utils.logtypes.show_log_retention_message(list_view.doctype); + }); + }, +}; diff --git a/xhiveframework/integrations/google_oauth.py b/xhiveframework/integrations/google_oauth.py new file mode 100644 index 0000000..712a535 --- /dev/null +++ b/xhiveframework/integrations/google_oauth.py @@ -0,0 +1,195 @@ +import json + +from google.oauth2.credentials import Credentials +from googleapiclient.discovery import build +from requests import get, post + +import xhiveframework +from xhiveframework.utils import get_request_site_address + +CALLBACK_METHOD = "/api/method/xhiveframework.integrations.google_oauth.callback" +_SCOPES = { + "mail": ("https://mail.google.com/"), + "contacts": ("https://www.googleapis.com/auth/contacts"), + "drive": ("https://www.googleapis.com/auth/drive"), + "indexing": ("https://www.googleapis.com/auth/indexing"), +} +_SERVICES = { + "contacts": ("people", "v1"), + "drive": ("drive", "v3"), + "indexing": ("indexing", "v3"), +} +_DOMAIN_CALLBACK_METHODS = { + "mail": "xhiveframework.email.oauth.authorize_google_access", + "contacts": "xhiveframework.integrations.doctype.google_contacts.google_contacts.authorize_access", + "drive": "xhiveframework.integrations.doctype.google_drive.google_drive.authorize_access", + "indexing": "xhiveframework.website.doctype.website_settings.google_indexing.authorize_access", +} + + +class GoogleAuthenticationError(Exception): + pass + + +class GoogleOAuth: + OAUTH_URL = "https://oauth2.googleapis.com/token" + + def __init__(self, domain: str, validate: bool = True): + self.google_settings = xhiveframework.get_single("Google Settings") + self.domain = domain.lower() + self.scopes = ( + " ".join(_SCOPES[self.domain]) + if isinstance(_SCOPES[self.domain], list | tuple) + else _SCOPES[self.domain] + ) + + if validate: + self.validate_google_settings() + + def validate_google_settings(self): + google_settings = "Google Settings" + + if not self.google_settings.enable: + xhiveframework.throw(xhiveframework._("Please enable {} before continuing.").format(google_settings)) + + if not (self.google_settings.client_id and self.google_settings.client_secret): + xhiveframework.throw(xhiveframework._("Please update {} before continuing.").format(google_settings)) + + def authorize(self, oauth_code: str) -> dict[str, str | int]: + """Returns a dict with access and refresh token. + + :param oauth_code: code got back from google upon successful auhtorization + """ + + data = { + "code": oauth_code, + "client_id": self.google_settings.client_id, + "client_secret": self.google_settings.get_password( + fieldname="client_secret", raise_exception=False + ), + "grant_type": "authorization_code", + "scope": self.scopes, + "redirect_uri": get_request_site_address(True) + CALLBACK_METHOD, + } + + return handle_response( + post(self.OAUTH_URL, data=data).json(), + "Google Oauth Authorization Error", + "Something went wrong during the authorization.", + ) + + def refresh_access_token(self, refresh_token: str) -> dict[str, str | int]: + """Refreshes google access token using refresh token""" + + data = { + "client_id": self.google_settings.client_id, + "client_secret": self.google_settings.get_password( + fieldname="client_secret", raise_exception=False + ), + "refresh_token": refresh_token, + "grant_type": "refresh_token", + "scope": self.scopes, + } + + return handle_response( + post(self.OAUTH_URL, data=data).json(), + "Google Oauth Access Token Refresh Error", + "Something went wrong during the access token generation.", + raise_err=True, + ) + + def get_authentication_url(self, state: dict[str, str]) -> dict[str, str]: + """Returns google authentication url. + + :param state: dict of values which you need on callback (for calling methods, redirection back to the form, doc name, etc) + """ + + state.update({"domain": self.domain}) + state = json.dumps(state) + callback_url = get_request_site_address(True) + CALLBACK_METHOD + + return { + "url": "https://accounts.google.com/o/oauth2/v2/auth?" + + "access_type=offline&response_type=code&prompt=consent&include_granted_scopes=true&" + + "client_id={}&scope={}&redirect_uri={}&state={}".format( + self.google_settings.client_id, self.scopes, callback_url, state + ) + } + + def get_google_service_object(self, access_token: str, refresh_token: str): + """Returns google service object""" + + credentials_dict = { + "token": access_token, + "refresh_token": refresh_token, + "token_uri": self.OAUTH_URL, + "client_id": self.google_settings.client_id, + "client_secret": self.google_settings.get_password( + fieldname="client_secret", raise_exception=False + ), + "scopes": self.scopes, + } + + return build( + serviceName=_SERVICES[self.domain][0], + version=_SERVICES[self.domain][1], + credentials=Credentials(**credentials_dict), + static_discovery=False, + ) + + +def handle_response( + response: dict[str, str | int], + error_title: str, + error_message: str, + raise_err: bool = False, +): + if "error" in response: + xhiveframework.log_error(xhiveframework._(error_title), xhiveframework._(response.get("error_description", error_message))) + + if raise_err: + xhiveframework.throw(xhiveframework._(error_title), GoogleAuthenticationError, xhiveframework._(error_message)) + + return {} + + return response + + +def is_valid_access_token(access_token: str) -> bool: + response = get("https://oauth2.googleapis.com/tokeninfo", params={"access_token": access_token}).json() + + if "error" in response: + return False + + return True + + +@xhiveframework.whitelist(methods=["GET"]) +def callback(state: str, code: str | None = None, error: str | None = None) -> None: + """Common callback for google integrations. + Invokes functions using `xhiveframework.get_attr` and also adds required (keyworded) arguments + along with committing and redirecting us back to xhiveframework site.""" + + state = json.loads(state) + redirect = state.pop("redirect", "/app") + success_query_param = state.pop("success_query_param", "") + failure_query_param = state.pop("failure_query_param", "") + + if not error: + if (domain := state.pop("domain")) in _DOMAIN_CALLBACK_METHODS: + state.update({"code": code}) + xhiveframework.get_attr(_DOMAIN_CALLBACK_METHODS[domain])(**state) + + # GET request, hence using commit to persist changes + xhiveframework.db.commit() # nosemgrep + else: + return xhiveframework.respond_as_web_page( + "Invalid Google Callback", + "The callback domain provided is not valid for Google Authentication", + http_status_code=400, + indicator_color="red", + width=640, + ) + + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = f"{redirect}?{failure_query_param if error else success_query_param}" diff --git a/xhiveframework/integrations/oauth2.py b/xhiveframework/integrations/oauth2.py new file mode 100644 index 0000000..67b02d7 --- /dev/null +++ b/xhiveframework/integrations/oauth2.py @@ -0,0 +1,244 @@ +import json +from urllib.parse import quote, urlencode + +from oauthlib.oauth2 import FatalClientError, OAuth2Error +from oauthlib.openid.connect.core.endpoints.pre_configured import Server as WebApplicationServer + +import xhiveframework +from xhiveframework.integrations.doctype.oauth_provider_settings.oauth_provider_settings import ( + get_oauth_settings, +) +from xhiveframework.oauth import ( + OAuthWebRequestValidator, + generate_json_error_response, + get_server_url, + get_userinfo, +) + + +def get_oauth_server(): + if not getattr(xhiveframework.local, "oauth_server", None): + oauth_validator = OAuthWebRequestValidator() + xhiveframework.local.oauth_server = WebApplicationServer(oauth_validator) + + return xhiveframework.local.oauth_server + + +def sanitize_kwargs(param_kwargs): + """Remove 'data' and 'cmd' keys, if present.""" + arguments = param_kwargs + arguments.pop("data", None) + arguments.pop("cmd", None) + + return arguments + + +def encode_params(params): + """ + Encode a dict of params into a query string. + + Use `quote_via=urllib.parse.quote` so that whitespaces will be encoded as + `%20` instead of as `+`. This is needed because oauthlib cannot handle `+` + as a whitespace. + """ + return urlencode(params, quote_via=quote) + + +@xhiveframework.whitelist() +def approve(*args, **kwargs): + r = xhiveframework.request + + try: + ( + scopes, + xhiveframework.flags.oauth_credentials, + ) = get_oauth_server().validate_authorization_request(r.url, r.method, r.get_data(), r.headers) + + headers, body, status = get_oauth_server().create_authorization_response( + uri=xhiveframework.flags.oauth_credentials["redirect_uri"], + body=r.get_data(), + headers=r.headers, + scopes=scopes, + credentials=xhiveframework.flags.oauth_credentials, + ) + uri = headers.get("Location", None) + + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = uri + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@xhiveframework.whitelist(allow_guest=True) +def authorize(**kwargs): + success_url = "/api/method/xhiveframework.integrations.oauth2.approve?" + encode_params(sanitize_kwargs(kwargs)) + failure_url = xhiveframework.form_dict["redirect_uri"] + "?error=access_denied" + + if xhiveframework.session.user == "Guest": + # Force login, redirect to preauth again. + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = "/login?" + encode_params({"redirect-to": xhiveframework.request.url}) + else: + try: + r = xhiveframework.request + ( + scopes, + xhiveframework.flags.oauth_credentials, + ) = get_oauth_server().validate_authorization_request(r.url, r.method, r.get_data(), r.headers) + + skip_auth = xhiveframework.db.get_value( + "OAuth Client", + xhiveframework.flags.oauth_credentials["client_id"], + "skip_authorization", + ) + unrevoked_tokens = xhiveframework.get_all("OAuth Bearer Token", filters={"status": "Active"}) + + if skip_auth or (get_oauth_settings().skip_authorization == "Auto" and unrevoked_tokens): + xhiveframework.local.response["type"] = "redirect" + xhiveframework.local.response["location"] = success_url + else: + if "openid" in scopes: + scopes.remove("openid") + scopes.extend(["Full Name", "Email", "User Image", "Roles"]) + + # Show Allow/Deny screen. + response_html_params = xhiveframework._dict( + { + "client_id": xhiveframework.db.get_value("OAuth Client", kwargs["client_id"], "app_name"), + "success_url": success_url, + "failure_url": failure_url, + "details": scopes, + } + ) + resp_html = xhiveframework.render_template( + "templates/includes/oauth_confirmation.html", response_html_params + ) + xhiveframework.respond_as_web_page("Confirm Access", resp_html, primary_action=None) + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@xhiveframework.whitelist(allow_guest=True) +def get_token(*args, **kwargs): + try: + r = xhiveframework.request + headers, body, status = get_oauth_server().create_token_response( + r.url, r.method, r.form, r.headers, xhiveframework.flags.oauth_credentials + ) + body = xhiveframework._dict(json.loads(body)) + + if body.error: + xhiveframework.local.response = body + xhiveframework.local.response["http_status_code"] = 400 + return + + xhiveframework.local.response = body + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@xhiveframework.whitelist(allow_guest=True) +def revoke_token(*args, **kwargs): + try: + r = xhiveframework.request + headers, body, status = get_oauth_server().create_revocation_response( + r.url, + headers=r.headers, + body=r.form, + http_method=r.method, + ) + except (FatalClientError, OAuth2Error): + pass + + # status_code must be 200 + xhiveframework.local.response = xhiveframework._dict({}) + xhiveframework.local.response["http_status_code"] = status or 200 + return + + +@xhiveframework.whitelist() +def openid_profile(*args, **kwargs): + try: + r = xhiveframework.request + headers, body, status = get_oauth_server().create_userinfo_response( + r.url, + headers=r.headers, + body=r.form, + ) + body = xhiveframework._dict(json.loads(body)) + xhiveframework.local.response = body + return + + except (FatalClientError, OAuth2Error) as e: + return generate_json_error_response(e) + + +@xhiveframework.whitelist(allow_guest=True) +def openid_configuration(): + xhiveframework_server_url = get_server_url() + xhiveframework.local.response = xhiveframework._dict( + { + "issuer": xhiveframework_server_url, + "authorization_endpoint": f"{xhiveframework_server_url}/api/method/xhiveframework.integrations.oauth2.authorize", + "token_endpoint": f"{xhiveframework_server_url}/api/method/xhiveframework.integrations.oauth2.get_token", + "userinfo_endpoint": f"{xhiveframework_server_url}/api/method/xhiveframework.integrations.oauth2.openid_profile", + "revocation_endpoint": f"{xhiveframework_server_url}/api/method/xhiveframework.integrations.oauth2.revoke_token", + "introspection_endpoint": f"{xhiveframework_server_url}/api/method/xhiveframework.integrations.oauth2.introspect_token", + "response_types_supported": [ + "code", + "token", + "code id_token", + "code token id_token", + "id_token", + "id_token token", + ], + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": ["HS256"], + } + ) + + +@xhiveframework.whitelist(allow_guest=True) +def introspect_token(token=None, token_type_hint=None): + if token_type_hint not in ["access_token", "refresh_token"]: + token_type_hint = "access_token" + try: + bearer_token = None + if token_type_hint == "access_token": + bearer_token = xhiveframework.get_doc("OAuth Bearer Token", {"access_token": token}) + elif token_type_hint == "refresh_token": + bearer_token = xhiveframework.get_doc("OAuth Bearer Token", {"refresh_token": token}) + + client = xhiveframework.get_doc("OAuth Client", bearer_token.client) + + token_response = xhiveframework._dict( + { + "client_id": client.client_id, + "trusted_client": client.skip_authorization, + "active": bearer_token.status == "Active", + "exp": round(bearer_token.expiration_time.timestamp()), + "scope": bearer_token.scopes, + } + ) + + if "openid" in bearer_token.scopes: + sub = xhiveframework.get_value( + "User Social Login", + {"provider": "xhiveframework", "parent": bearer_token.user}, + "userid", + ) + + if sub: + token_response.update({"sub": sub}) + user = xhiveframework.get_doc("User", bearer_token.user) + userinfo = get_userinfo(user) + token_response.update(userinfo) + + xhiveframework.local.response = token_response + + except Exception: + xhiveframework.local.response = xhiveframework._dict({"active": False}) diff --git a/xhiveframework/integrations/oauth2_logins.py b/xhiveframework/integrations/oauth2_logins.py new file mode 100644 index 0000000..a5aa631 --- /dev/null +++ b/xhiveframework/integrations/oauth2_logins.py @@ -0,0 +1,63 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +import xhiveframework.utils +from xhiveframework.utils.oauth import login_via_oauth2, login_via_oauth2_id_token + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_google(code: str, state: str): + login_via_oauth2("google", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_github(code: str, state: str): + login_via_oauth2("github", code, state) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_facebook(code: str, state: str): + login_via_oauth2("facebook", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_xhiveframework(code: str, state: str): + login_via_oauth2("xhiveframework", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_office365(code: str, state: str): + login_via_oauth2_id_token("office_365", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_salesforce(code: str, state: str): + login_via_oauth2("salesforce", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def login_via_fairlogin(code: str, state: str): + login_via_oauth2("fairlogin", code, state, decoder=decoder_compat) + + +@xhiveframework.whitelist(allow_guest=True) +def custom(code: str, state: str): + """ + Callback for processing code and state for user added providers + + process social login from /api/method/xhiveframework.integrations.oauth2_logins.custom/ + """ + path = xhiveframework.request.path[1:].split("/") + if len(path) == 4 and path[3]: + provider = path[3] + # Validates if provider doctype exists + if xhiveframework.db.exists("Social Login Key", provider): + login_via_oauth2(provider, code, state, decoder=decoder_compat) + + +def decoder_compat(b): + # https://github.com/litl/rauth/issues/145#issuecomment-31199471 + return json.loads(bytes(b).decode("utf-8")) diff --git a/xhiveframework/integrations/offsite_backup_utils.py b/xhiveframework/integrations/offsite_backup_utils.py new file mode 100644 index 0000000..4a044d8 --- /dev/null +++ b/xhiveframework/integrations/offsite_backup_utils.py @@ -0,0 +1,119 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import glob +import os + +import xhiveframework +from xhiveframework.utils import cint, split_emails + + +def send_email(success, service_name, doctype, email_field, error_status=None): + recipients = get_recipients(doctype, email_field) + if not recipients: + xhiveframework.log_error( + f"No Email Recipient found for {service_name}", + f"{service_name}: Failed to send backup status email", + ) + return + + if success: + if not xhiveframework.db.get_single_value(doctype, "send_email_for_successful_backup"): + return + + subject = "Backup Upload Successful" + message = """ +

      Backup Uploaded Successfully!

      +

      Hi there, this is just to inform you that your backup was successfully uploaded to your {} bucket. So relax!

      """.format( + service_name + ) + else: + subject = "[Warning] Backup Upload Failed" + message = f""" +

      Backup Upload Failed!

      +

      Oops, your automated backup to {service_name} failed.

      +

      Error message: {error_status}

      +

      Please contact your system manager for more information.

      """ + + xhiveframework.sendmail(recipients=recipients, subject=subject, message=message) + + +def get_recipients(doctype, email_field): + if not xhiveframework.db: + xhiveframework.connect() + + return split_emails(xhiveframework.db.get_value(doctype, None, email_field)) + + +def get_latest_backup_file(with_files=False): + from xhiveframework.utils.backups import BackupGenerator + + odb = BackupGenerator( + xhiveframework.conf.db_name, + xhiveframework.conf.db_name, + xhiveframework.conf.db_password, + db_host=xhiveframework.conf.db_host, + db_port=xhiveframework.conf.db_port, + db_type=xhiveframework.conf.db_type, + ) + database, public, private, config = odb.get_recent_backup(older_than=24 * 30) + + if with_files: + return database, config, public, private + + return database, config + + +def get_file_size(file_path, unit="MB"): + file_size = os.path.getsize(file_path) + + memory_size_unit_mapper = {"KB": 1, "MB": 2, "GB": 3, "TB": 4} + i = 0 + while i < memory_size_unit_mapper[unit]: + file_size = file_size / 1000.0 + i += 1 + + return file_size + + +def get_chunk_site(file_size): + """this function will return chunk size in megabytes based on file size""" + + file_size_in_gb = cint(file_size / 1024 / 1024) + + MB = 1024 * 1024 + if file_size_in_gb > 5000: + return 200 * MB + elif file_size_in_gb >= 3000: + return 150 * MB + elif file_size_in_gb >= 1000: + return 100 * MB + elif file_size_in_gb >= 500: + return 50 * MB + else: + return 15 * MB + + +def validate_file_size(): + xhiveframework.flags.create_new_backup = True + latest_file, site_config = get_latest_backup_file() + file_size = get_file_size(latest_file, unit="GB") if latest_file else 0 + + if file_size > 1: + xhiveframework.flags.create_new_backup = False + + +def generate_files_backup(): + from xhiveframework.utils.backups import BackupGenerator + + backup = BackupGenerator( + xhiveframework.conf.db_name, + xhiveframework.conf.db_name, + xhiveframework.conf.db_password, + db_host=xhiveframework.conf.db_host, + db_port=xhiveframework.conf.db_port, + db_type=xhiveframework.conf.db_type, + ) + + backup.set_backup_file_name() + backup.zip_files() diff --git a/xhiveframework/integrations/utils.py b/xhiveframework/integrations/utils.py new file mode 100644 index 0000000..e6720c9 --- /dev/null +++ b/xhiveframework/integrations/utils.py @@ -0,0 +1,111 @@ +# Copyright (c) 2019, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import datetime +import json +from urllib.parse import parse_qs + +import xhiveframework +from xhiveframework import _ +from xhiveframework.utils import get_request_session + + +def make_request(method, url, auth=None, headers=None, data=None, json=None, params=None): + auth = auth or "" + data = data or {} + headers = headers or {} + + try: + s = get_request_session() + xhiveframework.flags.integration_request = s.request( + method, url, data=data, auth=auth, headers=headers, json=json, params=params + ) + xhiveframework.flags.integration_request.raise_for_status() + + if xhiveframework.flags.integration_request.headers.get("content-type") == "text/plain; charset=utf-8": + return parse_qs(xhiveframework.flags.integration_request.text) + + return xhiveframework.flags.integration_request.json() + except Exception as exc: + xhiveframework.log_error() + raise exc + + +def make_get_request(url, **kwargs): + return make_request("GET", url, **kwargs) + + +def make_post_request(url, **kwargs): + return make_request("POST", url, **kwargs) + + +def make_put_request(url, **kwargs): + return make_request("PUT", url, **kwargs) + + +def make_patch_request(url, **kwargs): + return make_request("PATCH", url, **kwargs) + + +def make_delete_request(url, **kwargs): + return make_request("DELETE", url, **kwargs) + + +def create_request_log( + data, + integration_type=None, + service_name=None, + name=None, + error=None, + request_headers=None, + output=None, + **kwargs, +): + """ + DEPRECATED: The parameter integration_type will be removed in the next major release. + Use is_remote_request instead. + """ + if integration_type == "Remote": + kwargs["is_remote_request"] = 1 + + elif integration_type == "Subscription Notification": + kwargs["request_description"] = integration_type + + reference_doctype = reference_docname = None + if "reference_doctype" not in kwargs: + if isinstance(data, str): + data = json.loads(data) + + reference_doctype = data.get("reference_doctype") + reference_docname = data.get("reference_docname") + + integration_request = xhiveframework.get_doc( + { + "doctype": "Integration Request", + "integration_request_service": service_name, + "request_headers": get_json(request_headers), + "data": get_json(data), + "output": get_json(output), + "error": get_json(error), + "reference_doctype": reference_doctype, + "reference_docname": reference_docname, + **kwargs, + } + ) + + if name: + integration_request.flags._name = name + + integration_request.insert(ignore_permissions=True) + xhiveframework.db.commit() + + return integration_request + + +def get_json(obj): + return obj if isinstance(obj, str) else xhiveframework.as_json(obj, indent=1) + + +def json_handler(obj): + if isinstance(obj, datetime.date | datetime.timedelta | datetime.datetime): + return str(obj) diff --git a/xhiveframework/integrations/workspace/integrations/integrations.json b/xhiveframework/integrations/workspace/integrations/integrations.json new file mode 100644 index 0000000..5dab61a --- /dev/null +++ b/xhiveframework/integrations/workspace/integrations/integrations.json @@ -0,0 +1,235 @@ +{ + "charts": [], + "content": "[{\"id\":\"NPK_AfSLQ2\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"lDOo58F7ZI\",\"type\":\"card\",\"data\":{\"card_name\":\"Backup\",\"col\":4}},{\"id\":\"ij1pcK8jst\",\"type\":\"card\",\"data\":{\"card_name\":\"Google Services\",\"col\":4}},{\"id\":\"aTlMujEHpN\",\"type\":\"card\",\"data\":{\"card_name\":\"Authentication\",\"col\":4}},{\"id\":\"gY5NXKtXss\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"n_CI3GGqW-\",\"type\":\"card\",\"data\":{\"card_name\":\"Push Notifications\",\"col\":4}}]", + "creation": "2020-03-02 15:16:18.714190", + "custom_blocks": [], + "docstatus": 0, + "doctype": "Workspace", + "for_user": "", + "hide_custom": 0, + "icon": "integration", + "idx": 0, + "is_hidden": 0, + "label": "Integrations", + "links": [ + { + "hidden": 0, + "is_query_report": 0, + "label": "Backup", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Dropbox Settings", + "link_count": 0, + "link_to": "Dropbox Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "S3 Backup Settings", + "link_count": 0, + "link_to": "S3 Backup Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Google Drive", + "link_count": 0, + "link_to": "Google Drive", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Google Services", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Google Settings", + "link_count": 0, + "link_to": "Google Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Google Contacts", + "link_count": 0, + "link_to": "Google Contacts", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Google Calendar", + "link_count": 0, + "link_to": "Google Calendar", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Google Drive", + "link_count": 0, + "link_to": "Google Drive", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "link_count": 0, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Webhook", + "link_count": 0, + "link_to": "Webhook", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Slack Webhook URL", + "link_count": 0, + "link_to": "Slack Webhook URL", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "SMS Settings", + "link_count": 0, + "link_to": "SMS Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Authentication", + "link_count": 4, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Social Login Key", + "link_count": 0, + "link_to": "Social Login Key", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "LDAP Settings", + "link_count": 0, + "link_to": "LDAP Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "OAuth Client", + "link_count": 0, + "link_to": "OAuth Client", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "OAuth Provider Settings", + "link_count": 0, + "link_to": "OAuth Provider Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Push Notifications", + "link_count": 1, + "link_type": "DocType", + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Push Notification Settings", + "link_count": 0, + "link_to": "Push Notification Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + } + ], + "modified": "2024-02-28 10:47:38.188832", + "modified_by": "Administrator", + "module": "Integrations", + "name": "Integrations", + "number_cards": [], + "owner": "Administrator", + "parent_page": "", + "public": 1, + "quick_lists": [], + "restrict_to_domain": "", + "roles": [], + "sequence_id": 20.0, + "shortcuts": [], + "title": "Integrations" +} \ No newline at end of file diff --git a/xhiveframework/integrations/xhiveframework_providers/__init__.py b/xhiveframework/integrations/xhiveframework_providers/__init__.py new file mode 100644 index 0000000..c945f15 --- /dev/null +++ b/xhiveframework/integrations/xhiveframework_providers/__init__.py @@ -0,0 +1,13 @@ +# imports - standard imports +import sys + +# imports - module imports +from xhiveframework.integrations.xhiveframework_providers.xhiveframeworkcloud import xhiveframeworkcloud_migrator + + +def migrate_to(local_site, xhiveframework_provider): + if xhiveframework_provider in ("xhiveframework.cloud", "xhiveframeworkcloud.com"): + return xhiveframeworkcloud_migrator(local_site) + else: + print(f"{xhiveframework_provider} is not supported yet") + sys.exit(1) diff --git a/xhiveframework/integrations/xhiveframework_providers/xhiveframeworkcloud.py b/xhiveframework/integrations/xhiveframework_providers/xhiveframeworkcloud.py new file mode 100644 index 0000000..f087c10 --- /dev/null +++ b/xhiveframework/integrations/xhiveframework_providers/xhiveframeworkcloud.py @@ -0,0 +1,32 @@ +import click +import requests + +import xhiveframework +from xhiveframework.core.utils import html2text + + +def xhiveframeworkcloud_migrator(local_site): + print("Retrieving Site Migrator...") + remote_site = xhiveframework.conf.xhiveframeworkcloud_url or "xhiveframeworkcloud.com" + request_url = f"https://{remote_site}/api/method/press.api.script" + request = requests.get(request_url) + + if request.status_code / 100 != 2: + print(f"Request exited with Status Code: {request.status_code}\nPayload: {html2text(request.text)}") + click.secho( + "Some errors occurred while recovering the migration script. Please contact us @ XhiveFramework Cloud if this issue persists", + fg="yellow", + ) + return + + script_contents = request.json()["message"] + + import os + import sys + import tempfile + + py = sys.executable + script = tempfile.NamedTemporaryFile(mode="w") + script.write(script_contents) + print(f"Site Migrator stored at {script.name}") + os.execv(py, [py, script.name, local_site]) diff --git a/xhiveframework/middlewares.py b/xhiveframework/middlewares.py new file mode 100644 index 0000000..483eba5 --- /dev/null +++ b/xhiveframework/middlewares.py @@ -0,0 +1,28 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import os + +from werkzeug.exceptions import NotFound +from werkzeug.middleware.shared_data import SharedDataMiddleware + +import xhiveframework +from xhiveframework.utils import cstr, get_site_name + + +class StaticDataMiddleware(SharedDataMiddleware): + def __call__(self, environ, start_response): + self.environ = environ + return super().__call__(environ, start_response) + + def get_directory_loader(self, directory): + def loader(path): + site = get_site_name(xhiveframework.app._site or self.environ.get("HTTP_HOST")) + path = os.path.join(directory, site, "public", "files", cstr(path)) + if os.path.isfile(path): + return os.path.basename(path), self._opener(path) + else: + raise NotFound + # return None, None + + return loader diff --git a/xhiveframework/migrate.py b/xhiveframework/migrate.py new file mode 100644 index 0000000..a717ee8 --- /dev/null +++ b/xhiveframework/migrate.py @@ -0,0 +1,190 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import contextlib +import functools +import json +import os +from textwrap import dedent + +import xhiveframework +import xhiveframework.model.sync +import xhiveframework.modules.patch_handler +import xhiveframework.translate +from xhiveframework.cache_manager import clear_global_cache +from xhiveframework.core.doctype.language.language import sync_languages +from xhiveframework.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs +from xhiveframework.database.schema import add_column +from xhiveframework.deferred_insert import save_to_db as flush_deferred_inserts +from xhiveframework.desk.notifications import clear_notifications +from xhiveframework.modules.patch_handler import PatchType +from xhiveframework.modules.utils import sync_customizations +from xhiveframework.search.website_search import build_index_for_all_routes +from xhiveframework.utils.connections import check_connection +from xhiveframework.utils.dashboard import sync_dashboards +from xhiveframework.utils.fixtures import sync_fixtures +from xhiveframework.website.utils import clear_website_cache + +BENCH_START_MESSAGE = dedent( + """ + Cannot run bench migrate without the services running. + If you are running bench in development mode, make sure that bench is running: + + $ bench start + + Otherwise, check the server logs and ensure that all the required services are running. + """ +) + + +def atomic(method): + @functools.wraps(method) + def wrapper(*args, **kwargs): + try: + ret = method(*args, **kwargs) + xhiveframework.db.commit() + return ret + except Exception as e: + # database itself can be gone while attempting rollback. + # We should preserve original exception in this case. + with contextlib.suppress(Exception): + xhiveframework.db.rollback() + raise e + + return wrapper + + +class SiteMigration: + """Migrate all apps to the current version, will: + - run before migrate hooks + - run patches + - sync doctypes (schema) + - sync dashboards + - sync jobs + - sync fixtures + - sync customizations + - sync languages + - sync web pages (from /www) + - run after migrate hooks + """ + + def __init__(self, skip_failing: bool = False, skip_search_index: bool = False) -> None: + self.skip_failing = skip_failing + self.skip_search_index = skip_search_index + + def setUp(self): + """Complete setup required for site migration""" + xhiveframework.flags.touched_tables = set() + self.touched_tables_file = xhiveframework.get_site_path("touched_tables.json") + xhiveframework.clear_cache() + add_column(doctype="DocType", column_name="migration_hash", fieldtype="Data") + clear_global_cache() + + if os.path.exists(self.touched_tables_file): + os.remove(self.touched_tables_file) + + xhiveframework.flags.in_migrate = True + + def tearDown(self): + """Run operations that should be run post schema updation processes + This should be executed irrespective of outcome + """ + xhiveframework.translate.clear_cache() + clear_website_cache() + clear_notifications() + + with open(self.touched_tables_file, "w") as f: + json.dump(list(xhiveframework.flags.touched_tables), f, sort_keys=True, indent=4) + + if not self.skip_search_index: + print(f"Queued rebuilding of search index for {xhiveframework.local.site}") + xhiveframework.enqueue(build_index_for_all_routes, queue="long") + + xhiveframework.publish_realtime("version-update") + xhiveframework.flags.touched_tables.clear() + xhiveframework.flags.in_migrate = False + + @atomic + def pre_schema_updates(self): + """Executes `before_migrate` hooks""" + for app in xhiveframework.get_installed_apps(): + for fn in xhiveframework.get_hooks("before_migrate", app_name=app): + xhiveframework.get_attr(fn)() + + @atomic + def run_schema_updates(self): + """Run patches as defined in patches.txt, sync schema changes as defined in the {doctype}.json files""" + xhiveframework.modules.patch_handler.run_all( + skip_failing=self.skip_failing, patch_type=PatchType.pre_model_sync + ) + xhiveframework.model.sync.sync_all() + xhiveframework.modules.patch_handler.run_all( + skip_failing=self.skip_failing, patch_type=PatchType.post_model_sync + ) + + @atomic + def post_schema_updates(self): + """Execute pending migration tasks post patches execution & schema sync + This includes: + * Sync `Scheduled Job Type` and scheduler events defined in hooks + * Sync fixtures & custom scripts + * Sync in-Desk Module Dashboards + * Sync customizations: Custom Fields, Property Setters, Custom Permissions + * Sync XhiveFramework's internal language master + * Flush deferred inserts made during maintenance mode. + * Sync Portal Menu Items + * Sync Installed Applications Version History + * Execute `after_migrate` hooks + """ + sync_jobs() + sync_fixtures() + sync_dashboards() + sync_customizations() + sync_languages() + flush_deferred_inserts() + xhiveframework.model.sync.remove_orphan_doctypes() + + xhiveframework.get_single("Portal Settings").sync_menu() + xhiveframework.get_single("Installed Applications").update_versions() + + for app in xhiveframework.get_installed_apps(): + for fn in xhiveframework.get_hooks("after_migrate", app_name=app): + xhiveframework.get_attr(fn)() + + def required_services_running(self) -> bool: + """Returns True if all required services are running. Returns False and prints + instructions to stdout when required services are not available. + """ + service_status = check_connection(redis_services=["redis_cache"]) + are_services_running = all(service_status.values()) + + if not are_services_running: + for service in service_status: + if not service_status.get(service, True): + print(f"Service {service} is not running.") + print(BENCH_START_MESSAGE) + + return are_services_running + + def run(self, site: str): + """Run Migrate operation on site specified. This method initializes + and destroys connections to the site database. + """ + from xhiveframework.utils.synchronization import filelock + + if site: + xhiveframework.init(site=site) + xhiveframework.connect() + + if not self.required_services_running(): + raise SystemExit(1) + + with filelock("bench_migrate", timeout=1): + self.setUp() + try: + self.pre_schema_updates() + self.run_schema_updates() + self.post_schema_updates() + finally: + self.tearDown() + xhiveframework.destroy() diff --git a/xhiveframework/model/__init__.py b/xhiveframework/model/__init__.py new file mode 100644 index 0000000..14e0ccb --- /dev/null +++ b/xhiveframework/model/__init__.py @@ -0,0 +1,254 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# model __init__.py +import xhiveframework +from xhiveframework import _ + +data_fieldtypes = ( + "Currency", + "Int", + "Long Int", + "Float", + "Percent", + "Check", + "Small Text", + "Long Text", + "Code", + "Text Editor", + "Markdown Editor", + "HTML Editor", + "Date", + "Datetime", + "Time", + "Text", + "Data", + "Link", + "Dynamic Link", + "Password", + "Select", + "Rating", + "Read Only", + "Attach", + "Attach Image", + "Signature", + "Color", + "Barcode", + "Geolocation", + "Duration", + "Icon", + "Phone", + "Autocomplete", + "JSON", +) + +float_like_fields = {"Float", "Currency", "Percent"} +datetime_fields = {"Datetime", "Date", "Time"} + +attachment_fieldtypes = ( + "Attach", + "Attach Image", +) + +no_value_fields = ( + "Section Break", + "Column Break", + "Tab Break", + "HTML", + "Table", + "Table MultiSelect", + "Button", + "Image", + "Fold", + "Heading", +) + +display_fieldtypes = ( + "Section Break", + "Column Break", + "Tab Break", + "HTML", + "Button", + "Image", + "Fold", + "Heading", +) + +numeric_fieldtypes = ("Currency", "Int", "Long Int", "Float", "Percent", "Check") + +data_field_options = ("Email", "Name", "Phone", "URL", "Barcode") + +default_fields = ( + "doctype", + "name", + "owner", + "creation", + "modified", + "modified_by", + "docstatus", + "idx", +) + +child_table_fields = ("parent", "parentfield", "parenttype") + +optional_fields = ("_user_tags", "_comments", "_assign", "_liked_by", "_seen") + +table_fields = ("Table", "Table MultiSelect") + +core_doctypes_list = ( + "DefaultValue", + "DocType", + "DocField", + "DocPerm", + "DocType Action", + "DocType Link", + "User", + "Role", + "Has Role", + "Page", + "Module Def", + "Print Format", + "Report", + "Customize Form", + "Customize Form Field", + "Property Setter", + "Custom Field", + "Client Script", +) + +log_types = ( + "Version", + "Error Log", + "Scheduled Job Log", + "Event Sync Log", + "Event Update Log", + "Access Log", + "View Log", + "Activity Log", + "Energy Point Log", + "Notification Log", + "Email Queue", + "DocShare", + "Document Follow", + "Console Log", +) + +std_fields = [ + {"fieldname": "name", "fieldtype": "Link", "label": "ID"}, + {"fieldname": "owner", "fieldtype": "Link", "label": "Created By", "options": "User"}, + {"fieldname": "idx", "fieldtype": "Int", "label": "Index"}, + {"fieldname": "creation", "fieldtype": "Datetime", "label": "Created On"}, + {"fieldname": "modified", "fieldtype": "Datetime", "label": "Last Updated On"}, + { + "fieldname": "modified_by", + "fieldtype": "Link", + "label": "Last Updated By", + "options": "User", + }, + {"fieldname": "_user_tags", "fieldtype": "Data", "label": "Tags"}, + {"fieldname": "_liked_by", "fieldtype": "Data", "label": "Liked By"}, + {"fieldname": "_comments", "fieldtype": "Text", "label": "Comments"}, + {"fieldname": "_assign", "fieldtype": "Text", "label": "Assigned To"}, + {"fieldname": "docstatus", "fieldtype": "Int", "label": "Document Status"}, +] + + +def delete_fields(args_dict, delete=0): + """ + Delete a field. + * Deletes record from `tabDocField` + * If not single doctype: Drops column from table + * If single, deletes record from `tabSingles` + args_dict = { dt: [field names] } + """ + import xhiveframework.utils + + for dt in args_dict: + fields = args_dict[dt] + if not fields: + continue + + xhiveframework.db.delete( + "DocField", + { + "parent": dt, + "fieldname": ("in", fields), + }, + ) + + # Delete the data/column only if delete is specified + if not delete: + continue + + if xhiveframework.db.get_value("DocType", dt, "issingle"): + xhiveframework.db.delete( + "Singles", + { + "doctype": dt, + "field": ("in", fields), + }, + ) + else: + existing_fields = xhiveframework.db.describe(dt) + existing_fields = existing_fields and [e[0] for e in existing_fields] or [] + fields_need_to_delete = set(fields) & set(existing_fields) + if not fields_need_to_delete: + continue + + if xhiveframework.db.db_type == "mariadb": + # mariadb implicitly commits before DDL, make it explicit + xhiveframework.db.commit() + + query = "ALTER TABLE `tab%s` " % dt + ", ".join( + "DROP COLUMN `%s`" % f for f in fields_need_to_delete + ) + xhiveframework.db.sql(query) + + if xhiveframework.db.db_type == "postgres": + # commit the results to db + xhiveframework.db.commit() + + +def get_permitted_fields( + doctype: str, + parenttype: str | None = None, + user: str | None = None, + permission_type: str | None = None, + *, + ignore_virtual=False, +) -> list[str]: + meta = xhiveframework.get_meta(doctype) + valid_columns = meta.get_valid_columns() + + if doctype in core_doctypes_list: + return valid_columns + + # DocType has only fields of type Table (Table, Table MultiSelect) + if set(valid_columns).issubset(default_fields): + return valid_columns + + if permission_type is None: + permission_type = "select" if xhiveframework.only_has_select_perm(doctype, user=user) else "read" + + meta_fields = meta.default_fields.copy() + optional_meta_fields = [x for x in optional_fields if x in valid_columns] + + if permitted_fields := meta.get_permitted_fieldnames( + parenttype=parenttype, + user=user, + permission_type=permission_type, + with_virtual_fields=not ignore_virtual, + ): + if permission_type == "select": + return permitted_fields + + if meta.istable: + meta_fields.extend(child_table_fields) + + return meta_fields + permitted_fields + optional_meta_fields + + return meta_fields + optional_meta_fields + + +def is_default_field(fieldname: str) -> bool: + return fieldname in default_fields diff --git a/xhiveframework/model/base_document.py b/xhiveframework/model/base_document.py new file mode 100644 index 0000000..88f54bd --- /dev/null +++ b/xhiveframework/model/base_document.py @@ -0,0 +1,1308 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import datetime +import json +import weakref +from functools import cached_property +from typing import TYPE_CHECKING, TypeVar + +import xhiveframework +from xhiveframework import _, _dict +from xhiveframework.model import ( + child_table_fields, + datetime_fields, + default_fields, + display_fieldtypes, + float_like_fields, + get_permitted_fields, + table_fields, +) +from xhiveframework.model.docstatus import DocStatus +from xhiveframework.model.naming import set_new_name +from xhiveframework.model.utils.link_count import notify_link_count +from xhiveframework.modules import load_doctype_module +from xhiveframework.utils import ( + cast_fieldtype, + cint, + compare, + cstr, + flt, + is_a_property, + now, + sanitize_html, + strip_html, +) +from xhiveframework.utils.html_utils import unescape_html + +if TYPE_CHECKING: + from xhiveframework.model.document import Document + +D = TypeVar("D", bound="Document") + + +max_positive_value = {"smallint": 2**15 - 1, "int": 2**31 - 1, "bigint": 2**63 - 1} + +DOCTYPE_TABLE_FIELDS = [ + _dict(fieldname="fields", options="DocField"), + _dict(fieldname="permissions", options="DocPerm"), + _dict(fieldname="actions", options="DocType Action"), + _dict(fieldname="links", options="DocType Link"), + _dict(fieldname="states", options="DocType State"), +] + +TABLE_DOCTYPES_FOR_DOCTYPE = {df["fieldname"]: df["options"] for df in DOCTYPE_TABLE_FIELDS} +DOCTYPES_FOR_DOCTYPE = {"DocType", *TABLE_DOCTYPES_FOR_DOCTYPE.values()} + + +def get_controller(doctype): + """ + Returns the locally cached **class** object of the given DocType. + For `custom` type, returns `xhiveframework.model.document.Document`. + + :param doctype: DocType name as string. + """ + + if xhiveframework.local.dev_server or xhiveframework.flags.in_migrate: + return import_controller(doctype) + + site_controllers = xhiveframework.controllers.setdefault(xhiveframework.local.site, {}) + if doctype not in site_controllers: + site_controllers[doctype] = import_controller(doctype) + + return site_controllers[doctype] + + +def import_controller(doctype): + from xhiveframework.model.document import Document + from xhiveframework.utils.nestedset import NestedSet + + module_name = "Core" + if doctype not in DOCTYPES_FOR_DOCTYPE: + doctype_info = xhiveframework.db.get_value("DocType", doctype, fieldname="*") + if doctype_info: + if doctype_info.custom: + return NestedSet if doctype_info.is_tree else Document + module_name = doctype_info.module + + module_path = None + class_overrides = xhiveframework.get_hooks("override_doctype_class") + if class_overrides and class_overrides.get(doctype): + import_path = class_overrides[doctype][-1] + module_path, classname = import_path.rsplit(".", 1) + module = xhiveframework.get_module(module_path) + + else: + module = load_doctype_module(doctype, module_name) + classname = doctype.replace(" ", "").replace("-", "") + + class_ = getattr(module, classname, None) + if class_ is None: + raise ImportError( + doctype + if module_path is None + else f"{doctype}: {classname} does not exist in module {module_path}" + ) + + if not issubclass(class_, BaseDocument): + raise ImportError(f"{doctype}: {classname} is not a subclass of BaseDocument") + + return class_ + + +class BaseDocument: + _reserved_keywords = frozenset( + ( + "doctype", + "meta", + "flags", + "parent_doc", + "_table_fields", + "_valid_columns", + "_doc_before_save", + "_table_fieldnames", + "_reserved_keywords", + "permitted_fieldnames", + "dont_update_if_missing", + ) + ) + + def __init__(self, d): + if d.get("doctype"): + self.doctype = d["doctype"] + + self._table_fieldnames = {df.fieldname for df in self._get_table_fields()} + self.update(d) + self.dont_update_if_missing = [] + + if hasattr(self, "__setup__"): + self.__setup__() + + @cached_property + def meta(self): + return xhiveframework.get_meta(self.doctype) + + @cached_property + def permitted_fieldnames(self): + return get_permitted_fields(doctype=self.doctype, parenttype=getattr(self, "parenttype", None)) + + def __getstate__(self): + """ + Called when pickling. + Returns a copy of `__dict__` excluding unpicklable values like `meta`. + More info: https://docs.python.org/3/library/pickle.html#handling-stateful-objects + """ + + # Always use the dict.copy() method to avoid modifying the original state + state = self.__dict__.copy() + self.remove_unpicklable_values(state) + + return state + + def remove_unpicklable_values(self, state): + """Remove unpicklable values before pickling""" + + state.pop("meta", None) + state.pop("permitted_fieldnames", None) + state.pop("_parent_doc", None) + + def update(self, d): + """Update multiple fields of a doctype using a dictionary of key-value pairs. + + Example: + doc.update({ + "user": "admin", + "balance": 42000 + }) + """ + + # set name first, as it is used a reference in child document + if "name" in d: + self.name = d["name"] + + ignore_children = hasattr(self, "flags") and self.flags.ignore_children + for key, value in d.items(): + self.set(key, value, as_value=ignore_children) + + return self + + def update_if_missing(self, d): + """Set default values for fields without existing values""" + if isinstance(d, BaseDocument): + d = d.get_valid_dict() + + for key, value in d.items(): + if ( + value is not None + and self.get(key) is None + # dont_update_if_missing is a list of fieldnames + # for which you don't want to set default value + and key not in self.dont_update_if_missing + ): + self.set(key, value) + + def get_db_value(self, key): + return xhiveframework.db.get_value(self.doctype, self.name, key) + + def get(self, key, filters=None, limit=None, default=None): + if isinstance(key, dict): + return _filter(self.get_all_children(), key, limit=limit) + + if filters: + if isinstance(filters, dict): + return _filter(self.__dict__.get(key, []), filters, limit=limit) + + # perhaps you wanted to set a default instead + default = filters + + value = self.__dict__.get(key, default) + + if limit and isinstance(value, list | tuple) and len(value) > limit: + value = value[:limit] + + return value + + def getone(self, key, filters=None): + return self.get(key, filters=filters, limit=1)[0] + + def set(self, key, value, as_value=False): + if key in self._reserved_keywords: + return + + if not as_value and key in self._table_fieldnames: + self.__dict__[key] = [] + + # if value is falsy, just init to an empty list + if value: + self.extend(key, value) + + return + + self.__dict__[key] = value + + def delete_key(self, key): + if key in self.__dict__: + del self.__dict__[key] + + def append(self, key: str, value: D | dict | None = None) -> D: + """Append an item to a child table. + + Example: + doc.append("childtable", { + "child_table_field": "value", + "child_table_int_field": 0, + ... + }) + """ + if value is None: + value = {} + + if (table := self.__dict__.get(key)) is None: + self.__dict__[key] = table = [] + + ret_value = self._init_child(value, key) + table.append(ret_value) + + # reference parent document but with weak reference, parent_doc will be deleted if self is garbage collected. + ret_value.parent_doc = weakref.ref(self) + + return ret_value + + @property + def parent_doc(self): + parent_doc_ref = getattr(self, "_parent_doc", None) + + if isinstance(parent_doc_ref, BaseDocument): + return parent_doc_ref + elif isinstance(parent_doc_ref, weakref.ReferenceType): + return parent_doc_ref() + + @parent_doc.setter + def parent_doc(self, value): + self._parent_doc = value + + @parent_doc.deleter + def parent_doc(self): + self._parent_doc = None + + def extend(self, key, value): + try: + value = iter(value) + except TypeError: + raise ValueError + + for v in value: + self.append(key, v) + + def remove(self, doc): + # Usage: from the parent doc, pass the child table doc + # to remove that child doc from the child table, thus removing it from the parent doc + if doc.get("parentfield"): + self.get(doc.parentfield).remove(doc) + + def _init_child(self, value, key): + if not isinstance(value, BaseDocument): + if not (doctype := self.get_table_field_doctype(key)): + raise AttributeError(key) + + value["doctype"] = doctype + value = get_controller(doctype)(value) + + value.parent = self.name + value.parenttype = self.doctype + value.parentfield = key + + if value.docstatus is None: + value.docstatus = DocStatus.draft() + + if not getattr(value, "idx", None): + if table := getattr(self, key, None): + value.idx = len(table) + 1 + else: + value.idx = 1 + + if not getattr(value, "name", None): + value.__dict__["__islocal"] = 1 + + return value + + def _get_table_fields(self): + """ + To get table fields during Document init + Meta.get_table_fields goes into recursion for special doctypes + """ + + if self.doctype == "DocType": + return DOCTYPE_TABLE_FIELDS + + # child tables don't have child tables + if self.doctype in DOCTYPES_FOR_DOCTYPE: + return () + + return self.meta.get_table_fields() + + def get_valid_dict( + self, sanitize=True, convert_dates_to_str=False, ignore_nulls=False, ignore_virtual=False + ) -> _dict: + d = _dict() + field_values = self.__dict__ + + for fieldname in self.meta.get_valid_columns(): + value = field_values.get(fieldname) + + # if no need for sanitization and value is None, continue + if not sanitize and value is None: + d[fieldname] = None + continue + + df = self.meta.get_field(fieldname) + is_virtual_field = getattr(df, "is_virtual", False) + + if df: + if is_virtual_field: + if ignore_virtual or fieldname not in self.permitted_fieldnames: + continue + + if (prop := getattr(type(self), fieldname, None)) and is_a_property(prop): + value = getattr(self, fieldname) + + elif options := getattr(df, "options", None): + from xhiveframework.utils.safe_exec import get_safe_globals + + value = xhiveframework.safe_eval( + code=options, + eval_globals=get_safe_globals(), + eval_locals={"doc": self}, + ) + + if isinstance(value, list) and df.fieldtype not in table_fields: + xhiveframework.throw(_("Value for {0} cannot be a list").format(_(df.label, context=df.parent))) + + if df.fieldtype == "Check": + value = 1 if cint(value) else 0 + + elif df.fieldtype == "Int" and not isinstance(value, int): + value = cint(value) + + elif df.fieldtype == "JSON" and isinstance(value, dict): + value = json.dumps(value, separators=(",", ":")) + + elif df.fieldtype in float_like_fields and not isinstance(value, float): + value = flt(value) + + elif (df.fieldtype in datetime_fields and value == "") or ( + getattr(df, "unique", False) and cstr(value).strip() == "" + ): + value = None + + if convert_dates_to_str and isinstance( + value, datetime.datetime | datetime.date | datetime.time | datetime.timedelta + ): + value = str(value) + + if ignore_nulls and not is_virtual_field and value is None: + continue + + d[fieldname] = value + + return d + + def init_child_tables(self): + """ + This is needed so that one can loop over child table properties + without worrying about whether or not they have values + """ + + for fieldname in self._table_fieldnames: + if self.__dict__.get(fieldname) is None: + self.__dict__[fieldname] = [] + + def init_valid_columns(self): + for key in default_fields: + if key not in self.__dict__: + self.__dict__[key] = None + + if self.__dict__[key] is None: + if key == "docstatus": + self.docstatus = DocStatus.draft() + elif key == "idx": + self.__dict__[key] = 0 + + for key in self.get_valid_columns(): + if key not in self.__dict__: + self.__dict__[key] = None + + def get_valid_columns(self) -> list[str]: + if self.doctype not in xhiveframework.local.valid_columns: + if self.doctype in DOCTYPES_FOR_DOCTYPE: + from xhiveframework.model.meta import get_table_columns + + valid = get_table_columns(self.doctype) + else: + valid = self.meta.get_valid_columns() + + xhiveframework.local.valid_columns[self.doctype] = valid + + return xhiveframework.local.valid_columns[self.doctype] + + def is_new(self) -> bool: + return self.get("__islocal") + + @property + def docstatus(self): + return DocStatus(cint(self.get("docstatus"))) + + @docstatus.setter + def docstatus(self, value): + self.__dict__["docstatus"] = DocStatus(cint(value)) + + def as_dict( + self, + no_nulls=False, + no_default_fields=False, + convert_dates_to_str=False, + no_child_table_fields=False, + ) -> dict: + doc = self.get_valid_dict(convert_dates_to_str=convert_dates_to_str, ignore_nulls=no_nulls) + doc["doctype"] = self.doctype + + for fieldname in self._table_fieldnames: + children = self.get(fieldname) or [] + doc[fieldname] = [ + d.as_dict( + convert_dates_to_str=convert_dates_to_str, + no_nulls=no_nulls, + no_default_fields=no_default_fields, + no_child_table_fields=no_child_table_fields, + ) + for d in children + ] + + if no_default_fields: + for key in default_fields: + if key in doc: + del doc[key] + + if no_child_table_fields: + for key in child_table_fields: + if key in doc: + del doc[key] + + for key in ( + "_user_tags", + "__islocal", + "__onload", + "_liked_by", + "__run_link_triggers", + "__unsaved", + ): + if value := getattr(self, key, None): + doc[key] = value + + return doc + + def as_json(self): + return xhiveframework.as_json(self.as_dict()) + + def get_table_field_doctype(self, fieldname): + try: + return self.meta.get_field(fieldname).options + except AttributeError: + if self.doctype == "DocType" and (table_doctype := TABLE_DOCTYPES_FOR_DOCTYPE.get(fieldname)): + return table_doctype + + raise + + def get_parentfield_of_doctype(self, doctype): + fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options == doctype] + return fieldname[0] if fieldname else None + + def db_insert(self, ignore_if_duplicate=False): + """INSERT the document (with valid columns) in the database. + + args: + ignore_if_duplicate: ignore primary key collision + at database level (postgres) + in python (mariadb) + """ + if not self.name: + # name will be set by document class in most cases + set_new_name(self) + + conflict_handler = "" + # On postgres we can't implcitly ignore PK collision + # So instruct pg to ignore `name` field conflicts + if ignore_if_duplicate and xhiveframework.db.db_type == "postgres": + conflict_handler = "on conflict (name) do nothing" + + if not self.creation: + self.creation = self.modified = now() + self.owner = self.modified_by = xhiveframework.session.user + + # if doctype is "DocType", don't insert null values as we don't know who is valid yet + d = self.get_valid_dict( + convert_dates_to_str=True, + ignore_nulls=self.doctype in DOCTYPES_FOR_DOCTYPE, + ignore_virtual=True, + ) + + columns = list(d) + try: + xhiveframework.db.sql( + """INSERT INTO `tab{doctype}` ({columns}) + VALUES ({values}) {conflict_handler}""".format( + doctype=self.doctype, + columns=", ".join("`" + c + "`" for c in columns), + values=", ".join(["%s"] * len(columns)), + conflict_handler=conflict_handler, + ), + list(d.values()), + ) + except Exception as e: + if xhiveframework.db.is_primary_key_violation(e): + if self.meta.autoname == "hash": + # hash collision? try again + xhiveframework.flags.retry_count = (xhiveframework.flags.retry_count or 0) + 1 + if xhiveframework.flags.retry_count > 5 and not xhiveframework.flags.in_test: + raise + self.name = None + self.db_insert() + return + + if not ignore_if_duplicate: + xhiveframework.msgprint( + _("{0} {1} already exists").format(_(self.doctype), xhiveframework.bold(self.name)), + title=_("Duplicate Name"), + indicator="red", + ) + raise xhiveframework.DuplicateEntryError(self.doctype, self.name, e) + + elif xhiveframework.db.is_unique_key_violation(e): + # unique constraint + self.show_unique_validation_message(e) + + else: + raise + + self.set("__islocal", False) + + def db_update(self): + if self.get("__islocal") or not self.name: + self.db_insert() + return + + d = self.get_valid_dict( + convert_dates_to_str=True, + ignore_nulls=self.doctype in DOCTYPES_FOR_DOCTYPE, + ignore_virtual=True, + ) + + # don't update name, as case might've been changed + name = cstr(d["name"]) + del d["name"] + + columns = list(d) + + try: + xhiveframework.db.sql( + """UPDATE `tab{doctype}` + SET {values} WHERE `name`=%s""".format( + doctype=self.doctype, values=", ".join("`" + c + "`=%s" for c in columns) + ), + [*list(d.values()), name], + ) + except Exception as e: + if xhiveframework.db.is_unique_key_violation(e): + self.show_unique_validation_message(e) + else: + raise + + def db_update_all(self): + """Raw update parent + children + DOES NOT VALIDATE AND CALL TRIGGERS""" + self.db_update() + for fieldname in self._table_fieldnames: + for doc in self.get(fieldname): + doc.db_update() + + def show_unique_validation_message(self, e): + if xhiveframework.db.db_type != "postgres": + fieldname = str(e).split("'")[-2] + label = None + + # MariaDB gives key_name in error. Extracting fieldname from key name + try: + fieldname = self.get_field_name_by_key_name(fieldname) + except IndexError: + pass + + label = self.get_label_from_fieldname(fieldname) + + xhiveframework.msgprint(_("{0} must be unique").format(label or fieldname)) + + # this is used to preserve traceback + raise xhiveframework.UniqueValidationError(self.doctype, self.name, e) + + def get_field_name_by_key_name(self, key_name): + """MariaDB stores a mapping between `key_name` and `column_name`. + This function returns the `column_name` associated with the `key_name` passed + + Args: + key_name (str): The name of the database index. + + Raises: + IndexError: If the key is not found in the table. + + Returns: + str: The column name associated with the key. + """ + return xhiveframework.db.sql( + f""" + SHOW + INDEX + FROM + `tab{self.doctype}` + WHERE + key_name=%s + AND + Non_unique=0 + """, + key_name, + as_dict=True, + )[0].get("Column_name") + + def get_label_from_fieldname(self, fieldname): + """Returns the associated label for fieldname + + Args: + fieldname (str): The fieldname in the DocType to use to pull the label. + + Returns: + str: The label associated with the fieldname, if found, otherwise `None`. + """ + df = self.meta.get_field(fieldname) + if df: + return df.label + + def update_modified(self): + """Update modified timestamp""" + self.set("modified", now()) + if getattr(self.meta, "issingle", False): + xhiveframework.db.set_single_value(self.doctype, "modified", self.modified, update_modified=False) + else: + xhiveframework.db.set_value(self.doctype, self.name, "modified", self.modified, update_modified=False) + + def _fix_numeric_types(self): + for df in self.meta.get("fields"): + if df.fieldtype == "Check": + self.set(df.fieldname, cint(self.get(df.fieldname))) + + elif self.get(df.fieldname) is not None: + if df.fieldtype == "Int": + self.set(df.fieldname, cint(self.get(df.fieldname))) + + elif df.fieldtype in ("Float", "Currency", "Percent"): + self.set(df.fieldname, flt(self.get(df.fieldname))) + + if self.docstatus is not None: + self.docstatus = DocStatus(cint(self.docstatus)) + + def _get_missing_mandatory_fields(self): + """Get mandatory fields that do not have any values""" + + def get_msg(df): + if df.fieldtype in table_fields: + return "{}: {}: {}".format( + _("Error"), _("Data missing in table"), _(df.label, context=df.parent) + ) + + # check if parentfield exists (only applicable for child table doctype) + elif self.get("parentfield"): + return "{}: {} {} #{}: {}: {}".format( + _("Error"), + xhiveframework.bold(_(self.doctype)), + _("Row"), + self.idx, + _("Value missing for"), + _(df.label, context=df.parent), + ) + + return _("Error: Value missing for {0}: {1}").format(_(df.parent), _(df.label, context=df.parent)) + + def has_content(df): + value = cstr(self.get(df.fieldname)) + has_text_content = strip_html(value).strip() + has_img_tag = " max_length: + self.throw_length_exceeded_error(df, max_length, value) + + elif column_type in ("int", "bigint", "smallint"): + max_length = max_positive_value[column_type] + + if abs(cint(value)) > max_length: + self.throw_length_exceeded_error(df, max_length, value) + + def _validate_code_fields(self): + for field in self.meta.get_code_fields(): + code_string = self.get(field.fieldname) + language = field.get("options") + + if language == "Python": + xhiveframework.utils.validate_python_code(code_string, fieldname=field.label, is_expression=False) + + elif language == "PythonExpression": + xhiveframework.utils.validate_python_code(code_string, fieldname=field.label) + + def _sync_autoname_field(self): + """Keep autoname field in sync with `name`""" + autoname = self.meta.autoname or "" + _empty, _field_specifier, fieldname = autoname.partition("field:") + + if fieldname and self.name and self.name != self.get(fieldname): + self.set(fieldname, self.name) + + def throw_length_exceeded_error(self, df, max_length, value): + # check if parentfield exists (only applicable for child table doctype) + if self.get("parentfield"): + reference = _("{0}, Row {1}").format(_(self.doctype), self.idx) + else: + reference = f"{_(self.doctype)} {self.name}" + + xhiveframework.throw( + _("{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}").format( + reference, _(df.label, context=df.parent), max_length, value + ), + xhiveframework.CharacterLengthExceededError, + title=_("Value too big"), + ) + + def _validate_update_after_submit(self): + # get the full doc with children + db_values = xhiveframework.get_doc(self.doctype, self.name).as_dict() + + for key in self.as_dict(): + df = self.meta.get_field(key) + db_value = db_values.get(key) + + if df and not df.allow_on_submit and (self.get(key) or db_value): + if df.fieldtype in table_fields: + # just check if the table size has changed + # individual fields will be checked in the loop for children + self_value = len(self.get(key)) + db_value = len(db_value) + + else: + self_value = self.get_value(key) + # Postgres stores values as `datetime.time`, MariaDB as `timedelta` + if isinstance(self_value, datetime.timedelta) and isinstance(db_value, datetime.time): + db_value = datetime.timedelta( + hours=db_value.hour, + minutes=db_value.minute, + seconds=db_value.second, + microseconds=db_value.microsecond, + ) + if self_value != db_value: + xhiveframework.throw( + _("{0} Not allowed to change {1} after submission from {2} to {3}").format( + f"Row #{self.idx}:" if self.get("parent") else "", + xhiveframework.bold(_(df.label, context=df.parent)), + xhiveframework.bold(db_value), + xhiveframework.bold(self_value), + ), + xhiveframework.UpdateAfterSubmitError, + title=_("Cannot Update After Submit"), + ) + + def _sanitize_content(self): + """Sanitize HTML and Email in field values. Used to prevent XSS. + + - Ignore if 'Ignore XSS Filter' is checked or fieldtype is 'Code' + """ + from bs4 import BeautifulSoup + + if xhiveframework.flags.in_install: + return + + for fieldname, value in self.get_valid_dict(ignore_virtual=True).items(): + if not value or not isinstance(value, str): + continue + + value = xhiveframework.as_unicode(value) + + if "<" not in value and ">" not in value: + # doesn't look like html so no need + continue + + elif "" in value and not bool(BeautifulSoup(value, "html.parser").find()): + # should be handled separately via the markdown converter function + continue + + df = self.meta.get_field(fieldname) + sanitized_value = value + + if df and ( + df.get("ignore_xss_filter") + or (df.get("fieldtype") in ("Data", "Small Text", "Text") and df.get("options") == "Email") + or df.get("fieldtype") in ("Attach", "Attach Image", "Barcode", "Code") + # cancelled and submit but not update after submit should be ignored + or self.docstatus.is_cancelled() + or (self.docstatus.is_submitted() and not df.get("allow_on_submit")) + ): + continue + + else: + sanitized_value = sanitize_html(value, linkify=df and df.fieldtype == "Text Editor") + + self.set(fieldname, sanitized_value) + + def _save_passwords(self): + """Save password field values in __Auth table""" + from xhiveframework.utils.password import remove_encrypted_password, set_encrypted_password + + if self.flags.ignore_save_passwords is True: + return + + for df in self.meta.get("fields", {"fieldtype": ("=", "Password")}): + if self.flags.ignore_save_passwords and df.fieldname in self.flags.ignore_save_passwords: + continue + new_password = self.get(df.fieldname) + + if not new_password: + remove_encrypted_password(self.doctype, self.name, df.fieldname) + + if new_password and not self.is_dummy_password(new_password): + # is not a dummy password like '*****' + set_encrypted_password(self.doctype, self.name, new_password, df.fieldname) + + # set dummy password like '*****' + self.set(df.fieldname, "*" * len(new_password)) + + def get_password(self, fieldname="password", raise_exception=True): + from xhiveframework.utils.password import get_decrypted_password + + if self.get(fieldname) and not self.is_dummy_password(self.get(fieldname)): + return self.get(fieldname) + + return get_decrypted_password(self.doctype, self.name, fieldname, raise_exception=raise_exception) + + def is_dummy_password(self, pwd): + return "".join(set(pwd)) == "*" + + def precision(self, fieldname, parentfield=None) -> int | None: + """Returns float precision for a particular field (or get global default). + + :param fieldname: Fieldname for which precision is required. + :param parentfield: If fieldname is in child table.""" + from xhiveframework.model.meta import get_field_precision + + if parentfield and not isinstance(parentfield, str) and parentfield.get("parentfield"): + parentfield = parentfield.parentfield + + cache_key = parentfield or "main" + + if not hasattr(self, "_precision"): + self._precision = _dict() + + if cache_key not in self._precision: + self._precision[cache_key] = _dict() + + if fieldname not in self._precision[cache_key]: + self._precision[cache_key][fieldname] = None + + doctype = self.meta.get_field(parentfield).options if parentfield else self.doctype + df = xhiveframework.get_meta(doctype).get_field(fieldname) + + if df.fieldtype in ("Currency", "Float", "Percent"): + self._precision[cache_key][fieldname] = get_field_precision(df, self) + + return self._precision[cache_key][fieldname] + + def get_formatted( + self, fieldname, doc=None, currency=None, absolute_value=False, translated=False, format=None + ): + from xhiveframework.utils.formatters import format_value + + df = self.meta.get_field(fieldname) + if not df: + from xhiveframework.model.meta import get_default_df + + df = get_default_df(fieldname) + + if ( + df + and df.fieldtype == "Currency" + and not currency + and (currency_field := df.get("options")) + and (currency_value := self.get(currency_field)) + ): + currency = xhiveframework.db.get_value("Currency", currency_value, cache=True) + + val = self.get(fieldname) + + if translated: + val = _(val) + + if not doc: + doc = getattr(self, "parent_doc", None) or self + + if (absolute_value or doc.get("absolute_value")) and isinstance(val, int | float): + val = abs(self.get(fieldname)) + + return format_value(val, df=df, doc=doc, currency=currency, format=format) + + def is_print_hide(self, fieldname, df=None, for_print=True): + """Returns true if fieldname is to be hidden for print. + + Print Hide can be set via the Print Format Builder or in the controller as a list + of hidden fields. Example + + class MyDoc(Document): + def __setup__(self): + self.print_hide = ["field1", "field2"] + + :param fieldname: Fieldname to be checked if hidden. + """ + meta_df = self.meta.get_field(fieldname) + if meta_df and meta_df.get("__print_hide"): + return True + + print_hide = 0 + + if self.get(fieldname) == 0 and not self.meta.istable: + print_hide = (df and df.print_hide_if_no_value) or (meta_df and meta_df.print_hide_if_no_value) + + if not print_hide: + if df and df.print_hide is not None: + print_hide = df.print_hide + elif meta_df: + print_hide = meta_df.print_hide + + return print_hide + + def in_format_data(self, fieldname): + """Returns True if shown via Print Format::`format_data` property. + Called from within standard print format.""" + doc = getattr(self, "parent_doc", self) + + if hasattr(doc, "format_data_map"): + return fieldname in doc.format_data_map + else: + return True + + def reset_values_if_no_permlevel_access(self, has_access_to, high_permlevel_fields): + """If the user does not have permissions at permlevel > 0, then reset the values to original / default""" + to_reset = [ + df + for df in high_permlevel_fields + if ( + df.permlevel not in has_access_to + and df.fieldtype not in display_fieldtypes + and df.fieldname not in self.flags.get("ignore_permlevel_for_fields", []) + ) + ] + + if to_reset: + if self.is_new(): + # if new, set default value + ref_doc = xhiveframework.new_doc(self.doctype) + else: + # get values from old doc + if self.parent_doc: + parent_doc = self.parent_doc.get_latest() + child_docs = [d for d in parent_doc.get(self.parentfield) if d.name == self.name] + if not child_docs: + return + ref_doc = child_docs[0] + else: + ref_doc = self.get_latest() + + for df in to_reset: + self.set(df.fieldname, ref_doc.get(df.fieldname)) + + def get_value(self, fieldname): + df = self.meta.get_field(fieldname) + val = self.get(fieldname) + + return self.cast(val, df) + + def cast(self, value, df): + return cast_fieldtype(df.fieldtype, value, show_warning=False) + + def _extract_images_from_text_editor(self): + from xhiveframework.core.doctype.file.utils import extract_images_from_doc + + if self.doctype != "DocType": + for df in self.meta.get("fields", {"fieldtype": ("=", "Text Editor")}): + extract_images_from_doc(self, df.fieldname) + + +def _filter(data, filters, limit=None): + """pass filters as: + {"key": "val", "key": ["!=", "val"], + "key": ["in", "val"], "key": ["not in", "val"], "key": "^val", + "key" : True (exists), "key": False (does not exist) }""" + + out, _filters = [], {} + + if not data: + return out + + # setup filters as tuples + if filters: + for f in filters: + fval = filters[f] + + if not isinstance(fval, tuple | list): + if fval is True: + fval = ("not None", fval) + elif fval is False: + fval = ("None", fval) + elif isinstance(fval, str) and fval.startswith("^"): + fval = ("^", fval[1:]) + else: + fval = ("=", fval) + + _filters[f] = fval + + for d in data: + for f, fval in _filters.items(): + if not compare(getattr(d, f, None), fval[0], fval[1]): + break + else: + out.append(d) + if limit and len(out) >= limit: + break + + return out diff --git a/xhiveframework/model/create_new.py b/xhiveframework/model/create_new.py new file mode 100644 index 0000000..5134770 --- /dev/null +++ b/xhiveframework/model/create_new.py @@ -0,0 +1,182 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +""" +Create a new document with defaults set +""" + +import copy + +import xhiveframework +import xhiveframework.defaults +from xhiveframework.core.doctype.user_permission.user_permission import get_user_permissions +from xhiveframework.model import data_fieldtypes +from xhiveframework.permissions import filter_allowed_docs_for_doctype +from xhiveframework.utils import cstr, now_datetime, nowdate, nowtime + + +def get_new_doc(doctype, parent_doc=None, parentfield=None, as_dict=False): + if doctype not in xhiveframework.local.new_doc_templates: + # cache a copy of new doc as it is called + # frequently for inserts + xhiveframework.local.new_doc_templates[doctype] = make_new_doc(doctype) + + doc = copy.deepcopy(xhiveframework.local.new_doc_templates[doctype]) + + set_dynamic_default_values(doc, parent_doc, parentfield) + + if as_dict: + return doc + else: + return xhiveframework.get_doc(doc) + + +def make_new_doc(doctype): + doc = xhiveframework.get_doc({"doctype": doctype, "__islocal": 1, "owner": xhiveframework.session.user, "docstatus": 0}) + + set_user_and_static_default_values(doc) + + doc._fix_numeric_types() + doc = doc.get_valid_dict(sanitize=False) + doc["doctype"] = doctype + doc["__islocal"] = 1 + + if not xhiveframework.model.meta.is_single(doctype): + doc["__unsaved"] = 1 + + return doc + + +def set_user_and_static_default_values(doc): + user_permissions = get_user_permissions() + defaults = xhiveframework.defaults.get_defaults() + + for df in doc.meta.get("fields"): + if df.fieldtype in data_fieldtypes: + # user permissions for link options + doctype_user_permissions = user_permissions.get(df.options, []) + # Allowed records for the reference doctype (link field) along with default doc + allowed_records, default_doc = filter_allowed_docs_for_doctype( + doctype_user_permissions, df.parent, with_default_doc=True + ) + + user_default_value = get_user_default_value( + df, defaults, doctype_user_permissions, allowed_records, default_doc + ) + if user_default_value is not None: + # if fieldtype is link check if doc exists + if not df.fieldtype == "Link" or xhiveframework.db.exists(df.options, user_default_value): + doc.set(df.fieldname, user_default_value) + + else: + if df.fieldname != doc.meta.title_field: + static_default_value = get_static_default_value( + df, doctype_user_permissions, allowed_records + ) + if static_default_value is not None: + doc.set(df.fieldname, static_default_value) + + +def get_user_default_value(df, defaults, doctype_user_permissions, allowed_records, default_doc): + # don't set defaults for "User" link field using User Permissions! + if df.fieldtype == "Link" and df.options != "User": + # If user permission has Is Default enabled or single-user permission has found against respective doctype. + if not df.ignore_user_permissions and default_doc: + return default_doc + + # 2 - Look in user defaults + user_default = defaults.get(df.fieldname) + + allowed_by_user_permission = validate_value_via_user_permissions( + df, doctype_user_permissions, allowed_records, user_default=user_default + ) + + # is this user default also allowed as per user permissions? + if user_default and allowed_by_user_permission: + return user_default + + +def get_static_default_value(df, doctype_user_permissions, allowed_records): + # 3 - look in default of docfield + if df.get("default"): + if df.default == "__user": + return xhiveframework.session.user + + elif df.default == "Today": + return nowdate() + + elif not cstr(df.default).startswith(":"): + # a simple default value + is_allowed_default_value = validate_value_via_user_permissions( + df, doctype_user_permissions, allowed_records + ) + + if df.fieldtype != "Link" or df.options == "User" or is_allowed_default_value: + return df.default + + elif df.fieldtype == "Select" and df.options and df.options not in ("[Select]", "Loading..."): + return df.options.split("\n", 1)[0] + + +def validate_value_via_user_permissions(df, doctype_user_permissions, allowed_records, user_default=None): + is_valid = True + # If User Permission exists and allowed records is empty, + # that means there are User Perms, but none applicable to this new doctype. + + if user_permissions_exist(df, doctype_user_permissions) and allowed_records: + # If allowed records is not empty, + # check if this field value is allowed via User Permissions applied to this doctype. + value = user_default if user_default else df.default + is_valid = value in allowed_records + + return is_valid + + +def set_dynamic_default_values(doc, parent_doc, parentfield): + # these values should not be cached + user_permissions = get_user_permissions() + + for df in xhiveframework.get_meta(doc["doctype"]).get("fields"): + if df.get("default"): + if cstr(df.default).startswith(":"): + default_value = get_default_based_on_another_field(df, user_permissions, parent_doc) + if default_value is not None and not doc.get(df.fieldname): + doc[df.fieldname] = default_value + + elif df.fieldtype == "Datetime" and df.default.lower() == "now": + doc[df.fieldname] = now_datetime() + + if df.fieldtype == "Time": + doc[df.fieldname] = nowtime() + + if parent_doc: + doc["parent"] = parent_doc.name + doc["parenttype"] = parent_doc.doctype + + if parentfield: + doc["parentfield"] = parentfield + + +def user_permissions_exist(df, doctype_user_permissions): + return ( + df.fieldtype == "Link" + and not getattr(df, "ignore_user_permissions", False) + and doctype_user_permissions + ) + + +def get_default_based_on_another_field(df, user_permissions, parent_doc): + # default value based on another document + from xhiveframework.permissions import get_allowed_docs_for_doctype + + ref_doctype = df.default[1:] + ref_fieldname = ref_doctype.lower().replace(" ", "_") + reference_name = parent_doc.get(ref_fieldname) if parent_doc else xhiveframework.db.get_default(ref_fieldname) + default_value = xhiveframework.db.get_value(ref_doctype, reference_name, df.fieldname) + is_allowed_default_value = not user_permissions_exist(df, user_permissions.get(df.options)) or ( + default_value in get_allowed_docs_for_doctype(user_permissions[df.options], df.parent) + ) + + # is this allowed as per user permissions + if is_allowed_default_value: + return default_value diff --git a/xhiveframework/model/db_query.py b/xhiveframework/model/db_query.py new file mode 100644 index 0000000..5e32a9a --- /dev/null +++ b/xhiveframework/model/db_query.py @@ -0,0 +1,1380 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +"""build query for doclistview and return results""" + +import copy +import datetime +import json +import re +from collections import Counter + +import xhiveframework +import xhiveframework.defaults +import xhiveframework.permissions +import xhiveframework.share +from xhiveframework import _ +from xhiveframework.core.doctype.server_script.server_script_utils import get_server_script_map +from xhiveframework.database.utils import DefaultOrderBy, FallBackDateTimeStr, NestedSetHierarchy +from xhiveframework.model import get_permitted_fields, optional_fields +from xhiveframework.model.meta import get_table_columns +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.model.utils.user_settings import get_user_settings, update_user_settings +from xhiveframework.query_builder.utils import Column +from xhiveframework.utils import ( + cint, + cstr, + flt, + get_filter, + get_time, + get_timespan_date_range, + make_filter_tuple, +) +from xhiveframework.utils.data import DateTimeLikeObject, get_datetime, getdate, sbool + +LOCATE_PATTERN = re.compile(r"locate\([^,]+,\s*[`\"]?name[`\"]?\s*\)", flags=re.IGNORECASE) +LOCATE_CAST_PATTERN = re.compile(r"locate\(([^,]+),\s*([`\"]?name[`\"]?)\s*\)", flags=re.IGNORECASE) +FUNC_IFNULL_PATTERN = re.compile(r"(strpos|ifnull|coalesce)\(\s*[`\"]?name[`\"]?\s*,", flags=re.IGNORECASE) +CAST_VARCHAR_PATTERN = re.compile(r"([`\"]?tab[\w`\" -]+\.[`\"]?name[`\"]?)(?!\w)", flags=re.IGNORECASE) +ORDER_BY_PATTERN = re.compile(r"\ order\ by\ |\ asc|\ ASC|\ desc|\ DESC", flags=re.IGNORECASE) +SUB_QUERY_PATTERN = re.compile("^.*[,();@].*") +IS_QUERY_PATTERN = re.compile(r"^(select|delete|update|drop|create)\s") +IS_QUERY_PREDICATE_PATTERN = re.compile(r"\s*[0-9a-zA-z]*\s*( from | group by | order by | where | join )") +FIELD_QUOTE_PATTERN = re.compile(r"[0-9a-zA-Z]+\s*'") +FIELD_COMMA_PATTERN = re.compile(r"[0-9a-zA-Z]+\s*,") +STRICT_FIELD_PATTERN = re.compile(r".*/\*.*") +STRICT_UNION_PATTERN = re.compile(r".*\s(union).*\s") +ORDER_GROUP_PATTERN = re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*") +FN_PARAMS_PATTERN = re.compile(r".*?\((.*)\).*") +SPECIAL_FIELD_CHARS = frozenset(("(", "`", ".", "'", '"', "*")) + + +class DatabaseQuery: + def __init__(self, doctype, user=None): + self.doctype = doctype + self.tables = [] + self.link_tables = [] + self.linked_table_aliases = {} + self.linked_table_counter = Counter() + self.conditions = [] + self.or_conditions = [] + self.fields = None + self.user = user or xhiveframework.session.user + self.ignore_ifnull = False + self.flags = xhiveframework._dict() + self.reference_doctype = None + self.permission_map = {} + self.shared = [] + self._fetch_shared_documents = False + + @property + def doctype_meta(self): + if not hasattr(self, "_doctype_meta"): + self._doctype_meta = xhiveframework.get_meta(self.doctype) + return self._doctype_meta + + @property + def query_tables(self): + return self.tables + [d.table_alias for d in self.link_tables] + + def execute( + self, + fields=None, + filters=None, + or_filters=None, + docstatus=None, + group_by=None, + order_by=DefaultOrderBy, + limit_start=False, + limit_page_length=None, + as_list=False, + with_childnames=False, + debug=False, + ignore_permissions=False, + user=None, + with_comment_count=False, + join="left join", + distinct=False, + start=None, + page_length=None, + limit=None, + ignore_ifnull=False, + save_user_settings=False, + save_user_settings_fields=False, + update=None, + add_total_row=None, + user_settings=None, + reference_doctype=None, + run=True, + strict=True, + pluck=None, + ignore_ddl=False, + *, + parent_doctype=None, + ) -> list: + if not ignore_permissions: + self.check_read_permission(self.doctype, parent_doctype=parent_doctype) + + # filters and fields swappable + # its hard to remember what comes first + if isinstance(fields, dict) or (fields and isinstance(fields, list) and isinstance(fields[0], list)): + # if fields is given as dict/list of list, its probably filters + filters, fields = fields, filters + + elif fields and isinstance(filters, list) and len(filters) > 1 and isinstance(filters[0], str): + # if `filters` is a list of strings, its probably fields + filters, fields = fields, filters + + if fields: + self.fields = fields + else: + self.fields = [f"`tab{self.doctype}`.`{pluck or 'name'}`"] + + if start: + limit_start = start + if page_length: + limit_page_length = page_length + if limit: + limit_page_length = limit + + self.filters = filters or [] + self.or_filters = or_filters or [] + self.docstatus = docstatus or [] + self.group_by = group_by + self.order_by = order_by + self.limit_start = cint(limit_start) + self.limit_page_length = cint(limit_page_length) if limit_page_length else None + self.with_childnames = with_childnames + self.debug = debug + self.join = join + self.distinct = distinct + self.as_list = as_list + self.ignore_ifnull = ignore_ifnull + self.flags.ignore_permissions = ignore_permissions + self.user = user or xhiveframework.session.user + self.update = update + self.user_settings_fields = copy.deepcopy(self.fields) + self.run = run + self.strict = strict + self.ignore_ddl = ignore_ddl + self.parent_doctype = parent_doctype + + # for contextual user permission check + # to determine which user permission is applicable on link field of specific doctype + self.reference_doctype = reference_doctype or self.doctype + + if user_settings: + self.user_settings = json.loads(user_settings) + + if is_virtual_doctype(self.doctype): + from xhiveframework.model.base_document import get_controller + + controller = get_controller(self.doctype) + if not hasattr(controller, "get_list"): + return [] + + self.parse_args() + kwargs = { + "as_list": as_list, + "with_comment_count": with_comment_count, + "save_user_settings": save_user_settings, + "save_user_settings_fields": save_user_settings_fields, + "pluck": pluck, + "parent_doctype": parent_doctype, + } | self.__dict__ + return controller.get_list(kwargs) + + self.columns = self.get_table_columns() + + # no table & ignore_ddl, return + if not self.columns: + return [] + + result = self.build_and_run() + + if sbool(with_comment_count) and not as_list and self.doctype: + self.add_comment_count(result) + + if save_user_settings: + self.save_user_settings_fields = save_user_settings_fields + self.update_user_settings() + + if pluck: + return [d[pluck] for d in result] + + return result + + def build_and_run(self): + args = self.prepare_args() + args.limit = self.add_limit() + + if not args.fields: + # apply_fieldlevel_read_permissions has likely removed ALL the fields that user asked for + return [] + + if args.conditions: + args.conditions = "where " + args.conditions + + if self.distinct: + args.fields = "distinct " + args.fields + args.order_by = "" # TODO: recheck for alternative + + # Postgres requires any field that appears in the select clause to also + # appear in the order by and group by clause + if xhiveframework.db.db_type == "postgres" and args.order_by and args.group_by: + args = self.prepare_select_args(args) + + query = """select {fields} + from {tables} + {conditions} + {group_by} + {order_by} + {limit}""".format(**args) + + return xhiveframework.db.sql( + query, + as_dict=not self.as_list, + debug=self.debug, + update=self.update, + ignore_ddl=self.ignore_ddl, + run=self.run, + ) + + def prepare_args(self): + self.parse_args() + self.sanitize_fields() + self.extract_tables() + self.set_optional_columns() + self.build_conditions() + self.apply_fieldlevel_read_permissions() + + args = xhiveframework._dict() + + if self.with_childnames: + for t in self.tables: + if t != f"`tab{self.doctype}`": + self.fields.append(f"{t}.name as '{t[4:-1]}:name'") + + # query dict + args.tables = self.tables[0] + + # left join parent, child tables + for child in self.tables[1:]: + parent_name = cast_name(f"{self.tables[0]}.name") + args.tables += f" {self.join} {child} on ({child}.parenttype = {xhiveframework.db.escape(self.doctype)} and {child}.parent = {parent_name})" + + # left join link tables + for link in self.link_tables: + args.tables += f" {self.join} {link.table_name} {link.table_alias} on ({link.table_alias}.`name` = {self.tables[0]}.`{link.fieldname}`)" + + if self.grouped_or_conditions: + self.conditions.append(f"({' or '.join(self.grouped_or_conditions)})") + + args.conditions = " and ".join(self.conditions) + + if self.or_conditions: + args.conditions += (" or " if args.conditions else "") + " or ".join(self.or_conditions) + + self.set_field_tables() + self.cast_name_fields() + + fields = [] + + # Wrapping fields with grave quotes to allow support for sql keywords + # TODO: Add support for wrapping fields with sql functions and distinct keyword + for field in self.fields: + if field is None: + fields.append("NULL") + continue + + stripped_field = field.strip().lower() + + if ( + stripped_field[0] in {"`", "*", '"', "'"} + or "(" in stripped_field + or "distinct" in stripped_field + ): + fields.append(field) + elif "as" in stripped_field.split(" "): + col, _, new = field.split() + fields.append(f"`{col}` as {new}") + else: + fields.append(f"`{field}`") + + args.fields = ", ".join(fields) + + self.set_order_by(args) + + self.validate_order_by_and_group_by(args.order_by) + args.order_by = args.order_by and (" order by " + args.order_by) or "" + + self.validate_order_by_and_group_by(self.group_by) + args.group_by = self.group_by and (" group by " + self.group_by) or "" + + return args + + def prepare_select_args(self, args): + order_field = ORDER_BY_PATTERN.sub("", args.order_by) + + if order_field not in args.fields: + extracted_column = order_column = order_field.replace("`", "") + if "." in extracted_column: + extracted_column = extracted_column.split(".")[1] + + args.fields += f", MAX({extracted_column}) as `{order_column}`" + args.order_by = args.order_by.replace(order_field, f"`{order_column}`") + + return args + + def parse_args(self): + """Convert fields and filters from strings to list, dicts""" + if isinstance(self.fields, str): + if self.fields == "*": + self.fields = ["*"] + else: + try: + self.fields = json.loads(self.fields) + except ValueError: + self.fields = [f.strip() for f in self.fields.split(",")] + + # remove empty strings / nulls in fields + self.fields = [f for f in self.fields if f] + + # convert child_table.fieldname to `tabChild DocType`.`fieldname` + for field in self.fields: + if "." in field: + original_field = field + alias = None + if " as " in field: + field, alias = field.split(" as ", 1) + linked_fieldname, fieldname = field.split(".", 1) + linked_field = xhiveframework.get_meta(self.doctype).get_field(linked_fieldname) + # this is not a link field + if not linked_field: + continue + linked_doctype = linked_field.options + if linked_field.fieldtype == "Link": + linked_table = self.append_link_table(linked_doctype, linked_fieldname) + field = f"{linked_table.table_alias}.`{fieldname}`" + else: + field = f"`tab{linked_doctype}`.`{fieldname}`" + if alias: + field = f"{field} as {alias}" + self.fields[self.fields.index(original_field)] = field + + for filter_name in ["filters", "or_filters"]: + filters = getattr(self, filter_name) + if isinstance(filters, str): + filters = json.loads(filters) + + if isinstance(filters, dict): + fdict = filters + filters = [make_filter_tuple(self.doctype, key, value) for key, value in fdict.items()] + setattr(self, filter_name, filters) + + def sanitize_fields(self): + """ + regex : ^.*[,();].* + purpose : The regex will look for malicious patterns like `,`, '(', ')', '@', ;' in each + field which may leads to sql injection. + example : + field = "`DocType`.`issingle`, version()" + As field contains `,` and mysql function `version()`, with the help of regex + the system will filter out this field. + """ + blacklisted_keywords = ["select", "create", "insert", "delete", "drop", "update", "case", "show"] + blacklisted_functions = [ + "concat", + "concat_ws", + "if", + "ifnull", + "nullif", + "coalesce", + "connection_id", + "current_user", + "database", + "last_insert_id", + "session_user", + "system_user", + "user", + "version", + "global", + ] + + def _raise_exception(): + xhiveframework.throw(_("Use of sub-query or function is restricted"), xhiveframework.DataError) + + def _is_query(field): + if IS_QUERY_PATTERN.match(field): + _raise_exception() + + elif IS_QUERY_PREDICATE_PATTERN.match(field): + _raise_exception() + + for field in self.fields: + lower_field = field.lower().strip() + + if SUB_QUERY_PATTERN.match(field): + if lower_field[0] == "(": + subquery_token = lower_field[1:].lstrip().split(" ", 1)[0] + if subquery_token in blacklisted_keywords: + _raise_exception() + + function = lower_field.split("(", 1)[0].rstrip() + if function in blacklisted_functions: + xhiveframework.throw( + _("Use of function {0} in field is restricted").format(function), exc=xhiveframework.DataError + ) + + if "@" in lower_field: + # prevent access to global variables + _raise_exception() + + if FIELD_QUOTE_PATTERN.match(field): + _raise_exception() + + if FIELD_COMMA_PATTERN.match(field): + _raise_exception() + + _is_query(field) + + if self.strict: + if STRICT_FIELD_PATTERN.match(field): + xhiveframework.throw(_("Illegal SQL Query")) + + if STRICT_UNION_PATTERN.match(lower_field): + xhiveframework.throw(_("Illegal SQL Query")) + + def extract_tables(self): + """extract tables from fields""" + self.tables = [f"`tab{self.doctype}`"] + sql_functions = [ + "dayofyear(", + "extract(", + "locate(", + "strpos(", + "count(", + "sum(", + "avg(", + ] + # add tables from fields + if self.fields: + for field in self.fields: + if not ("tab" in field and "." in field) or any(x for x in sql_functions if x in field): + continue + + table_name = field.split(".", 1)[0] + + # Check if table_name is a linked_table alias + for linked_table in self.link_tables: + if linked_table.table_alias == table_name: + table_name = linked_table.table_name + break + + if table_name.lower().startswith("group_concat("): + table_name = table_name[13:] + if table_name.lower().startswith("distinct"): + table_name = table_name[8:].strip() + if table_name[0] != "`": + table_name = f"`{table_name}`" + if ( + table_name not in self.query_tables + and table_name not in self.linked_table_aliases.values() + ): + self.append_table(table_name) + + def append_table(self, table_name): + self.tables.append(table_name) + doctype = table_name[4:-1] + self.check_read_permission(doctype) + + def append_link_table(self, doctype, fieldname): + for linked_table in self.link_tables: + if linked_table.doctype == doctype and linked_table.fieldname == fieldname: + return linked_table + + self.check_read_permission(doctype) + self.linked_table_counter.update((doctype,)) + linked_table = xhiveframework._dict( + doctype=doctype, + fieldname=fieldname, + table_name=f"`tab{doctype}`", + table_alias=f"`tab{doctype}_{self.linked_table_counter[doctype]}`", + ) + self.linked_table_aliases[linked_table.table_alias.replace("`", "")] = linked_table.table_name + self.link_tables.append(linked_table) + return linked_table + + def check_read_permission(self, doctype: str, parent_doctype: str | None = None): + if self.flags.ignore_permissions: + return + + if doctype not in self.permission_map: + self._set_permission_map(doctype, parent_doctype) + + return self.permission_map[doctype] + + def _set_permission_map(self, doctype: str, parent_doctype: str | None = None): + ptype = "select" if xhiveframework.only_has_select_perm(doctype) else "read" + xhiveframework.has_permission( + doctype, + ptype=ptype, + parent_doctype=parent_doctype or self.doctype, + throw=True, + user=self.user, + ) + self.permission_map[doctype] = ptype + + def set_field_tables(self): + """If there are more than one table, the fieldname must not be ambiguous. + If the fieldname is not explicitly mentioned, set the default table""" + + def _in_standard_sql_methods(field): + methods = ("count(", "avg(", "sum(", "extract(", "dayofyear(") + return field.lower().startswith(methods) + + if len(self.tables) > 1 or len(self.link_tables) > 0: + for idx, field in enumerate(self.fields): + if field is not None and "." not in field and not _in_standard_sql_methods(field): + self.fields[idx] = f"{self.tables[0]}.{field}" + + def cast_name_fields(self): + for i, field in enumerate(self.fields): + if field is not None: + self.fields[i] = cast_name(field) + + def get_table_columns(self): + try: + return get_table_columns(self.doctype) + except xhiveframework.db.TableMissingError: + if self.ignore_ddl: + return None + else: + raise + + def set_optional_columns(self): + """Removes optional columns like `_user_tags`, `_comments` etc. if not in table""" + # remove from fields + to_remove = [] + for fld in self.fields: + to_remove.extend(fld for f in optional_fields if f in fld and f not in self.columns) + for fld in to_remove: + del self.fields[self.fields.index(fld)] + + # remove from filters + to_remove = [] + for each in self.filters: + if isinstance(each, str): + each = [each] + + to_remove.extend( + each for element in each if element in optional_fields and element not in self.columns + ) + for each in to_remove: + if isinstance(self.filters, dict): + del self.filters[each] + else: + self.filters.remove(each) + + def build_conditions(self): + self.conditions = [] + self.grouped_or_conditions = [] + self.build_filter_conditions(self.filters, self.conditions) + self.build_filter_conditions(self.or_filters, self.grouped_or_conditions) + + # match conditions + if not self.flags.ignore_permissions: + match_conditions = self.build_match_conditions() + if match_conditions: + self.conditions.append(f"({match_conditions})") + + def build_filter_conditions(self, filters, conditions: list, ignore_permissions=None): + """build conditions from user filters""" + if ignore_permissions is not None: + self.flags.ignore_permissions = ignore_permissions + + if isinstance(filters, dict): + filters = [filters] + + for f in filters: + if isinstance(f, str): + conditions.append(f) + else: + conditions.append(self.prepare_filter_condition(f)) + + def remove_field(self, idx: int): + if self.as_list: + self.fields[idx] = None + else: + self.fields.pop(idx) + + def apply_fieldlevel_read_permissions(self): + """Apply fieldlevel read permissions to the query + + Note: Does not apply to `xhiveframework.model.core_doctype_list` + + Remove fields that user is not allowed to read. If `fields=["*"]` is passed, only permitted fields will + be returned. + + Example: + - User has read permission only on `title` for DocType `Note` + - Query: fields=["*"] + - Result: fields=["title", ...] // will also include XhiveFramework's meta field like `name`, `owner`, etc. + """ + if self.flags.ignore_permissions: + return + + asterisk_fields = [] + permitted_fields = get_permitted_fields( + doctype=self.doctype, + parenttype=self.parent_doctype, + permission_type=self.permission_map.get(self.doctype), + ignore_virtual=True, + ) + + for i, field in enumerate(self.fields): + if "distinct" in field.lower(): + # field: 'count(distinct `tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + if _fn := FN_PARAMS_PATTERN.findall(field): + column = _fn[0].replace("distinct ", "").replace("DISTINCT ", "").replace("`", "") + # field: 'distinct name' + # column: 'name' + else: + column = field.split(" ", 1)[1].replace("`", "") + else: + # field: 'count(`tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + column = field.split("(")[-1].split(")", 1)[0] + column = strip_alias(column).replace("`", "") + + if column == "*" and not in_function("*", field): + asterisk_fields.append(i) + continue + + # handle pseudo columns + elif not column or column.isnumeric(): + continue + + # labels / pseudo columns or xhiveframework internals + elif column[0] in {"'", '"'} or column in optional_fields: + continue + + # handle child / joined table fields + elif "." in field: + table, column = column.split(".", 1) + ch_doctype = table + + if ch_doctype in self.linked_table_aliases: + ch_doctype = self.linked_table_aliases[ch_doctype] + + ch_doctype = ch_doctype.replace("`", "").replace("tab", "", 1) + + if wrap_grave_quotes(table) in self.query_tables: + permitted_child_table_fields = get_permitted_fields( + doctype=ch_doctype, parenttype=self.doctype + ) + if column in permitted_child_table_fields or column in optional_fields: + continue + else: + self.remove_field(i) + else: + raise xhiveframework.PermissionError(ch_doctype) + + elif column in permitted_fields: + continue + + # field inside function calls / * handles things like count(*) + elif "(" in field: + if "*" in field: + continue + elif _params := FN_PARAMS_PATTERN.findall(field): + params = (x.strip() for x in _params[0].split(",")) + for param in params: + if not ( + not param + or param in permitted_fields + or param.isnumeric() + or "'" in param + or '"' in param + ): + self.remove_field(i) + break + continue + self.remove_field(i) + + # remove if access not allowed + else: + self.remove_field(i) + + # handle * fields + j = 0 + for i in asterisk_fields: + self.fields[i + j : i + j + 1] = permitted_fields + j = j + len(permitted_fields) - 1 + + def prepare_filter_condition(self, f): + """Returns a filter condition in the format: + ifnull(`tabDocType`.`fieldname`, fallback) operator "value" + """ + + # TODO: refactor + + from xhiveframework.boot import get_additional_filters_from_hooks + + additional_filters_config = get_additional_filters_from_hooks() + f = get_filter(self.doctype, f, additional_filters_config) + + tname = "`tab" + f.doctype + "`" + if tname not in self.tables: + self.append_table(tname) + + column_name = cast_name(f.fieldname if "ifnull(" in f.fieldname else f"{tname}.`{f.fieldname}`") + + if f.operator.lower() in additional_filters_config: + f.update(get_additional_filter_field(additional_filters_config, f, f.value)) + + meta = xhiveframework.get_meta(f.doctype) + + # primary key is never nullable, modified is usually indexed by default and always present + can_be_null = f.fieldname not in ("name", "modified") + + # prepare in condition + if f.operator.lower() in NestedSetHierarchy: + values = f.value or "" + + # TODO: handle list and tuple + # if not isinstance(values, (list, tuple)): + # values = values.split(",") + field = meta.get_field(f.fieldname) + ref_doctype = field.options if field else f.doctype + lft, rgt = "", "" + if f.value: + lft, rgt = xhiveframework.db.get_value(ref_doctype, f.value, ["lft", "rgt"]) or (0, 0) + + # Get descendants elements of a DocType with a tree structure + if f.operator.lower() in ( + "descendants of", + "not descendants of", + "descendants of (inclusive)", + ): + nodes = xhiveframework.get_all( + ref_doctype, + filters={"lft": [">", lft], "rgt": ["<", rgt]}, + order_by="`lft` ASC", + pluck="name", + ) + if f.operator.lower() == "descendants of (inclusive)": + nodes += [f.value] + else: + # Get ancestor elements of a DocType with a tree structure + nodes = xhiveframework.get_all( + ref_doctype, + filters={"lft": ["<", lft], "rgt": [">", rgt]}, + order_by="`lft` DESC", + pluck="name", + ) + + fallback = "''" + value = [xhiveframework.db.escape((cstr(v)).strip(), percent=False) for v in nodes] + if len(value): + value = f"({', '.join(value)})" + else: + value = "('')" + + # changing operator to IN as the above code fetches all the parent / child values and convert into tuple + # which can be directly used with IN operator to query. + f.operator = ( + "not in" if f.operator.lower() in ("not ancestors of", "not descendants of") else "in" + ) + + elif f.operator.lower() in ("in", "not in"): + # if values contain '' or falsy values then only coalesce column + # for `in` query this is only required if values contain '' or values are empty. + # for `not in` queries we can't be sure as column values might contain null. + if f.operator.lower() == "in": + can_be_null &= not f.value or any(v is None or v == "" for v in f.value) + + values = f.value or "" + if isinstance(values, str): + values = values.split(",") + + fallback = "''" + value = [xhiveframework.db.escape((cstr(v) or "").strip(), percent=False) for v in values] + if len(value): + value = f"({', '.join(value)})" + else: + value = "('')" + + else: + escape = True + df = meta.get("fields", {"fieldname": f.fieldname}) + df = df[0] if df else None + + if df and df.fieldtype in ("Check", "Float", "Int", "Currency", "Percent"): + can_be_null = False + + if f.operator.lower() in ("previous", "next", "timespan"): + date_range = get_date_range(f.operator.lower(), f.value) + f.operator = "Between" + f.value = date_range + fallback = f"'{FallBackDateTimeStr}'" + + if f.operator in (">", "<", ">=", "<=") and (f.fieldname in ("creation", "modified")): + value = cstr(f.value) + fallback = f"'{FallBackDateTimeStr}'" + + elif f.operator.lower() in ("between") and ( + f.fieldname in ("creation", "modified") + or (df and (df.fieldtype == "Date" or df.fieldtype == "Datetime")) + ): + escape = False + value = get_between_date_filter(f.value, df) + fallback = f"'{FallBackDateTimeStr}'" + + elif f.operator.lower() == "is": + if f.value == "set": + f.operator = "!=" + # Value can technically be null, but comparing with null will always be falsy + # Not using coalesce here is faster because indexes can be used. + # null != '' -> null ~ falsy + # '' != '' -> false + can_be_null = False + elif f.value == "not set": + f.operator = "=" + fallback = "''" + can_be_null = True + + value = "" + + if can_be_null and "ifnull" not in column_name.lower(): + column_name = f"ifnull({column_name}, {fallback})" + + elif df and df.fieldtype == "Date": + value = xhiveframework.db.format_date(f.value) + fallback = "'0001-01-01'" + + elif (df and df.fieldtype == "Datetime") or isinstance(f.value, datetime.datetime): + value = xhiveframework.db.format_datetime(f.value) + fallback = f"'{FallBackDateTimeStr}'" + + elif df and df.fieldtype == "Time": + value = get_time(f.value).strftime("%H:%M:%S.%f") + fallback = "'00:00:00'" + + elif f.operator.lower() in ("like", "not like") or ( + isinstance(f.value, str) + and (not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"]) + ): + value = "" if f.value is None else f.value + fallback = "''" + + if f.operator.lower() in ("like", "not like") and isinstance(value, str): + # because "like" uses backslash (\) for escaping + value = value.replace("\\", "\\\\").replace("%", "%%") + + elif f.operator == "=" and df and df.fieldtype in ["Link", "Data"]: # TODO: Refactor if possible + value = f.value or "''" + fallback = "''" + + elif f.fieldname == "name": + value = f.value or "''" + fallback = "''" + + else: + value = flt(f.value) + fallback = 0 + + if isinstance(f.value, Column): + can_be_null = False # added to avoid the ifnull/coalesce addition + quote = '"' if xhiveframework.conf.db_type == "postgres" else "`" + value = f"{tname}.{quote}{f.value.name}{quote}" + + # escape value + elif escape and isinstance(value, str): + value = f"{xhiveframework.db.escape(value, percent=False)}" + + if ( + self.ignore_ifnull + or not can_be_null + or (f.value and f.operator.lower() in ("=", "like")) + or "ifnull(" in column_name.lower() + ): + if f.operator.lower() == "like" and xhiveframework.conf.get("db_type") == "postgres": + f.operator = "ilike" + condition = f"{column_name} {f.operator} {value}" + else: + condition = f"ifnull({column_name}, {fallback}) {f.operator} {value}" + + return condition + + def build_match_conditions(self, as_condition=True) -> str | list: + """add match conditions if applicable""" + self.match_filters = [] + self.match_conditions = [] + only_if_shared = False + if not self.user: + self.user = xhiveframework.session.user + + if not self.tables: + self.extract_tables() + + role_permissions = xhiveframework.permissions.get_role_permissions(self.doctype_meta, user=self.user) + if ( + not self.doctype_meta.istable + and not (role_permissions.get("select") or role_permissions.get("read")) + and not self.flags.ignore_permissions + and not has_any_user_permission_for_doctype(self.doctype, self.user, self.reference_doctype) + ): + only_if_shared = True + self.shared = xhiveframework.share.get_shared(self.doctype, self.user) + if not self.shared: + xhiveframework.throw(_("No permission to read {0}").format(_(self.doctype)), xhiveframework.PermissionError) + else: + self.conditions.append(self.get_share_condition()) + + else: + # skip user perm check if owner constraint is required + if requires_owner_constraint(role_permissions): + self._fetch_shared_documents = True + self.match_conditions.append( + f"`tab{self.doctype}`.`owner` = {xhiveframework.db.escape(self.user, percent=False)}" + ) + + # add user permission only if role has read perm + elif role_permissions.get("read") or role_permissions.get("select"): + # get user permissions + user_permissions = xhiveframework.permissions.get_user_permissions(self.user) + self.add_user_permissions(user_permissions) + + # Only when full read access is not present fetch shared docuemnts. + # This is done to avoid extra query. + # Only following cases can require explicit addition of shared documents. + # 1. DocType has if_owner constraint and hence can't see shared documents + # 2. DocType has user permissions and hence can't see shared documents + if self._fetch_shared_documents: + self.shared = xhiveframework.share.get_shared(self.doctype, self.user) + + if as_condition: + conditions = "" + if self.match_conditions: + # will turn out like ((blog_post in (..) and blogger in (...)) or (blog_category in (...))) + conditions = "((" + ") or (".join(self.match_conditions) + "))" + + doctype_conditions = self.get_permission_query_conditions() + if doctype_conditions: + conditions += (" and " + doctype_conditions) if conditions else doctype_conditions + + # share is an OR condition, if there is a role permission + if not only_if_shared and self.shared and conditions: + conditions = f"(({conditions}) or ({self.get_share_condition()}))" + + return conditions + + else: + return self.match_filters + + def get_share_condition(self): + return ( + cast_name(f"`tab{self.doctype}`.name") + + f" in ({', '.join(xhiveframework.db.escape(s, percent=False) for s in self.shared)})" + ) + + def add_user_permissions(self, user_permissions): + doctype_link_fields = [] + doctype_link_fields = self.doctype_meta.get_link_fields() + + # append current doctype with fieldname as 'name' as first link field + doctype_link_fields.append( + dict( + options=self.doctype, + fieldname="name", + ) + ) + + match_filters = {} + match_conditions = [] + for df in doctype_link_fields: + if df.get("ignore_user_permissions"): + continue + + user_permission_values = user_permissions.get(df.get("options"), {}) + + if user_permission_values: + docs = [] + if xhiveframework.get_system_settings("apply_strict_user_permissions"): + condition = "" + else: + empty_value_condition = cast_name( + f"ifnull(`tab{self.doctype}`.`{df.get('fieldname')}`, '')=''" + ) + condition = empty_value_condition + " or " + + for permission in user_permission_values: + if not permission.get("applicable_for"): + docs.append(permission.get("doc")) + + # append docs based on user permission applicable on reference doctype + # this is useful when getting list of docs from a link field + # in this case parent doctype of the link + # will be the reference doctype + + elif df.get("fieldname") == "name" and self.reference_doctype: + if permission.get("applicable_for") == self.reference_doctype: + docs.append(permission.get("doc")) + + elif permission.get("applicable_for") == self.doctype: + docs.append(permission.get("doc")) + + if docs: + values = ", ".join(xhiveframework.db.escape(doc, percent=False) for doc in docs) + condition += cast_name(f"`tab{self.doctype}`.`{df.get('fieldname')}`") + f" in ({values})" + match_conditions.append(f"({condition})") + match_filters[df.get("options")] = docs + + if match_conditions: + self._fetch_shared_documents = True + self.match_conditions.append(" and ".join(match_conditions)) + + if match_filters: + self._fetch_shared_documents = True + self.match_filters.append(match_filters) + + def get_permission_query_conditions(self): + conditions = [] + condition_methods = xhiveframework.get_hooks("permission_query_conditions", {}).get(self.doctype, []) + if condition_methods: + for method in condition_methods: + c = xhiveframework.call(xhiveframework.get_attr(method), self.user) + if c: + conditions.append(c) + + permision_script_name = get_server_script_map().get("permission_query", {}).get(self.doctype) + if permision_script_name: + script = xhiveframework.get_doc("Server Script", permision_script_name) + condition = script.get_permission_query_conditions(self.user) + if condition: + conditions.append(condition) + + return " and ".join(conditions) if conditions else "" + + def set_order_by(self, args): + if self.order_by and self.order_by != "KEEP_DEFAULT_ORDERING": + args.order_by = self.order_by + else: + args.order_by = "" + + # don't add order by from meta if a mysql group function is used without group by clause + group_function_without_group_by = ( + len(self.fields) == 1 + and ( + self.fields[0].lower().startswith("count(") + or self.fields[0].lower().startswith("min(") + or self.fields[0].lower().startswith("max(") + or self.fields[0].lower().startswith("sum(") + or self.fields[0].lower().startswith("avg(") + ) + and not self.group_by + ) + + if not group_function_without_group_by: + sort_field = sort_order = None + if self.doctype_meta.sort_field and "," in self.doctype_meta.sort_field: + # multiple sort given in doctype definition + # Example: + # `idx desc, modified desc` + # will covert to + # `tabItem`.`idx` desc, `tabItem`.`modified` desc + args.order_by = ", ".join( + f"`tab{self.doctype}`.`{f_split[0].strip()}` {f_split[1].strip()}" + for f in self.doctype_meta.sort_field.split(",") + if (f_split := f.split(maxsplit=2)) + ) + else: + sort_field = self.doctype_meta.sort_field or "modified" + sort_order = (self.doctype_meta.sort_field and self.doctype_meta.sort_order) or "desc" + if self.order_by: + args.order_by = ( + f"`tab{self.doctype}`.`{sort_field or 'modified'}` {sort_order or 'desc'}" + ) + + # draft docs always on top + if hasattr(self.doctype_meta, "is_submittable") and self.doctype_meta.is_submittable: + if self.order_by: + args.order_by = f"`tab{self.doctype}`.docstatus asc, {args.order_by}" + + def validate_order_by_and_group_by(self, parameters: str): + """Check order by, group by so that atleast one column is selected and does not have subquery""" + if not parameters: + return + + blacklisted_sql_functions = { + "sleep", + } + _lower = parameters.lower() + + if "select" in _lower and "from" in _lower: + xhiveframework.throw(_("Cannot use sub-query in order by")) + + if ORDER_GROUP_PATTERN.match(_lower): + xhiveframework.throw(_("Illegal SQL Query")) + + for field in parameters.split(","): + field = field.strip() + function = field.split("(", 1)[0].rstrip().lower() + full_field_name = "." in field and field.startswith("`tab") + + if full_field_name: + tbl = field.split(".", 1)[0] + if tbl not in self.tables: + if tbl.startswith("`"): + tbl = tbl[4:-1] + xhiveframework.throw(_("Please select atleast 1 column from {0} to sort/group").format(tbl)) + + if function in blacklisted_sql_functions: + xhiveframework.throw(_("Cannot use {0} in order/group by").format(field)) + + def add_limit(self): + if self.limit_page_length: + return f"limit {self.limit_page_length} offset {self.limit_start}" + else: + return "" + + def add_comment_count(self, result): + for r in result: + if not r.name: + continue + + r._comment_count = 0 + if "_comments" in r: + r._comment_count = len(json.loads(r._comments or "[]")) + + def update_user_settings(self): + # update user settings if new search + user_settings = json.loads(get_user_settings(self.doctype)) + + if hasattr(self, "user_settings"): + user_settings.update(self.user_settings) + + if self.save_user_settings_fields: + user_settings["fields"] = self.user_settings_fields + + update_user_settings(self.doctype, user_settings) + + +def cast_name(column: str) -> str: + """Casts name field to varchar for postgres + + Handles majorly 4 cases: + 1. locate + 2. strpos + 3. ifnull + 4. coalesce + + Uses regex substitution. + + Example: + input - "ifnull(`tabBlog Post`.`name`, '')=''" + output - "ifnull(cast(`tabBlog Post`.`name` as varchar), '')=''" """ + + if xhiveframework.db.db_type == "mariadb": + return column + + kwargs = {"string": column} + if "cast(" not in column.lower() and "::" not in column: + if LOCATE_PATTERN.search(**kwargs): + return LOCATE_CAST_PATTERN.sub(r"locate(\1, cast(\2 as varchar))", **kwargs) + + elif match := FUNC_IFNULL_PATTERN.search(**kwargs): + func = match.groups()[0] + return re.sub(rf"{func}\(\s*([`\"]?name[`\"]?)\s*,", rf"{func}(cast(\1 as varchar),", **kwargs) + + return CAST_VARCHAR_PATTERN.sub(r"cast(\1 as varchar)", **kwargs) + + return column + + +def check_parent_permission(parent, child_doctype): + if parent: + # User may pass fake parent and get the information from the child table + if child_doctype and not ( + xhiveframework.db.exists("DocField", {"parent": parent, "options": child_doctype}) + or xhiveframework.db.exists("Custom Field", {"dt": parent, "options": child_doctype}) + ): + raise xhiveframework.PermissionError + + if xhiveframework.permissions.has_permission(parent): + return + + # Either parent not passed or the user doesn't have permission on parent doctype of child table! + raise xhiveframework.PermissionError + + +def get_order_by(doctype, meta): + order_by = "" + + sort_field = sort_order = None + if meta.sort_field and "," in meta.sort_field: + # multiple sort given in doctype definition + # Example: + # `idx desc, modified desc` + # will covert to + # `tabItem`.`idx` desc, `tabItem`.`modified` desc + order_by = ", ".join( + f"`tab{doctype}`.`{f_split[0].strip()}` {f_split[1].strip()}" + for f in meta.sort_field.split(",") + if (f_split := f.split(maxsplit=2)) + ) + + else: + sort_field = meta.sort_field or "modified" + sort_order = (meta.sort_field and meta.sort_order) or "desc" + order_by = f"`tab{doctype}`.`{sort_field or 'modified'}` {sort_order or 'desc'}" + + # draft docs always on top + if meta.is_submittable: + order_by = f"`tab{doctype}`.docstatus asc, {order_by}" + + return order_by + + +def has_any_user_permission_for_doctype(doctype, user, applicable_for): + user_permissions = xhiveframework.permissions.get_user_permissions(user=user) + doctype_user_permissions = user_permissions.get(doctype, []) + + for permission in doctype_user_permissions: + if not permission.applicable_for or permission.applicable_for == applicable_for: + return True + + return False + + +def get_between_date_filter(value, df=None): + """Handle datetime filter bounds for between filter values. + + If date is passed but fieldtype is datetime then + from part is converted to start of day and to part is converted to end of day. + If any of filter part (to or from) are missing then: + start or end of current day is assumed as fallback. + If fieldtypes match with filter values then: + no change is applied. + """ + + fieldtype = df and df.fieldtype or "Datetime" + + from_date = xhiveframework.utils.nowdate() + to_date = xhiveframework.utils.nowdate() + + if value and isinstance(value, list | tuple): + if len(value) >= 1: + from_date = value[0] + if len(value) >= 2: + to_date = value[1] + + # if filter value is date but fieldtype is datetime: + if fieldtype == "Datetime": + from_date = _convert_type_for_between_filters(from_date, set_time=datetime.time()) + to_date = _convert_type_for_between_filters(to_date, set_time=datetime.time(23, 59, 59, 999999)) + + # If filter value is already datetime, do nothing. + if fieldtype == "Datetime": + cond = f"'{xhiveframework.db.format_datetime(from_date)}' AND '{xhiveframework.db.format_datetime(to_date)}'" + else: + cond = f"'{xhiveframework.db.format_date(from_date)}' AND '{xhiveframework.db.format_date(to_date)}'" + + return cond + + +def _convert_type_for_between_filters( + value: DateTimeLikeObject, set_time: datetime.time +) -> datetime.datetime: + if isinstance(value, str): + if " " in value.strip(): + value = get_datetime(value) + else: + value = getdate(value) + + if isinstance(value, datetime.datetime): + return value + elif isinstance(value, datetime.date): + return datetime.datetime.combine(value, set_time) + + return value + + +def get_additional_filter_field(additional_filters_config, f, value): + additional_filter = additional_filters_config[f.operator.lower()] + f = xhiveframework._dict(xhiveframework.get_attr(additional_filter["get_field"])()) + if f.query_value: + for option in f.options: + option = xhiveframework._dict(option) + if option.value == value: + f.value = option.query_value + return f + + +def get_date_range(operator: str, value: str): + timespan_map = { + "1 week": "week", + "1 month": "month", + "3 months": "quarter", + "6 months": "6 months", + "1 year": "year", + } + period_map = { + "previous": "last", + "next": "next", + } + + if operator != "timespan": + timespan = f"{period_map[operator]} {timespan_map[value]}" + else: + timespan = value + + return get_timespan_date_range(timespan) + + +def requires_owner_constraint(role_permissions): + """Returns True if "select" or "read" isn't available without being creator.""" + + if not role_permissions.get("has_if_owner_enabled"): + return + + if_owner_perms = role_permissions.get("if_owner") + if not if_owner_perms: + return + + # has select or read without if owner, no need for constraint + for perm_type in ("select", "read"): + if role_permissions.get(perm_type) and perm_type not in if_owner_perms: + return + + # not checking if either select or read if present in if_owner_perms + # because either of those is required to perform a query + return True + + +def wrap_grave_quotes(table: str) -> str: + if table[0] != "`": + table = f"`{table}`" + return table + + +def is_plain_field(field: str) -> bool: + for char in field: + if char in SPECIAL_FIELD_CHARS: + return False + return True + + +def in_function(substr: str, field: str) -> bool: + try: + return substr in field and field.index("(") < field.index(substr) < field.index(")") + except ValueError: + return False + + +def strip_alias(field: str) -> str: + # Note: Currently only supports aliases that use the " AS " syntax + if " as " in field.lower(): + return field.split(" as ", 1)[0] + return field diff --git a/xhiveframework/model/delete_doc.py b/xhiveframework/model/delete_doc.py new file mode 100644 index 0000000..8153c42 --- /dev/null +++ b/xhiveframework/model/delete_doc.py @@ -0,0 +1,463 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import os +import shutil + +import xhiveframework +import xhiveframework.defaults +import xhiveframework.model.meta +from xhiveframework import _, get_module_path +from xhiveframework.desk.doctype.tag.tag import delete_tags_for_document +from xhiveframework.model.docstatus import DocStatus +from xhiveframework.model.dynamic_links import get_dynamic_link_map +from xhiveframework.model.naming import revert_series_if_last +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.utils.file_manager import remove_all +from xhiveframework.utils.global_search import delete_for_document +from xhiveframework.utils.password import delete_all_passwords_for + + +def delete_doc( + doctype=None, + name=None, + force=0, + ignore_doctypes=None, + for_reload=False, + ignore_permissions=False, + flags=None, + ignore_on_trash=False, + ignore_missing=True, + delete_permanently=False, +): + """ + Deletes a doc(dt, dn) and validates if it is not submitted and not linked in a live record + """ + if not ignore_doctypes: + ignore_doctypes = [] + + # get from form + if not doctype: + doctype = xhiveframework.form_dict.get("dt") + name = xhiveframework.form_dict.get("dn") + + is_virtual = is_virtual_doctype(doctype) + + names = name + if isinstance(name, str) or isinstance(name, int): + names = [name] + + for name in names or []: + if is_virtual: + xhiveframework.get_doc(doctype, name).delete() + continue + + # already deleted..? + if not xhiveframework.db.exists(doctype, name): + if not ignore_missing: + raise xhiveframework.DoesNotExistError + else: + return False + + # delete passwords + delete_all_passwords_for(doctype, name) + + doc = None + if doctype == "DocType": + if for_reload: + try: + doc = xhiveframework.get_doc(doctype, name) + except xhiveframework.DoesNotExistError: + pass + else: + doc.run_method("before_reload") + + else: + doc = xhiveframework.get_doc(doctype, name) + if not (doc.custom or xhiveframework.conf.developer_mode or xhiveframework.flags.in_patch or force): + xhiveframework.throw(_("Standard DocType can not be deleted.")) + + update_flags(doc, flags, ignore_permissions) + check_permission_and_not_submitted(doc) + # delete custom table fields using this doctype. + xhiveframework.db.delete( + "Custom Field", {"options": name, "fieldtype": ("in", xhiveframework.model.table_fields)} + ) + xhiveframework.db.delete("__global_search", {"doctype": name}) + + delete_from_table(doctype, name, ignore_doctypes, None) + + if ( + xhiveframework.conf.developer_mode + and not doc.custom + and not ( + for_reload + or xhiveframework.flags.in_migrate + or xhiveframework.flags.in_install + or xhiveframework.flags.in_uninstall + ) + ): + try: + delete_controllers(name, doc.module) + except (OSError, KeyError): + # in case a doctype doesnt have any controller code nor any app and module + pass + + else: + # Lock the doc without waiting + try: + xhiveframework.db.get_value(doctype, name, for_update=True, wait=False) + except xhiveframework.QueryTimeoutError: + xhiveframework.throw( + _( + "This document can not be deleted right now as it's being modified by another user. Please try again after some time." + ), + exc=xhiveframework.QueryTimeoutError, + ) + doc = xhiveframework.get_doc(doctype, name) + + if not for_reload: + update_flags(doc, flags, ignore_permissions) + check_permission_and_not_submitted(doc) + + if not ignore_on_trash: + doc.run_method("on_trash") + doc.flags.in_delete = True + doc.run_method("on_change") + + # check if links exist + if not force: + check_if_doc_is_linked(doc) + check_if_doc_is_dynamically_linked(doc) + + update_naming_series(doc) + delete_from_table(doctype, name, ignore_doctypes, doc) + doc.run_method("after_delete") + + # delete attachments + remove_all(doctype, name, from_delete=True, delete_permanently=delete_permanently) + + if not for_reload: + # Enqueued at the end, because it gets committed + # All the linked docs should be checked beforehand + xhiveframework.enqueue( + "xhiveframework.model.delete_doc.delete_dynamic_links", + doctype=doc.doctype, + name=doc.name, + now=xhiveframework.flags.in_test, + enqueue_after_commit=True, + ) + + # clear cache for Document + doc.clear_cache() + # delete global search entry + delete_for_document(doc) + # delete tag link entry + delete_tags_for_document(doc) + + if for_reload: + delete_permanently = True + + if not delete_permanently: + add_to_deleted_document(doc) + + if doc and not for_reload: + if not xhiveframework.flags.in_patch: + try: + doc.notify_update() + insert_feed(doc) + except ImportError: + pass + + +def add_to_deleted_document(doc): + """Add this document to Deleted Document table. Called after delete""" + if doc.doctype != "Deleted Document" and xhiveframework.flags.in_install != "xhiveframework": + xhiveframework.get_doc( + dict( + doctype="Deleted Document", + deleted_doctype=doc.doctype, + deleted_name=doc.name, + data=doc.as_json(), + owner=xhiveframework.session.user, + ) + ).db_insert() + + +def update_naming_series(doc): + if doc.meta.autoname: + if doc.meta.autoname.startswith("naming_series:") and getattr(doc, "naming_series", None): + revert_series_if_last(doc.naming_series, doc.name, doc) + + elif doc.meta.autoname.split(":", 1)[0] not in ("Prompt", "field", "hash", "autoincrement"): + revert_series_if_last(doc.meta.autoname, doc.name, doc) + + +def delete_from_table(doctype: str, name: str, ignore_doctypes: list[str], doc): + if doctype != "DocType" and doctype == name: + xhiveframework.db.delete("Singles", {"doctype": name}) + else: + xhiveframework.db.delete(doctype, {"name": name}) + if doc: + child_doctypes = [ + d.options for d in doc.meta.get_table_fields() if xhiveframework.get_meta(d.options).is_virtual == 0 + ] + + else: + child_doctypes = xhiveframework.get_all( + "DocField", + fields="options", + filters={"fieldtype": ["in", xhiveframework.model.table_fields], "parent": doctype}, + pluck="options", + ) + + child_doctypes_to_delete = set(child_doctypes) - set(ignore_doctypes) + for child_doctype in child_doctypes_to_delete: + xhiveframework.db.delete(child_doctype, {"parenttype": doctype, "parent": name}) + + +def update_flags(doc, flags=None, ignore_permissions=False): + if ignore_permissions: + if not flags: + flags = {} + flags["ignore_permissions"] = ignore_permissions + + if flags: + doc.flags.update(flags) + + +def check_permission_and_not_submitted(doc): + # permission + if ( + not doc.flags.ignore_permissions + and xhiveframework.session.user != "Administrator" + and (not doc.has_permission("delete") or (doc.doctype == "DocType" and not doc.custom)) + ): + xhiveframework.msgprint( + _("User not allowed to delete {0}: {1}").format(doc.doctype, doc.name), + raise_exception=xhiveframework.PermissionError, + ) + + # check if submitted + if doc.docstatus.is_submitted(): + xhiveframework.msgprint( + _("{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first.").format( + _(doc.doctype), + doc.name, + "", + "", + ), + raise_exception=True, + ) + + +def check_if_doc_is_linked(doc, method="Delete"): + """ + Raises excption if the given doc(dt, dn) is linked in another record. + """ + from xhiveframework.model.rename_doc import get_link_fields + + link_fields = get_link_fields(doc.doctype) + ignored_doctypes = set() + + if method == "Cancel" and (doc_ignore_flags := doc.get("ignore_linked_doctypes")): + ignored_doctypes.update(doc_ignore_flags) + if method == "Delete": + ignored_doctypes.update(xhiveframework.get_hooks("ignore_links_on_delete")) + + for lf in link_fields: + link_dt, link_field, issingle = lf["parent"], lf["fieldname"], lf["issingle"] + if link_dt in ignored_doctypes or (link_field == "amended_from" and method == "Cancel"): + continue + + try: + meta = xhiveframework.get_meta(link_dt) + except xhiveframework.DoesNotExistError: + xhiveframework.clear_last_message() + # This mostly happens when app do not remove their customizations, we shouldn't + # prevent link checks from failing in those cases + continue + + if issingle: + if xhiveframework.db.get_single_value(link_dt, link_field) == doc.name: + raise_link_exists_exception(doc, link_dt, link_dt) + continue + + fields = ["name", "docstatus"] + + if meta.istable: + fields.extend(["parent", "parenttype"]) + + for item in xhiveframework.db.get_values(link_dt, {link_field: doc.name}, fields, as_dict=True): + # available only in child table cases + item_parent = getattr(item, "parent", None) + linked_parent_doctype = item.parenttype if item_parent else link_dt + + if linked_parent_doctype in ignored_doctypes: + continue + + if method != "Delete" and (method != "Cancel" or not DocStatus(item.docstatus).is_submitted()): + # don't raise exception if not + # linked to a non-cancelled doc when deleting or to a submitted doc when cancelling + continue + elif link_dt == doc.doctype and (item_parent or item.name) == doc.name: + # don't raise exception if not + # linked to same item or doc having same name as the item + continue + else: + reference_docname = item_parent or item.name + raise_link_exists_exception(doc, linked_parent_doctype, reference_docname) + + +def check_if_doc_is_dynamically_linked(doc, method="Delete"): + """Raise `xhiveframework.LinkExistsError` if the document is dynamically linked""" + for df in get_dynamic_link_map().get(doc.doctype, []): + ignore_linked_doctypes = doc.get("ignore_linked_doctypes") or [] + + if df.parent in xhiveframework.get_hooks("ignore_links_on_delete") or ( + df.parent in ignore_linked_doctypes and method == "Cancel" + ): + # don't check for communication and todo! + continue + + meta = xhiveframework.get_meta(df.parent) + if meta.issingle: + # dynamic link in single doc + refdoc = xhiveframework.db.get_singles_dict(df.parent) + if ( + refdoc.get(df.options) == doc.doctype + and refdoc.get(df.fieldname) == doc.name + and ( + # linked to an non-cancelled doc when deleting + (method == "Delete" and not DocStatus(refdoc.docstatus).is_cancelled()) + # linked to a submitted doc when cancelling + or (method == "Cancel" and DocStatus(refdoc.docstatus).is_submitted()) + ) + ): + raise_link_exists_exception(doc, df.parent, df.parent) + else: + # dynamic link in table + df["table"] = ", `parent`, `parenttype`, `idx`" if meta.istable else "" + for refdoc in xhiveframework.db.sql( + """select `name`, `docstatus` {table} from `tab{parent}` where + {options}=%s and {fieldname}=%s""".format(**df), + (doc.doctype, doc.name), + as_dict=True, + ): + # linked to an non-cancelled doc when deleting + # or linked to a submitted doc when cancelling + if (method == "Delete" and not DocStatus(refdoc.docstatus).is_cancelled()) or ( + method == "Cancel" and DocStatus(refdoc.docstatus).is_submitted() + ): + reference_doctype = refdoc.parenttype if meta.istable else df.parent + reference_docname = refdoc.parent if meta.istable else refdoc.name + + if reference_doctype in xhiveframework.get_hooks("ignore_links_on_delete") or ( + reference_doctype in ignore_linked_doctypes and method == "Cancel" + ): + # don't check for communication and todo! + continue + + at_position = f"at Row: {refdoc.idx}" if meta.istable else "" + + raise_link_exists_exception(doc, reference_doctype, reference_docname, at_position) + + +def raise_link_exists_exception(doc, reference_doctype, reference_docname, row=""): + doc_link = f'{doc.name}' + reference_link = f'{reference_docname}' + + # hack to display Single doctype only once in message + if reference_doctype == reference_docname: + reference_doctype = "" + + xhiveframework.throw( + _("Cannot delete or cancel because {0} {1} is linked with {2} {3} {4}").format( + _(doc.doctype), doc_link, _(reference_doctype), reference_link, row + ), + xhiveframework.LinkExistsError, + ) + + +def delete_dynamic_links(doctype, name): + delete_references("ToDo", doctype, name, "reference_type") + delete_references("Email Unsubscribe", doctype, name) + delete_references("DocShare", doctype, name, "share_doctype", "share_name") + delete_references("Version", doctype, name, "ref_doctype", "docname") + delete_references("Comment", doctype, name) + delete_references("View Log", doctype, name) + delete_references("Document Follow", doctype, name, "ref_doctype", "ref_docname") + delete_references("Notification Log", doctype, name, "document_type", "document_name") + + # unlink communications + clear_timeline_references(doctype, name) + clear_references("Communication", doctype, name) + + clear_references("Activity Log", doctype, name) + clear_references("Activity Log", doctype, name, "timeline_doctype", "timeline_name") + + +def delete_references( + doctype, + reference_doctype, + reference_name, + reference_doctype_field="reference_doctype", + reference_name_field="reference_name", +): + xhiveframework.db.delete( + doctype, {reference_doctype_field: reference_doctype, reference_name_field: reference_name} + ) + + +def clear_references( + doctype, + reference_doctype, + reference_name, + reference_doctype_field="reference_doctype", + reference_name_field="reference_name", +): + xhiveframework.db.sql( + f"""update + `tab{doctype}` + set + {reference_doctype_field}=NULL, {reference_name_field}=NULL + where + {reference_doctype_field}=%s and {reference_name_field}=%s""", # nosec + (reference_doctype, reference_name), + ) + + +def clear_timeline_references(link_doctype, link_name): + xhiveframework.db.delete("Communication Link", {"link_doctype": link_doctype, "link_name": link_name}) + + +def insert_feed(doc): + if ( + xhiveframework.flags.in_install + or xhiveframework.flags.in_uninstall + or xhiveframework.flags.in_import + or getattr(doc, "no_feed_on_delete", False) + ): + return + + from xhiveframework.utils import get_fullname + + xhiveframework.get_doc( + { + "doctype": "Comment", + "comment_type": "Deleted", + "reference_doctype": doc.doctype, + "subject": f"{_(doc.doctype)} {doc.name}", + "full_name": get_fullname(doc.owner), + } + ).insert(ignore_permissions=True) + + +def delete_controllers(doctype, module): + """ + Delete controller code in the doctype folder + """ + module_path = get_module_path(module) + dir_path = os.path.join(module_path, "doctype", xhiveframework.scrub(doctype)) + + shutil.rmtree(dir_path) diff --git a/xhiveframework/model/docfield.py b/xhiveframework/model/docfield.py new file mode 100644 index 0000000..b7d8f19 --- /dev/null +++ b/xhiveframework/model/docfield.py @@ -0,0 +1,10 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +"""docfield utililtes""" + +import xhiveframework + + +def supports_translation(fieldtype): + return fieldtype in ["Data", "Select", "Text", "Small Text", "Text Editor"] diff --git a/xhiveframework/model/docstatus.py b/xhiveframework/model/docstatus.py new file mode 100644 index 0000000..9b8f13a --- /dev/null +++ b/xhiveframework/model/docstatus.py @@ -0,0 +1,25 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +class DocStatus(int): + def is_draft(self): + return self == self.draft() + + def is_submitted(self): + return self == self.submitted() + + def is_cancelled(self): + return self == self.cancelled() + + @classmethod + def draft(cls): + return cls(0) + + @classmethod + def submitted(cls): + return cls(1) + + @classmethod + def cancelled(cls): + return cls(2) diff --git a/xhiveframework/model/document.py b/xhiveframework/model/document.py new file mode 100644 index 0000000..0c58fcd --- /dev/null +++ b/xhiveframework/model/document.py @@ -0,0 +1,1736 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import hashlib +import json +import time +from collections.abc import Generator, Iterable +from typing import TYPE_CHECKING, Any, Optional + +from werkzeug.exceptions import NotFound + +import xhiveframework +from xhiveframework import _, is_whitelisted, msgprint +from xhiveframework.core.doctype.file.utils import relink_mismatched_files +from xhiveframework.core.doctype.server_script.server_script_utils import run_server_script_for_doc_event +from xhiveframework.desk.form.document_follow import follow_document +from xhiveframework.integrations.doctype.webhook import run_webhooks +from xhiveframework.model import optional_fields, table_fields +from xhiveframework.model.base_document import BaseDocument, get_controller +from xhiveframework.model.docstatus import DocStatus +from xhiveframework.model.naming import set_new_name, validate_name +from xhiveframework.model.utils import is_virtual_doctype +from xhiveframework.model.workflow import set_workflow_state_on_action, validate_workflow +from xhiveframework.types import DF +from xhiveframework.utils import compare, cstr, date_diff, file_lock, flt, get_datetime_str, now +from xhiveframework.utils.data import get_absolute_url, get_datetime, get_timedelta, getdate +from xhiveframework.utils.global_search import update_global_search + +if TYPE_CHECKING: + from xhiveframework.core.doctype.docfield.docfield import DocField + + +DOCUMENT_LOCK_EXPIRTY = 12 * 60 * 60 # All locks expire in 12 hours automatically +DOCUMENT_LOCK_SOFT_EXPIRY = 60 * 60 # Let users force-unlock after 60 minutes + + +def get_doc(*args, **kwargs): + """returns a xhiveframework.model.Document object. + + :param arg1: Document dict or DocType name. + :param arg2: [optional] document name. + :param for_update: [optional] select document for update. + + There are multiple ways to call `get_doc` + + # will fetch the latest user object (with child table) from the database + user = get_doc("User", "test@example.com") + + # create a new object + user = get_doc({ + "doctype":"User" + "email_id": "test@example.com", + "roles: [ + {"role": "System Manager"} + ] + }) + + # create new object with keyword arguments + user = get_doc(doctype='User', email_id='test@example.com') + + # select a document for update + user = get_doc("User", "test@example.com", for_update=True) + """ + if args: + if isinstance(args[0], BaseDocument): + # already a document + return args[0] + elif isinstance(args[0], str): + doctype = args[0] + + elif isinstance(args[0], dict): + # passed a dict + kwargs = args[0] + + else: + raise ValueError("First non keyword argument must be a string or dict") + + if len(args) < 2 and kwargs: + if "doctype" in kwargs: + doctype = kwargs["doctype"] + else: + raise ValueError('"doctype" is a required key') + + controller = get_controller(doctype) + if controller: + return controller(*args, **kwargs) + + raise ImportError(doctype) + + +class Document(BaseDocument): + """All controllers inherit from `Document`.""" + + doctype: DF.Data + name: DF.Data | None + flags: xhiveframework._dict[str, Any] + owner: DF.Link + creation: DF.Datetime + modified: DF.Datetime + modified_by: DF.Link + idx: DF.Int + + def __init__(self, *args, **kwargs): + """Constructor. + + :param arg1: DocType name as string or document **dict** + :param arg2: Document name, if `arg1` is DocType name. + + If DocType name and document name are passed, the object will load + all values (including child documents) from the database. + """ + self.doctype = None + self.name = None + self.flags = xhiveframework._dict() + + if args and args[0]: + if isinstance(args[0], str): + # first arugment is doctype + self.doctype = args[0] + + # doctype for singles, string value or filters for other documents + self.name = self.doctype if len(args) == 1 else args[1] + + # for_update is set in flags to avoid changing load_from_db signature + # since it is used in virtual doctypes and inherited in child classes + self.flags.for_update = kwargs.get("for_update") + self.load_from_db() + return + + if isinstance(args[0], dict): + # first argument is a dict + kwargs = args[0] + + if kwargs: + # init base document + super().__init__(kwargs) + self.init_child_tables() + self.init_valid_columns() + + else: + # incorrect arguments. let's not proceed. + raise ValueError("Illegal arguments") + + @property + def is_locked(self): + return file_lock.lock_exists(self.get_signature()) + + def load_from_db(self): + """Load document and children from database and create properties + from fields""" + self.flags.ignore_children = True + if not getattr(self, "_metaclass", False) and self.meta.issingle: + single_doc = xhiveframework.db.get_singles_dict(self.doctype, for_update=self.flags.for_update) + if not single_doc: + single_doc = xhiveframework.new_doc(self.doctype, as_dict=True) + single_doc["name"] = self.doctype + del single_doc["__islocal"] + + super().__init__(single_doc) + self.init_valid_columns() + self._fix_numeric_types() + + else: + get_value_kwargs = {"for_update": self.flags.for_update, "as_dict": True} + if not isinstance(self.name, dict | list): + get_value_kwargs["order_by"] = None + + d = xhiveframework.db.get_value( + doctype=self.doctype, filters=self.name, fieldname="*", **get_value_kwargs + ) + + if not d: + xhiveframework.throw( + _("{0} {1} not found").format(_(self.doctype), self.name), xhiveframework.DoesNotExistError + ) + + super().__init__(d) + self.flags.pop("ignore_children", None) + + for df in self._get_table_fields(): + # Make sure not to query the DB for a child table, if it is a virtual one. + # During xhiveframework is installed, the property "is_virtual" is not available in tabDocType, so + # we need to filter those cases for the access to xhiveframework.db.get_value() as it would crash otherwise. + if hasattr(self, "doctype") and not hasattr(self, "module") and is_virtual_doctype(df.options): + self.set(df.fieldname, []) + continue + + children = ( + xhiveframework.db.get_values( + df.options, + {"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname}, + "*", + as_dict=True, + order_by="idx asc", + for_update=self.flags.for_update, + ) + or [] + ) + + self.set(df.fieldname, children) + + # sometimes __setup__ can depend on child values, hence calling again at the end + if hasattr(self, "__setup__"): + self.__setup__() + + def reload(self): + """Reload document from database""" + self.load_from_db() + + def get_latest(self): + if not getattr(self, "_doc_before_save", None): + self.load_doc_before_save() + + return self._doc_before_save + + def check_permission(self, permtype="read", permlevel=None): + """Raise `xhiveframework.PermissionError` if not permitted""" + if not self.has_permission(permtype): + self.raise_no_permission_to(permtype) + + def has_permission(self, permtype="read", *, debug=False, user=None) -> bool: + """ + Call `xhiveframework.permissions.has_permission` if `ignore_permissions` flag isn't truthy + + :param permtype: `read`, `write`, `submit`, `cancel`, `delete`, etc. + """ + + if self.flags.ignore_permissions: + return True + + import xhiveframework.permissions + + return xhiveframework.permissions.has_permission(self.doctype, permtype, self, debug=debug, user=user) + + def raise_no_permission_to(self, perm_type): + """Raise `xhiveframework.PermissionError`.""" + xhiveframework.flags.error_message = ( + _("Insufficient Permission for {0}").format(self.doctype) + f" ({xhiveframework.bold(_(perm_type))})" + ) + raise xhiveframework.PermissionError + + def insert( + self, + ignore_permissions=None, + ignore_links=None, + ignore_if_duplicate=False, + ignore_mandatory=None, + set_name=None, + set_child_names=True, + ) -> "Document": + """Insert the document in the database (as a new document). + This will check for user permissions and execute `before_insert`, + `validate`, `on_update`, `after_insert` methods if they are written. + + :param ignore_permissions: Do not check permissions if True. + :param ignore_links: Do not check validity of links if True. + :param ignore_if_duplicate: Do not raise error if a duplicate entry exists. + :param ignore_mandatory: Do not check missing mandatory fields if True. + :param set_name: Name to set for the document, if valid. + :param set_child_names: Whether to set names for the child documents. + """ + if self.flags.in_print: + return self + + self.flags.notifications_executed = [] + + if ignore_permissions is not None: + self.flags.ignore_permissions = ignore_permissions + + if ignore_links is not None: + self.flags.ignore_links = ignore_links + + if ignore_mandatory is not None: + self.flags.ignore_mandatory = ignore_mandatory + + self.set("__islocal", True) + + self._set_defaults() + self.set_user_and_timestamp() + self.set_docstatus() + self.check_if_latest() + self._validate_links() + self.check_permission("create") + self.run_method("before_insert") + self.set_new_name(set_name=set_name, set_child_names=set_child_names) + self.set_parent_in_children() + self.validate_higher_perm_levels() + + self.flags.in_insert = True + self.run_before_save_methods() + self._validate() + self.set_docstatus() + self.flags.in_insert = False + + # run validate, on update etc. + + # parent + if getattr(self.meta, "issingle", 0): + self.update_single(self.get_valid_dict()) + else: + self.db_insert(ignore_if_duplicate=ignore_if_duplicate) + + # children + for d in self.get_all_children(): + d.db_insert() + + self.run_method("after_insert") + self.flags.in_insert = True + + if self.get("amended_from"): + self.copy_attachments_from_amended_from() + + relink_mismatched_files(self) + self.run_post_save_methods() + self.flags.in_insert = False + + # delete __islocal + if hasattr(self, "__islocal"): + delattr(self, "__islocal") + + # clear unsaved flag + if hasattr(self, "__unsaved"): + delattr(self, "__unsaved") + + if not (xhiveframework.flags.in_migrate or xhiveframework.local.flags.in_install or xhiveframework.flags.in_setup_wizard): + if xhiveframework.get_cached_value("User", xhiveframework.session.user, "follow_created_documents"): + follow_document(self.doctype, self.name, xhiveframework.session.user) + return self + + def check_if_locked(self): + if self.creation and self.is_locked: + raise xhiveframework.DocumentLockedError + + def save(self, *args, **kwargs): + """Wrapper for _save""" + return self._save(*args, **kwargs) + + def _save(self, ignore_permissions=None, ignore_version=None) -> "Document": + """Save the current document in the database in the **DocType**'s table or + `tabSingles` (for single types). + + This will check for user permissions and execute + `validate` before updating, `on_update` after updating triggers. + + :param ignore_permissions: Do not check permissions if True. + :param ignore_version: Do not save version if True.""" + if self.flags.in_print: + return + + self.flags.notifications_executed = [] + + if ignore_permissions is not None: + self.flags.ignore_permissions = ignore_permissions + + self.flags.ignore_version = xhiveframework.flags.in_test if ignore_version is None else ignore_version + + if self.get("__islocal") or not self.get("name"): + return self.insert() + + self.check_if_locked() + self._set_defaults() + self.check_permission("write", "save") + + self.set_user_and_timestamp() + self.set_docstatus() + self.check_if_latest() + self.set_parent_in_children() + self.set_name_in_children() + + self.validate_higher_perm_levels() + self._validate_links() + self.run_before_save_methods() + + if self._action != "cancel": + self._validate() + + if self._action == "update_after_submit": + self.validate_update_after_submit() + + self.set_docstatus() + + # parent + if self.meta.issingle: + self.update_single(self.get_valid_dict()) + else: + self.db_update() + + self.update_children() + self.run_post_save_methods() + + # clear unsaved flag + if hasattr(self, "__unsaved"): + delattr(self, "__unsaved") + + return self + + def copy_attachments_from_amended_from(self): + """Copy attachments from `amended_from`""" + from xhiveframework.desk.form.load import get_attachments + + # loop through attachments + for attach_item in get_attachments(self.doctype, self.amended_from): + # save attachments to new doc + _file = xhiveframework.get_doc( + { + "doctype": "File", + "file_url": attach_item.file_url, + "file_name": attach_item.file_name, + "attached_to_name": self.name, + "attached_to_doctype": self.doctype, + "folder": "Home/Attachments", + "is_private": attach_item.is_private, + } + ) + _file.save() + + def update_children(self): + """update child tables""" + for df in self.meta.get_table_fields(): + self.update_child_table(df.fieldname, df) + + def update_child_table(self, fieldname: str, df: Optional["DocField"] = None): + """sync child table for given fieldname""" + df: "DocField" = df or self.meta.get_field(fieldname) + all_rows = self.get(df.fieldname) + + # delete rows that do not match the ones in the document + # if the doctype isn't in ignore_children_type flag and isn't virtual + if not ( + df.options in (self.flags.ignore_children_type or ()) + or xhiveframework.get_meta(df.options).is_virtual == 1 + ): + existing_row_names = [row.name for row in all_rows if row.name and not row.is_new()] + + tbl = xhiveframework.qb.DocType(df.options) + qry = ( + xhiveframework.qb.from_(tbl) + .where(tbl.parent == self.name) + .where(tbl.parenttype == self.doctype) + .where(tbl.parentfield == fieldname) + .delete() + ) + + if existing_row_names: + qry = qry.where(tbl.name.notin(existing_row_names)) + + qry.run() + + # update / insert + for d in all_rows: + d: Document + d.db_update() + + def get_doc_before_save(self) -> "Document": + return getattr(self, "_doc_before_save", None) + + def has_value_changed(self, fieldname): + """Return True if value has changed before and after saving.""" + from datetime import date, datetime, timedelta + + previous = self.get_doc_before_save() + + if not previous: + return True + + previous_value = previous.get(fieldname) + current_value = self.get(fieldname) + + if isinstance(previous_value, datetime): + current_value = get_datetime(current_value) + elif isinstance(previous_value, date): + current_value = getdate(current_value) + elif isinstance(previous_value, timedelta): + current_value = get_timedelta(current_value) + + return previous_value != current_value + + def set_new_name(self, force=False, set_name=None, set_child_names=True): + """Calls `xhiveframework.naming.set_new_name` for parent and child docs.""" + + if self.flags.name_set and not force: + return + + autoname = self.meta.autoname or "" + + # If autoname has set as Prompt (name) + if self.get("__newname") and autoname.lower() == "prompt": + self.name = validate_name(self.doctype, self.get("__newname")) + self.flags.name_set = True + return + + if set_name: + self.name = validate_name(self.doctype, set_name) + else: + set_new_name(self) + + if set_child_names: + # set name for children + for d in self.get_all_children(): + set_new_name(d) + + self.flags.name_set = True + + def get_title(self): + """Get the document title based on title_field or `title` or `name`""" + return self.get(self.meta.get_title_field()) or "" + + def set_title_field(self): + """Set title field based on template""" + + def get_values(): + values = self.as_dict() + # format values + for key, value in values.items(): + if value is None: + values[key] = "" + return values + + if self.meta.get("title_field") == "title": + df = self.meta.get_field(self.meta.title_field) + + if df.options: + self.set(df.fieldname, df.options.format(**get_values())) + elif self.is_new() and not self.get(df.fieldname) and df.default: + # set default title for new transactions (if default) + self.set(df.fieldname, df.default.format(**get_values())) + + def update_single(self, d): + """Updates values for Single type Document in `tabSingles`.""" + xhiveframework.db.delete("Singles", {"doctype": self.doctype}) + for field, value in d.items(): + if field != "doctype": + xhiveframework.db.sql( + """insert into `tabSingles` (doctype, field, value) + values (%s, %s, %s)""", + (self.doctype, field, value), + ) + + if self.doctype in xhiveframework.db.value_cache: + del xhiveframework.db.value_cache[self.doctype] + + def set_user_and_timestamp(self): + self._original_modified = self.modified + self.modified = now() + self.modified_by = xhiveframework.session.user + + # We'd probably want the creation and owner to be set via API + # or Data import at some point, that'd have to be handled here + if self.is_new() and not ( + xhiveframework.flags.in_install or xhiveframework.flags.in_patch or xhiveframework.flags.in_migrate + ): + self.creation = self.modified + self.owner = self.modified_by + + for d in self.get_all_children(): + d.modified = self.modified + d.modified_by = self.modified_by + if not d.owner: + d.owner = self.owner + if not d.creation: + d.creation = self.creation + + xhiveframework.flags.currently_saving.append((self.doctype, self.name)) + + def set_docstatus(self): + if self.docstatus is None: + self.docstatus = DocStatus.draft() + + for d in self.get_all_children(): + d.docstatus = self.docstatus + + def _validate(self): + self._validate_mandatory() + self._validate_data_fields() + self._validate_selects() + self._validate_non_negative() + self._validate_length() + self._fix_rating_value() + self._validate_code_fields() + self._sync_autoname_field() + self._extract_images_from_text_editor() + self._sanitize_content() + self._save_passwords() + self.validate_workflow() + + for d in self.get_all_children(): + d._validate_data_fields() + d._validate_selects() + d._validate_non_negative() + d._validate_length() + d._fix_rating_value() + d._validate_code_fields() + d._sync_autoname_field() + d._extract_images_from_text_editor() + d._sanitize_content() + d._save_passwords() + if self.is_new(): + # don't set fields like _assign, _comments for new doc + for fieldname in optional_fields: + self.set(fieldname, None) + else: + self.validate_set_only_once() + + def _validate_non_negative(self): + def get_msg(df): + if self.get("parentfield"): + return "{} {} #{}: {} {}".format( + xhiveframework.bold(_(self.doctype)), + _("Row"), + self.idx, + _("Value cannot be negative for"), + xhiveframework.bold(_(df.label, context=df.parent)), + ) + else: + return _("Value cannot be negative for {0}: {1}").format( + _(df.parent), xhiveframework.bold(_(df.label, context=df.parent)) + ) + + for df in self.meta.get( + "fields", {"non_negative": ("=", 1), "fieldtype": ("in", ["Int", "Float", "Currency"])} + ): + if flt(self.get(df.fieldname)) < 0: + msg = get_msg(df) + xhiveframework.throw(msg, xhiveframework.NonNegativeError, title=_("Negative Value")) + + def _fix_rating_value(self): + for field in self.meta.get("fields", {"fieldtype": "Rating"}): + value = self.get(field.fieldname) + if not isinstance(value, float): + value = flt(value) + + # Make sure rating is between 0 and 1 + self.set(field.fieldname, max(0, min(value, 1))) + + def validate_workflow(self): + """Validate if the workflow transition is valid""" + if xhiveframework.flags.in_install == "xhiveframework": + return + workflow = self.meta.get_workflow() + if workflow: + validate_workflow(self) + if not self._action == "save": + set_workflow_state_on_action(self, workflow, self._action) + + def validate_set_only_once(self): + """Validate that fields are not changed if not in insert""" + set_only_once_fields = self.meta.get_set_only_once_fields() + + if set_only_once_fields and self._doc_before_save: + # document exists before saving + for field in set_only_once_fields: + fail = False + value = self.get(field.fieldname) + original_value = self._doc_before_save.get(field.fieldname) + + if field.fieldtype in table_fields: + fail = not self.is_child_table_same(field.fieldname) + elif field.fieldtype in ("Date", "Datetime", "Time"): + fail = str(value) != str(original_value) + else: + fail = value != original_value + + if fail: + xhiveframework.throw( + _("Value cannot be changed for {0}").format( + xhiveframework.bold(self.meta.get_label(field.fieldname)) + ), + exc=xhiveframework.CannotChangeConstantError, + ) + + return False + + def is_child_table_same(self, fieldname): + """Validate child table is same as original table before saving""" + value = self.get(fieldname) + original_value = self._doc_before_save.get(fieldname) + same = True + + if len(original_value) != len(value): + same = False + else: + # check all child entries + for i, d in enumerate(original_value): + new_child = value[i].as_dict(convert_dates_to_str=True) + original_child = d.as_dict(convert_dates_to_str=True) + + # all fields must be same other than modified and modified_by + for key in ("modified", "modified_by", "creation"): + del new_child[key] + del original_child[key] + + if original_child != new_child: + same = False + break + + return same + + def apply_fieldlevel_read_permissions(self): + """Remove values the user is not allowed to read.""" + if xhiveframework.session.user == "Administrator": + return + + all_fields = self.meta.fields.copy() + for table_field in self.meta.get_table_fields(): + all_fields += xhiveframework.get_meta(table_field.options).fields or [] + + if all(df.permlevel == 0 for df in all_fields): + return + + has_access_to = self.get_permlevel_access("read") + + for df in self.meta.fields: + if df.permlevel and hasattr(self, df.fieldname) and df.permlevel not in has_access_to: + try: + delattr(self, df.fieldname) + except AttributeError: + # hasattr might return True for class attribute which can't be delattr-ed. + continue + + for table_field in self.meta.get_table_fields(): + for df in xhiveframework.get_meta(table_field.options).fields or []: + if df.permlevel and df.permlevel not in has_access_to: + for child in self.get(table_field.fieldname) or []: + if hasattr(child, df.fieldname): + delattr(child, df.fieldname) + + def validate_higher_perm_levels(self): + """If the user does not have permissions at permlevel > 0, then reset the values to original / default""" + if self.flags.ignore_permissions or xhiveframework.flags.in_install: + return + + if xhiveframework.session.user == "Administrator": + return + + has_access_to = self.get_permlevel_access() + high_permlevel_fields = self.meta.get_high_permlevel_fields() + + if high_permlevel_fields: + self.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields) + + # If new record then don't reset the values for child table + if self.is_new(): + return + + # check for child tables + for df in self.meta.get_table_fields(): + high_permlevel_fields = xhiveframework.get_meta(df.options).get_high_permlevel_fields() + if high_permlevel_fields: + for d in self.get(df.fieldname): + d.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields) + + def get_permlevel_access(self, permission_type="write"): + allowed_permlevels = [] + roles = xhiveframework.get_roles() + + for perm in self.get_permissions(): + if perm.role in roles and perm.get(permission_type) and perm.permlevel not in allowed_permlevels: + allowed_permlevels.append(perm.permlevel) + + return allowed_permlevels + + def has_permlevel_access_to(self, fieldname, df=None, permission_type="read"): + if not df: + df = self.meta.get_field(fieldname) + + return df.permlevel in self.get_permlevel_access(permission_type) + + def get_permissions(self): + if self.meta.istable: + # use parent permissions + permissions = xhiveframework.get_meta(self.parenttype).permissions + else: + permissions = self.meta.permissions + + return permissions + + def _set_defaults(self): + if xhiveframework.flags.in_import: + return + + if self.is_new(): + new_doc = xhiveframework.new_doc(self.doctype, as_dict=True) + self.update_if_missing(new_doc) + + # children + for df in self.meta.get_table_fields(): + new_doc = xhiveframework.new_doc(df.options, parent_doc=self, parentfield=df.fieldname, as_dict=True) + value = self.get(df.fieldname) + if isinstance(value, list): + for d in value: + if d.is_new(): + d.update_if_missing(new_doc) + + def check_if_latest(self): + """Checks if `modified` timestamp provided by document being updated is same as the + `modified` timestamp in the database. If there is a different, the document has been + updated in the database after the current copy was read. Will throw an error if + timestamps don't match. + + Will also validate document transitions (Save > Submit > Cancel) calling + `self.check_docstatus_transition`.""" + + self.load_doc_before_save(raise_exception=True) + + self._action = "save" + previous = self._doc_before_save + + # previous is None for new document insert + if not previous: + self.check_docstatus_transition(0) + return + + if cstr(previous.modified) != cstr(self._original_modified): + xhiveframework.msgprint( + _("Error: Document has been modified after you have opened it") + + (f" ({previous.modified}, {self.modified}). ") + + _("Please refresh to get the latest document."), + raise_exception=xhiveframework.TimestampMismatchError, + ) + + if not self.meta.issingle: + self.check_docstatus_transition(previous.docstatus) + + def check_docstatus_transition(self, to_docstatus): + """Ensures valid `docstatus` transition. + Valid transitions are (number in brackets is `docstatus`): + + - Save (0) > Save (0) + - Save (0) > Submit (1) + - Submit (1) > Submit (1) + - Submit (1) > Cancel (2) + + """ + if not self.docstatus: + self.docstatus = DocStatus.draft() + + if to_docstatus == DocStatus.draft(): + if self.docstatus.is_draft(): + self._action = "save" + elif self.docstatus.is_submitted(): + self._action = "submit" + self.check_permission("submit") + elif self.docstatus.is_cancelled(): + raise xhiveframework.DocstatusTransitionError( + _("Cannot change docstatus from 0 (Draft) to 2 (Cancelled)") + ) + else: + raise xhiveframework.ValidationError(_("Invalid docstatus"), self.docstatus) + + elif to_docstatus == DocStatus.submitted(): + if self.docstatus.is_submitted(): + self._action = "update_after_submit" + self.check_permission("submit") + elif self.docstatus.is_cancelled(): + self._action = "cancel" + self.check_permission("cancel") + elif self.docstatus.is_draft(): + raise xhiveframework.DocstatusTransitionError( + _("Cannot change docstatus from 1 (Submitted) to 0 (Draft)") + ) + else: + raise xhiveframework.ValidationError(_("Invalid docstatus"), self.docstatus) + + elif to_docstatus == DocStatus.cancelled(): + raise xhiveframework.ValidationError(_("Cannot edit cancelled document")) + + def set_parent_in_children(self): + """Updates `parent` and `parenttype` property in all children.""" + for d in self.get_all_children(): + d.parent = self.name + d.parenttype = self.doctype + + def set_name_in_children(self): + # Set name for any new children + for d in self.get_all_children(): + if not d.name: + set_new_name(d) + + def validate_update_after_submit(self): + if self.flags.ignore_validate_update_after_submit: + return + + self._validate_update_after_submit() + for d in self.get_all_children(): + if d.is_new() and self.meta.get_field(d.parentfield).allow_on_submit: + # in case of a new row, don't validate allow on submit, if table is allow on submit + continue + + d._validate_update_after_submit() + + # TODO check only allowed values are updated + + def _validate_mandatory(self): + if self.flags.ignore_mandatory: + return + + missing = self._get_missing_mandatory_fields() + for d in self.get_all_children(): + missing.extend(d._get_missing_mandatory_fields()) + + if not missing: + return + + for idx, msg in missing: # noqa: B007 + msgprint(msg) + + if xhiveframework.flags.print_messages: + print(self.as_json().encode("utf-8")) + + raise xhiveframework.MandatoryError( + "[{doctype}, {name}]: {fields}".format( + fields=", ".join(each[0] for each in missing), doctype=self.doctype, name=self.name + ) + ) + + def _validate_links(self): + if self.flags.ignore_links or self._action == "cancel": + return + + invalid_links, cancelled_links = self.get_invalid_links() + + for d in self.get_all_children(): + result = d.get_invalid_links(is_submittable=self.meta.is_submittable) + invalid_links.extend(result[0]) + cancelled_links.extend(result[1]) + + if invalid_links: + msg = ", ".join(each[2] for each in invalid_links) + xhiveframework.throw(_("Could not find {0}").format(msg), xhiveframework.LinkValidationError) + + if cancelled_links: + msg = ", ".join(each[2] for each in cancelled_links) + xhiveframework.throw(_("Cannot link cancelled document: {0}").format(msg), xhiveframework.CancelledLinkError) + + def get_all_children(self, parenttype=None) -> list["Document"]: + """Returns all children documents from **Table** type fields in a list.""" + + children = [] + + for df in self.meta.get_table_fields(): + if parenttype and df.options != parenttype: + continue + + if value := self.get(df.fieldname): + children.extend(value) + + return children + + def run_method(self, method, *args, **kwargs): + """run standard triggers, plus those in hooks""" + + def fn(self, *args, **kwargs): + method_object = getattr(self, method, None) + + # Cannot have a field with same name as method + # If method found in __dict__, expect it to be callable + if method in self.__dict__ or callable(method_object): + return method_object(*args, **kwargs) + + fn.__name__ = str(method) + out = Document.hook(fn)(self, *args, **kwargs) + + self.run_notifications(method) + run_webhooks(self, method) + run_server_script_for_doc_event(self, method) + + return out + + def run_trigger(self, method, *args, **kwargs): + return self.run_method(method, *args, **kwargs) + + def run_notifications(self, method): + """Run notifications for this method""" + if ( + (xhiveframework.flags.in_import and xhiveframework.flags.mute_emails) + or xhiveframework.flags.in_patch + or xhiveframework.flags.in_install + ): + return + + if self.flags.notifications_executed is None: + self.flags.notifications_executed = [] + + from xhiveframework.email.doctype.notification.notification import evaluate_alert + + if self.flags.notifications is None: + + def _get_notifications(): + """returns enabled notifications for the current doctype""" + + return xhiveframework.get_all( + "Notification", + fields=["name", "event", "method"], + filters={"enabled": 1, "document_type": self.doctype}, + ) + + self.flags.notifications = xhiveframework.cache.hget("notifications", self.doctype, _get_notifications) + + if not self.flags.notifications: + return + + def _evaluate_alert(alert): + if alert.name in self.flags.notifications_executed: + return + + evaluate_alert(self, alert.name, alert.event) + self.flags.notifications_executed.append(alert.name) + + event_map = { + "on_update": "Save", + "after_insert": "New", + "on_submit": "Submit", + "on_cancel": "Cancel", + } + + if not self.flags.in_insert: + # value change is not applicable in insert + event_map["on_change"] = "Value Change" + + for alert in self.flags.notifications: + event = event_map.get(method, None) + if event and alert.event == event: + _evaluate_alert(alert) + elif alert.event == "Method" and method == alert.method: + _evaluate_alert(alert) + + def _submit(self): + """Submit the document. Sets `docstatus` = 1, then saves.""" + self.docstatus = DocStatus.submitted() + return self.save() + + def _cancel(self): + """Cancel the document. Sets `docstatus` = 2, then saves.""" + self.docstatus = DocStatus.cancelled() + return self.save() + + def _rename(self, name: str, merge: bool = False, force: bool = False, validate_rename: bool = True): + """Rename the document. Triggers xhiveframework.rename_doc, then reloads.""" + from xhiveframework.model.rename_doc import rename_doc + + self.name = rename_doc(doc=self, new=name, merge=merge, force=force, validate=validate_rename) + self.reload() + + @xhiveframework.whitelist() + def submit(self): + """Submit the document. Sets `docstatus` = 1, then saves.""" + return self._submit() + + @xhiveframework.whitelist() + def cancel(self): + """Cancel the document. Sets `docstatus` = 2, then saves.""" + return self._cancel() + + @xhiveframework.whitelist() + def rename(self, name: str, merge=False, force=False, validate_rename=True): + """Rename the document to `name`. This transforms the current object.""" + return self._rename(name=name, merge=merge, force=force, validate_rename=validate_rename) + + def delete(self, ignore_permissions=False, force=False, *, delete_permanently=False): + """Delete document.""" + return xhiveframework.delete_doc( + self.doctype, + self.name, + ignore_permissions=ignore_permissions, + flags=self.flags, + force=force, + delete_permanently=delete_permanently, + ) + + def run_before_save_methods(self): + """Run standard methods before `INSERT` or `UPDATE`. Standard Methods are: + + - `validate`, `before_save` for **Save**. + - `validate`, `before_submit` for **Submit**. + - `before_cancel` for **Cancel** + - `before_update_after_submit` for **Update after Submit** + + Will also update title_field if set""" + + self.reset_seen() + + # before_validate method should be executed before ignoring validations + if self._action in ("save", "submit"): + self.run_method("before_validate") + + if self.flags.ignore_validate: + return + + if self._action == "save": + self.run_method("validate") + self.run_method("before_save") + elif self._action == "submit": + self.run_method("validate") + self.run_method("before_submit") + elif self._action == "cancel": + self.run_method("before_cancel") + elif self._action == "update_after_submit": + self.run_method("before_update_after_submit") + + self.set_title_field() + + def load_doc_before_save(self, *, raise_exception: bool = False): + """load existing document from db before saving""" + + self._doc_before_save = None + + if self.is_new(): + return + + try: + self._doc_before_save = xhiveframework.get_doc(self.doctype, self.name, for_update=True) + except xhiveframework.DoesNotExistError: + if raise_exception: + raise + + xhiveframework.clear_last_message() + + def run_post_save_methods(self): + """Run standard methods after `INSERT` or `UPDATE`. Standard Methods are: + + - `on_update` for **Save**. + - `on_update`, `on_submit` for **Submit**. + - `on_cancel` for **Cancel** + - `update_after_submit` for **Update after Submit**""" + + if self._action == "save": + self.run_method("on_update") + elif self._action == "submit": + self.run_method("on_update") + self.run_method("on_submit") + elif self._action == "cancel": + self.run_method("on_cancel") + self.check_no_back_links_exist() + elif self._action == "update_after_submit": + self.run_method("on_update_after_submit") + + self.clear_cache() + + if self.flags.get("notify_update", True): + self.notify_update() + + update_global_search(self) + + self.save_version() + + self.run_method("on_change") + + if (self.doctype, self.name) in xhiveframework.flags.currently_saving: + xhiveframework.flags.currently_saving.remove((self.doctype, self.name)) + + def clear_cache(self): + xhiveframework.clear_document_cache(self.doctype, self.name) + + def reset_seen(self): + """Clear _seen property and set current user as seen""" + if ( + getattr(self.meta, "track_seen", False) + and not getattr(self.meta, "issingle", False) + and not self.is_new() + ): + xhiveframework.db.set_value( + self.doctype, self.name, "_seen", json.dumps([xhiveframework.session.user]), update_modified=False + ) + + def notify_update(self): + """Publish realtime that the current document is modified""" + if xhiveframework.flags.in_patch: + return + + xhiveframework.publish_realtime( + "doc_update", + {"modified": self.modified, "doctype": self.doctype, "name": self.name}, + doctype=self.doctype, + docname=self.name, + after_commit=True, + ) + + if not self.meta.get("read_only") and not self.meta.get("issingle") and not self.meta.get("istable"): + data = {"doctype": self.doctype, "name": self.name, "user": xhiveframework.session.user} + xhiveframework.publish_realtime("list_update", data, after_commit=True) + + def db_set(self, fieldname, value=None, update_modified=True, notify=False, commit=False): + """Set a value in the document object, update the timestamp and update the database. + + WARNING: This method does not trigger controller validations and should + be used very carefully. + + :param fieldname: fieldname of the property to be updated, or a {"field":"value"} dictionary + :param value: value of the property to be updated + :param update_modified: default True. updates the `modified` and `modified_by` properties + :param notify: default False. run doc.notify_update() to send updates via socketio + :param commit: default False. run xhiveframework.db.commit() + """ + if isinstance(fieldname, dict): + self.update(fieldname) + else: + self.set(fieldname, value) + + if update_modified and (self.doctype, self.name) not in xhiveframework.flags.currently_saving: + # don't update modified timestamp if called from post save methods + # like on_update or on_submit + self.set("modified", now()) + self.set("modified_by", xhiveframework.session.user) + + # load but do not reload doc_before_save because before_change or on_change might expect it + if not self.get_doc_before_save(): + self.load_doc_before_save() + + # to trigger notification on value change + self.run_method("before_change") + + if self.name is None: + return + + if self.meta.issingle: + xhiveframework.db.set_single_value( + self.doctype, + fieldname, + value, + modified=self.modified, + modified_by=self.modified_by, + update_modified=update_modified, + ) + else: + xhiveframework.db.set_value( + self.doctype, + self.name, + fieldname, + value, + self.modified, + self.modified_by, + update_modified=update_modified, + ) + + self.run_method("on_change") + + if notify: + self.notify_update() + + if commit: + xhiveframework.db.commit() + + def db_get(self, fieldname): + """get database value for this fieldname""" + return xhiveframework.db.get_value(self.doctype, self.name, fieldname) + + def check_no_back_links_exist(self): + """Check if document links to any active document before Cancel.""" + from xhiveframework.model.delete_doc import check_if_doc_is_dynamically_linked, check_if_doc_is_linked + + if not self.flags.ignore_links: + check_if_doc_is_linked(self, method="Cancel") + check_if_doc_is_dynamically_linked(self, method="Cancel") + + def save_version(self): + """Save version info""" + + # don't track version under following conditions + if ( + not getattr(self.meta, "track_changes", False) + or self.doctype == "Version" + or self.flags.ignore_version + or xhiveframework.flags.in_install + or (not self._doc_before_save and xhiveframework.flags.in_patch) + ): + return + + doc_to_compare = self._doc_before_save + if not doc_to_compare and (amended_from := self.get("amended_from")): + doc_to_compare = xhiveframework.get_doc(self.doctype, amended_from) + + version = xhiveframework.new_doc("Version") + if version.update_version_info(doc_to_compare, self): + version.insert(ignore_permissions=True) + + if not xhiveframework.flags.in_migrate: + # follow since you made a change? + if xhiveframework.get_cached_value("User", xhiveframework.session.user, "follow_created_documents"): + follow_document(self.doctype, self.name, xhiveframework.session.user) + + @staticmethod + def hook(f): + """Decorator: Make method `hookable` (i.e. extensible by another app). + + Note: If each hooked method returns a value (dict), then all returns are + collated in one dict and returned. Ideally, don't return values in hookable + methods, set properties in the document.""" + + def add_to_return_value(self, new_return_value): + if new_return_value is None: + self._return_value = self.get("_return_value") + return + + if isinstance(new_return_value, dict): + if not self.get("_return_value"): + self._return_value = {} + self._return_value.update(new_return_value) + else: + self._return_value = new_return_value + + def compose(fn, *hooks): + def runner(self, method, *args, **kwargs): + add_to_return_value(self, fn(self, *args, **kwargs)) + for f in hooks: + add_to_return_value(self, f(self, method, *args, **kwargs)) + + return self.__dict__.pop("_return_value", None) + + return runner + + def composer(self, *args, **kwargs): + hooks = [] + method = f.__name__ + doc_events = xhiveframework.get_doc_hooks() + for handler in doc_events.get(self.doctype, {}).get(method, []) + doc_events.get("*", {}).get( + method, [] + ): + hooks.append(xhiveframework.get_attr(handler)) + + composed = compose(f, *hooks) + return composed(self, method, *args, **kwargs) + + return composer + + def is_whitelisted(self, method_name): + method = getattr(self, method_name, None) + if not method: + raise NotFound(f"Method {method_name} not found") + + is_whitelisted(getattr(method, "__func__", method)) + + def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None): + """Check that value of fieldname should be 'condition' val2 + else throw Exception.""" + error_condition_map = { + "in": _("one of"), + "not in": _("none of"), + "^": _("beginning with"), + } + + if not doc: + doc = self + + val1 = doc.get_value(fieldname) + + df = doc.meta.get_field(fieldname) + val2 = doc.cast(val2, df) + + if not compare(val1, condition, val2): + label = doc.meta.get_label(fieldname) + condition_str = error_condition_map.get(condition, condition) + if doc.get("parentfield"): + msg = _("Incorrect value in row {0}: {1} must be {2} {3}").format( + doc.idx, label, condition_str, val2 + ) + else: + msg = _("Incorrect value: {0} must be {1} {2}").format(label, condition_str, val2) + + # raise passed exception or True + msgprint(msg, raise_exception=raise_exception or True) + + def validate_table_has_rows(self, parentfield, raise_exception=None): + """Raise exception if Table field is empty.""" + if not (isinstance(self.get(parentfield), list) and len(self.get(parentfield)) > 0): + label = self.meta.get_label(parentfield) + xhiveframework.throw( + _("Table {0} cannot be empty").format(label), raise_exception or xhiveframework.EmptyTableError + ) + + def round_floats_in(self, doc, fieldnames=None): + """Round floats for all `Currency`, `Float`, `Percent` fields for the given doc. + + :param doc: Document whose numeric properties are to be rounded. + :param fieldnames: [Optional] List of fields to be rounded.""" + if not fieldnames: + fieldnames = ( + df.fieldname + for df in doc.meta.get("fields", {"fieldtype": ["in", ["Currency", "Float", "Percent"]]}) + ) + + for fieldname in fieldnames: + doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.get("parentfield")))) + + def get_url(self): + """Returns Desk URL for this document.""" + return get_absolute_url(self.doctype, self.name) + + def add_comment( + self, + comment_type="Comment", + text=None, + comment_email=None, + comment_by=None, + ): + """Add a comment to this document. + + :param comment_type: e.g. `Comment`. See Communication for more info.""" + + return xhiveframework.get_doc( + { + "doctype": "Comment", + "comment_type": comment_type, + "comment_email": comment_email or xhiveframework.session.user, + "comment_by": comment_by, + "reference_doctype": self.doctype, + "reference_name": self.name, + "content": text or comment_type, + } + ).insert(ignore_permissions=True) + + def add_seen(self, user=None): + """add the given/current user to list of users who have seen this document (_seen)""" + if not user: + user = xhiveframework.session.user + + if self.meta.track_seen and not xhiveframework.flags.read_only and not self.meta.issingle: + _seen = self.get("_seen") or [] + _seen = xhiveframework.parse_json(_seen) + + if user not in _seen: + _seen.append(user) + xhiveframework.db.set_value( + self.doctype, self.name, "_seen", json.dumps(_seen), update_modified=False + ) + xhiveframework.local.flags.commit = True + + def add_viewed(self, user=None, force=False, unique_views=False): + """add log to communication when a user views a document""" + if not user: + user = xhiveframework.session.user + + if unique_views and xhiveframework.db.exists( + "View Log", {"reference_doctype": self.doctype, "reference_name": self.name, "viewed_by": user} + ): + return + + if (hasattr(self.meta, "track_views") and self.meta.track_views) or force: + view_log = xhiveframework.get_doc( + { + "doctype": "View Log", + "viewed_by": user, + "reference_doctype": self.doctype, + "reference_name": self.name, + } + ) + if xhiveframework.flags.read_only: + view_log.deferred_insert() + else: + view_log.insert(ignore_permissions=True) + xhiveframework.local.flags.commit = True + + return view_log + + def log_error(self, title=None, message=None): + """Helper function to create an Error Log""" + return xhiveframework.log_error( + message=message, title=title, reference_doctype=self.doctype, reference_name=self.name + ) + + def get_signature(self): + """Return signature (hash) for private URL.""" + return hashlib.sha224(f"{self.doctype}:{self.name}".encode(), usedforsecurity=False).hexdigest() + + def get_document_share_key(self, expires_on=None, no_expiry=False): + if no_expiry: + expires_on = None + + existing_key = xhiveframework.db.exists( + "Document Share Key", + { + "reference_doctype": self.doctype, + "reference_docname": self.name, + "expires_on": expires_on, + }, + ) + if existing_key: + doc = xhiveframework.get_doc("Document Share Key", existing_key) + else: + doc = xhiveframework.new_doc("Document Share Key") + doc.reference_doctype = self.doctype + doc.reference_docname = self.name + doc.expires_on = expires_on + doc.flags.no_expiry = no_expiry + doc.insert(ignore_permissions=True) + + return doc.key + + def get_liked_by(self): + liked_by = getattr(self, "_liked_by", None) + if liked_by: + return json.loads(liked_by) + else: + return [] + + def set_onload(self, key, value): + if not self.get("__onload"): + self.set("__onload", xhiveframework._dict()) + self.get("__onload")[key] = value + + def get_onload(self, key=None): + if not key: + return self.get("__onload", xhiveframework._dict()) + + return self.get("__onload")[key] + + def queue_action(self, action, **kwargs): + """Run an action in background. If the action has an inner function, + like _submit for submit, it will call that instead""" + # call _submit instead of submit, so you can override submit to call + # run_delayed based on some action + # See: Stock Reconciliation + from xhiveframework.utils.background_jobs import enqueue + + if hasattr(self, f"_{action}"): + action = f"_{action}" + + try: + self.lock() + except xhiveframework.DocumentLockedError: + # Allow unlocking if created more than 60 minutes ago + primary_action = None + if file_lock.lock_age(self.get_signature()) > DOCUMENT_LOCK_SOFT_EXPIRY: + primary_action = { + "label": "Force Unlock", + "server_action": "xhiveframework.model.document.unlock_document", + "hide_on_success": True, + "args": { + "doctype": self.doctype, + "name": self.name, + }, + } + + xhiveframework.throw( + _( + "This document is currently locked and queued for execution. Please try again after some time." + ), + title=_("Document Queued"), + primary_action=primary_action, + ) + + enqueue_after_commit = kwargs.pop("enqueue_after_commit", None) + if enqueue_after_commit is None: + enqueue_after_commit = True + + return enqueue( + "xhiveframework.model.document.execute_action", + __doctype=self.doctype, + __name=self.name, + __action=action, + enqueue_after_commit=enqueue_after_commit, + **kwargs, + ) + + def lock(self, timeout=None): + """Creates a lock file for the given document. If timeout is set, + it will retry every 1 second for acquiring the lock again + + :param timeout: Timeout in seconds, default 0""" + signature = self.get_signature() + if file_lock.lock_exists(signature): + lock_exists = True + if file_lock.lock_age(signature) > DOCUMENT_LOCK_EXPIRTY: + file_lock.delete_lock(signature) + lock_exists = False + if timeout: + for _ in range(timeout): + time.sleep(1) + if not file_lock.lock_exists(signature): + lock_exists = False + break + if lock_exists: + raise xhiveframework.DocumentLockedError + file_lock.create_lock(signature) + xhiveframework.local.locked_documents.append(self) + + def unlock(self): + """Delete the lock file for this document""" + file_lock.delete_lock(self.get_signature()) + if self in xhiveframework.local.locked_documents: + xhiveframework.local.locked_documents.remove(self) + + def validate_from_to_dates(self, from_date_field: str, to_date_field: str) -> None: + """Validate that the value of `from_date_field` is not later than the value of `to_date_field`.""" + from_date = self.get(from_date_field) + to_date = self.get(to_date_field) + if not (from_date and to_date): + return + + if date_diff(to_date, from_date) < 0: + xhiveframework.throw( + _("{0} must be after {1}").format( + xhiveframework.bold(_(self.meta.get_label(to_date_field))), + xhiveframework.bold(_(self.meta.get_label(from_date_field))), + ), + xhiveframework.exceptions.InvalidDates, + ) + + def get_assigned_users(self): + assigned_users = xhiveframework.get_all( + "ToDo", + fields=["allocated_to"], + filters={ + "reference_type": self.doctype, + "reference_name": self.name, + "status": ("!=", "Cancelled"), + }, + pluck="allocated_to", + ) + + return set(assigned_users) + + def add_tag(self, tag): + """Add a Tag to this document""" + from xhiveframework.desk.doctype.tag.tag import DocTags + + DocTags(self.doctype).add(self.name, tag) + + def get_tags(self): + """Return a list of Tags attached to this document""" + from xhiveframework.desk.doctype.tag.tag import DocTags + + return DocTags(self.doctype).get_tags(self.name).split(",")[1:] + + def deferred_insert(self) -> None: + """Push the document to redis temporarily and insert later. + + WARN: This doesn't guarantee insertion as redis can be restarted + before data is flushed to database. + """ + + from xhiveframework.deferred_insert import deferred_insert + + self.set_user_and_timestamp() + + doc = self.get_valid_dict(convert_dates_to_str=True, ignore_virtual=True) + deferred_insert(doctype=self.doctype, records=doc) + + def __repr__(self): + name = self.name or "unsaved" + doctype = self.__class__.__name__ + + docstatus = f" docstatus={self.docstatus}" if self.docstatus else "" + parent = f" parent={self.parent}" if getattr(self, "parent", None) else "" + + return f"<{doctype}: {name}{docstatus}{parent}>" + + def __str__(self): + name = self.name or "unsaved" + doctype = self.__class__.__name__ + + return f"{doctype}({name})" + + +def execute_action(__doctype, __name, __action, **kwargs): + """Execute an action on a document (called by background worker)""" + doc = xhiveframework.get_doc(__doctype, __name) + doc.unlock() + try: + getattr(doc, __action)(**kwargs) + except Exception: + xhiveframework.db.rollback() + + # add a comment (?) + if xhiveframework.message_log: + msg = xhiveframework.message_log[-1].get("message") + else: + msg = "
      " + xhiveframework.get_traceback() + "
      " + + doc.add_comment("Comment", _("Action Failed") + "

      " + msg) + doc.notify_update() + + +def bulk_insert( + doctype: str, + documents: Iterable["Document"], + ignore_duplicates: bool = False, + chunk_size=10_000, +): + """Insert simple Documents objects to database in bulk. + + Warning/Info: + - All documents are inserted without triggering ANY hooks. + - This function assumes you've done the due dilligence and inserts in similar fashion as db_insert + - Documents can be any iterable / generator containing Document objects + """ + + doctype_meta = xhiveframework.get_meta(doctype) + documents = list(documents) + + valid_column_map = { + doctype: doctype_meta.get_valid_columns(), + } + values_map = { + doctype: _document_values_generator(documents, valid_column_map[doctype]), + } + + for child_table in doctype_meta.get_table_fields(): + valid_column_map[child_table.options] = xhiveframework.get_meta(child_table.options).get_valid_columns() + values_map[child_table.options] = _document_values_generator( + ( + ch_doc + for ch_doc in ( + child_docs for doc in documents for child_docs in doc.get(child_table.fieldname) + ) + ), + valid_column_map[child_table.options], + ) + + for dt, docs in values_map.items(): + xhiveframework.db.bulk_insert( + dt, valid_column_map[dt], docs, ignore_duplicates=ignore_duplicates, chunk_size=chunk_size + ) + + +def _document_values_generator( + documents: Iterable["Document"], + columns: list[str], +) -> Generator[tuple[Any], None, None]: + for doc in documents: + doc.creation = doc.modified = now() + doc.owner = doc.modified_by = xhiveframework.session.user + doc_values = doc.get_valid_dict( + convert_dates_to_str=True, + ignore_nulls=True, + ignore_virtual=True, + ) + yield tuple(doc_values.get(col) for col in columns) + + +@xhiveframework.whitelist() +def unlock_document(doctype: str | None = None, name: str | None = None, args=None): + if not doctype and not name and args: + # Backward compatibility + doctype = str(args["doctype"]) + name = str(args["name"]) + xhiveframework.get_doc(doctype, name).unlock() + xhiveframework.msgprint(xhiveframework._("Document Unlocked"), alert=True) diff --git a/xhiveframework/model/dynamic_links.py b/xhiveframework/model/dynamic_links.py new file mode 100644 index 0000000..ed3594c --- /dev/null +++ b/xhiveframework/model/dynamic_links.py @@ -0,0 +1,63 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + +# select doctypes that are accessed by the user (not read_only) first, so that the +# the validation message shows the user-facing doctype first. +# For example Journal Entry should be validated before GL Entry (which is an internal doctype) + +dynamic_link_queries = [ + """select `tabDocField`.parent, + `tabDocType`.read_only, `tabDocType`.in_create, + `tabDocField`.fieldname, `tabDocField`.options + from `tabDocField`, `tabDocType` + where `tabDocField`.fieldtype='Dynamic Link' and + `tabDocType`.`name`=`tabDocField`.parent and `tabDocType`.is_virtual = 0 + order by `tabDocType`.read_only, `tabDocType`.in_create""", + """select `tabCustom Field`.dt as parent, + `tabDocType`.read_only, `tabDocType`.in_create, + `tabCustom Field`.fieldname, `tabCustom Field`.options + from `tabCustom Field`, `tabDocType` + where `tabCustom Field`.fieldtype='Dynamic Link' and + `tabDocType`.`name`=`tabCustom Field`.dt + order by `tabDocType`.read_only, `tabDocType`.in_create""", +] + + +def get_dynamic_link_map(for_delete=False): + """Build a map of all dynamically linked tables. For example, + if Note is dynamically linked to ToDo, the function will return + `{"Note": ["ToDo"], "Sales Invoice": ["Journal Entry Detail"]}` + + Note: Will not map single doctypes + """ + if getattr(xhiveframework.local, "dynamic_link_map", None) is None or xhiveframework.flags.in_test: + # Build from scratch + dynamic_link_map = {} + for df in get_dynamic_links(): + meta = xhiveframework.get_meta(df.parent) + if meta.issingle: + # always check in Single DocTypes + dynamic_link_map.setdefault(meta.name, []).append(df) + else: + try: + links = xhiveframework.db.sql_list( + """select distinct {options} from `tab{parent}`""".format(**df) + ) + for doctype in links: + dynamic_link_map.setdefault(doctype, []).append(df) + except xhiveframework.db.TableMissingError: + pass + + xhiveframework.local.dynamic_link_map = dynamic_link_map + return xhiveframework.local.dynamic_link_map + + +def get_dynamic_links(): + """Return list of dynamic link fields as DocField. + Uses cache if possible""" + df = [] + for query in dynamic_link_queries: + df += xhiveframework.db.sql(query, as_dict=True) + return df diff --git a/xhiveframework/model/mapper.py b/xhiveframework/model/mapper.py new file mode 100644 index 0000000..7997ce9 --- /dev/null +++ b/xhiveframework/model/mapper.py @@ -0,0 +1,267 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import json + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import child_table_fields, default_fields, table_fields +from xhiveframework.utils import cstr + + +@xhiveframework.whitelist() +def make_mapped_doc(method, source_name, selected_children=None, args=None): + """Returns the mapped document calling the given mapper method. + Sets selected_children as flags for the `get_mapped_doc` method. + + Called from `open_mapped_doc` from create_new.js""" + + for hook in reversed(xhiveframework.get_hooks("override_whitelisted_methods", {}).get(method, [])): + # override using the first hook + method = hook + break + + method = xhiveframework.get_attr(method) + + if method not in xhiveframework.whitelisted: + raise xhiveframework.PermissionError + + if selected_children: + selected_children = json.loads(selected_children) + + if args: + xhiveframework.flags.args = xhiveframework._dict(json.loads(args)) + + xhiveframework.flags.selected_children = selected_children or None + + return method(source_name) + + +@xhiveframework.whitelist() +def map_docs(method, source_names, target_doc, args=None): + '''Returns the mapped document calling the given mapper method + with each of the given source docs on the target doc + + :param args: Args as string to pass to the mapper method + E.g. args: "{ 'supplier': 'XYZ' }"''' + + method = xhiveframework.get_attr(method) + if method not in xhiveframework.whitelisted: + raise xhiveframework.PermissionError + + for src in json.loads(source_names): + _args = (src, target_doc, json.loads(args)) if args else (src, target_doc) + target_doc = method(*_args) + return target_doc + + +def get_mapped_doc( + from_doctype, + from_docname, + table_maps, + target_doc=None, + postprocess=None, + ignore_permissions=False, + ignore_child_tables=False, + cached=False, +): + apply_strict_user_permissions = xhiveframework.get_system_settings("apply_strict_user_permissions") + + # main + if not target_doc: + target_doc = xhiveframework.new_doc(table_maps[from_doctype]["doctype"]) + elif isinstance(target_doc, str): + target_doc = xhiveframework.get_doc(json.loads(target_doc)) + + if ( + not apply_strict_user_permissions + and not ignore_permissions + and not target_doc.has_permission("create") + ): + target_doc.raise_no_permission_to("create") + + if cached: + source_doc = xhiveframework.get_cached_doc(from_doctype, from_docname) + else: + source_doc = xhiveframework.get_doc(from_doctype, from_docname) + + if not ignore_permissions: + if not source_doc.has_permission("read"): + source_doc.raise_no_permission_to("read") + + map_doc(source_doc, target_doc, table_maps[source_doc.doctype]) + + row_exists_for_parentfield = {} + + # children + if not ignore_child_tables: + for df in source_doc.meta.get_table_fields(): + source_child_doctype = df.options + table_map = table_maps.get(source_child_doctype) + + # if table_map isn't explicitly specified check if both source and target have the same fieldname and same table options and both of them don't have no_copy + if not table_map: + target_df = target_doc.meta.get_field(df.fieldname) + if target_df: + target_child_doctype = target_df.options + if ( + target_df + and target_child_doctype == source_child_doctype + and not df.no_copy + and not target_df.no_copy + ): + table_map = {"doctype": target_child_doctype} + + if table_map: + for source_d in source_doc.get(df.fieldname): + if "condition" in table_map: + if not table_map["condition"](source_d): + continue + + # if children are selected (checked from UI) for this table type, + # and this record is not in the selected children, then continue + if ( + xhiveframework.flags.selected_children + and (df.fieldname in xhiveframework.flags.selected_children) + and source_d.name not in xhiveframework.flags.selected_children[df.fieldname] + ): + continue + + target_child_doctype = table_map["doctype"] + target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype) + + # does row exist for a parentfield? + if target_parentfield not in row_exists_for_parentfield: + row_exists_for_parentfield[target_parentfield] = ( + True if target_doc.get(target_parentfield) else False + ) + + if table_map.get("ignore"): + continue + + if table_map.get("add_if_empty") and row_exists_for_parentfield.get(target_parentfield): + continue + + if table_map.get("filter") and table_map.get("filter")(source_d): + continue + + map_child_doc(source_d, target_doc, table_map, source_doc) + + if postprocess: + postprocess(source_doc, target_doc) + + target_doc.run_method("after_mapping", source_doc) + target_doc.set_onload("load_after_mapping", True) + + if apply_strict_user_permissions and not ignore_permissions and not target_doc.has_permission("create"): + target_doc.raise_no_permission_to("create") + + return target_doc + + +def map_doc(source_doc, target_doc, table_map, source_parent=None): + if table_map.get("validation"): + for key, condition in table_map["validation"].items(): + if condition[0] == "=" and source_doc.get(key) != condition[1]: + xhiveframework.throw( + _("Cannot map because following condition fails:") + f" {key}={cstr(condition[1])}" + ) + + map_fields(source_doc, target_doc, table_map, source_parent) + + if "postprocess" in table_map: + table_map["postprocess"](source_doc, target_doc, source_parent) + + +def map_fields(source_doc, target_doc, table_map, source_parent): + no_copy_fields = set( + [ + d.fieldname + for d in source_doc.meta.get("fields") + if (d.no_copy == 1 or d.fieldtype in table_fields) + ] + + [ + d.fieldname + for d in target_doc.meta.get("fields") + if (d.no_copy == 1 or d.fieldtype in table_fields) + ] + + list(default_fields) + + list(child_table_fields) + + list(table_map.get("field_no_map", [])) + ) + + for df in target_doc.meta.get("fields"): + if df.fieldname not in no_copy_fields: + # map same fields + val = source_doc.get(df.fieldname) + if val not in (None, ""): + target_doc.set(df.fieldname, val) + + elif df.fieldtype == "Link": + if not target_doc.get(df.fieldname): + # map link fields having options == source doctype + if df.options == source_doc.doctype: + target_doc.set(df.fieldname, source_doc.name) + + elif source_parent and df.options == source_parent.doctype: + target_doc.set(df.fieldname, source_parent.name) + + # map other fields + field_map = table_map.get("field_map") + + if field_map: + if isinstance(field_map, dict): + for source_key, target_key in field_map.items(): + val = source_doc.get(source_key) + if val not in (None, ""): + target_doc.set(target_key, val) + else: + for fmap in field_map: + val = source_doc.get(fmap[0]) + if val not in (None, ""): + target_doc.set(fmap[1], val) + + # map idx + if source_doc.idx: + target_doc.idx = source_doc.idx + + # add fetch + for df in target_doc.meta.get("fields", {"fieldtype": "Link"}): + if target_doc.get(df.fieldname): + map_fetch_fields(target_doc, df, no_copy_fields) + + +def map_fetch_fields(target_doc, df, no_copy_fields): + linked_doc = None + + # options should be like "link_fieldname.fieldname_in_liked_doc" + for fetch_df in target_doc.meta.get("fields", {"fetch_from": f"^{df.fieldname}."}): + if not (fetch_df.fieldtype == "Read Only" or fetch_df.read_only): + continue + + if ( + not target_doc.get(fetch_df.fieldname) or fetch_df.fieldtype == "Read Only" + ) and fetch_df.fieldname not in no_copy_fields: + source_fieldname = fetch_df.fetch_from.split(".")[1] + + if not linked_doc: + try: + linked_doc = xhiveframework.get_doc(df.options, target_doc.get(df.fieldname)) + except Exception: + return + + val = linked_doc.get(source_fieldname) + + if val not in (None, ""): + target_doc.set(fetch_df.fieldname, val) + + +def map_child_doc(source_d, target_parent, table_map, source_parent=None): + target_child_doctype = table_map["doctype"] + target_parentfield = target_parent.get_parentfield_of_doctype(target_child_doctype) + target_d = xhiveframework.new_doc(target_child_doctype, parent_doc=target_parent, parentfield=target_parentfield) + + map_doc(source_d, target_d, table_map, source_parent) + + target_d.idx = None + target_parent.append(target_parentfield, target_d) + return target_d diff --git a/xhiveframework/model/meta.py b/xhiveframework/model/meta.py new file mode 100644 index 0000000..36b69ff --- /dev/null +++ b/xhiveframework/model/meta.py @@ -0,0 +1,895 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +# metadata + +""" +Load metadata (DocType) class + +Example: + + meta = xhiveframework.get_meta('User') + if meta.has_field('first_name'): + print("DocType" table has field "first_name") + + +""" +import json +import os +from datetime import datetime + +import click + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import ( + child_table_fields, + data_fieldtypes, + default_fields, + no_value_fields, + optional_fields, + table_fields, +) +from xhiveframework.model.base_document import ( + DOCTYPE_TABLE_FIELDS, + TABLE_DOCTYPES_FOR_DOCTYPE, + BaseDocument, +) +from xhiveframework.model.document import Document +from xhiveframework.model.workflow import get_workflow_name +from xhiveframework.modules import load_doctype_module +from xhiveframework.utils import cast, cint, cstr + +DEFAULT_FIELD_LABELS = { + "name": lambda: _("ID"), + "creation": lambda: _("Created On"), + "docstatus": lambda: _("Document Status"), + "idx": lambda: _("Index"), + "modified": lambda: _("Last Updated On"), + "modified_by": lambda: _("Last Updated By"), + "owner": lambda: _("Created By"), + "_user_tags": lambda: _("Tags"), + "_liked_by": lambda: _("Liked By"), + "_comments": lambda: _("Comments"), + "_assign": lambda: _("Assigned To"), +} + + +def get_meta(doctype, cached=True) -> "Meta": + cached = cached and isinstance(doctype, str) + if cached and (meta := xhiveframework.cache.hget("doctype_meta", doctype)): + return meta + + meta = Meta(doctype) + xhiveframework.cache.hset("doctype_meta", meta.name, meta) + return meta + + +def load_meta(doctype): + return Meta(doctype) + + +def get_table_columns(doctype): + return xhiveframework.db.get_table_columns(doctype) + + +def load_doctype_from_file(doctype): + fname = xhiveframework.scrub(doctype) + with open(xhiveframework.get_app_path("xhiveframework", "core", "doctype", fname, fname + ".json")) as f: + txt = json.loads(f.read()) + + for d in txt.get("fields", []): + d["doctype"] = "DocField" + + for d in txt.get("permissions", []): + d["doctype"] = "DocPerm" + + txt["fields"] = [BaseDocument(d) for d in txt["fields"]] + if "permissions" in txt: + txt["permissions"] = [BaseDocument(d) for d in txt["permissions"]] + + return txt + + +class Meta(Document): + _metaclass = True + default_fields = list(default_fields)[1:] + special_doctypes = frozenset( + ( + "DocField", + "DocPerm", + "DocType", + "Module Def", + "DocType Action", + "DocType Link", + "DocType State", + ) + ) + standard_set_once_fields = ( + xhiveframework._dict(fieldname="creation", fieldtype="Datetime"), + xhiveframework._dict(fieldname="owner", fieldtype="Data"), + ) + + def __init__(self, doctype): + if isinstance(doctype, Document): + super().__init__(doctype.as_dict()) + else: + super().__init__("DocType", doctype) + + self.process() + + def load_from_db(self): + try: + super().load_from_db() + except xhiveframework.DoesNotExistError: + if self.doctype == "DocType" and self.name in self.special_doctypes: + self.__dict__.update(load_doctype_from_file(self.name)) + else: + raise + + def process(self): + # don't process for special doctypes + # prevents circular dependency + if self.name in self.special_doctypes: + self.init_field_caches() + return + + self.add_custom_fields() + self.apply_property_setters() + self.init_field_caches() + self.sort_fields() + self.get_valid_columns() + self.set_custom_permissions() + self.add_custom_links_and_actions() + + def as_dict(self, no_nulls=False): + def serialize(doc): + out = {} + for key, value in doc.__dict__.items(): + if isinstance(value, list | tuple): + if not value or not isinstance(value[0], BaseDocument): + # non standard list object, skip + continue + + value = [serialize(d) for d in value] + + if (not no_nulls and value is None) or isinstance( + value, str | int | float | datetime | list | tuple + ): + out[key] = value + + # set empty lists for unset table fields + for fieldname in TABLE_DOCTYPES_FOR_DOCTYPE.keys(): + if out.get(fieldname) is None: + out[fieldname] = [] + + return out + + return serialize(self) + + def get_link_fields(self): + return self.get("fields", {"fieldtype": "Link", "options": ["!=", "[Select]"]}) + + def get_data_fields(self): + return self.get("fields", {"fieldtype": "Data"}) + + def get_phone_fields(self): + return self.get("fields", {"fieldtype": "Phone"}) + + def get_dynamic_link_fields(self): + if not hasattr(self, "_dynamic_link_fields"): + self._dynamic_link_fields = self.get("fields", {"fieldtype": "Dynamic Link"}) + return self._dynamic_link_fields + + def get_select_fields(self): + return self.get("fields", {"fieldtype": "Select", "options": ["not in", ["[Select]", "Loading..."]]}) + + def get_image_fields(self): + return self.get("fields", {"fieldtype": "Attach Image"}) + + def get_code_fields(self): + return self.get("fields", {"fieldtype": "Code"}) + + def get_set_only_once_fields(self): + """Return fields with `set_only_once` set""" + if not hasattr(self, "_set_only_once_fields"): + self._set_only_once_fields = self.get("fields", {"set_only_once": 1}) + fieldnames = [d.fieldname for d in self._set_only_once_fields] + + for df in self.standard_set_once_fields: + if df.fieldname not in fieldnames: + self._set_only_once_fields.append(df) + + return self._set_only_once_fields + + def get_table_fields(self): + return self._table_fields + + def get_global_search_fields(self): + """Returns list of fields with `in_global_search` set and `name` if set""" + fields = self.get("fields", {"in_global_search": 1, "fieldtype": ["not in", no_value_fields]}) + if getattr(self, "show_name_in_global_search", None): + fields.append(xhiveframework._dict(fieldtype="Data", fieldname="name", label="Name")) + + return fields + + def get_valid_columns(self) -> list[str]: + if not hasattr(self, "_valid_columns"): + table_exists = xhiveframework.db.table_exists(self.name) + if self.name in self.special_doctypes and table_exists: + self._valid_columns = get_table_columns(self.name) + else: + self._valid_columns = self.default_fields + [ + df.fieldname for df in self.get("fields") if df.fieldtype in data_fieldtypes + ] + if self.istable: + self._valid_columns += list(child_table_fields) + + return self._valid_columns + + def get_table_field_doctype(self, fieldname): + return TABLE_DOCTYPES_FOR_DOCTYPE.get(fieldname) + + def get_field(self, fieldname): + """Return docfield from meta""" + + return self._fields.get(fieldname) + + def has_field(self, fieldname): + """Returns True if fieldname exists""" + + return fieldname in self._fields + + def get_label(self, fieldname): + """Get label of the given fieldname""" + if df := self.get_field(fieldname): + return df.get("label") + + if fieldname in DEFAULT_FIELD_LABELS: + return DEFAULT_FIELD_LABELS[fieldname]() + + return "No Label" + + def get_options(self, fieldname): + return self.get_field(fieldname).options + + def get_link_doctype(self, fieldname): + df = self.get_field(fieldname) + + if df.fieldtype == "Link": + return df.options + + if df.fieldtype == "Dynamic Link": + return self.get_options(df.options) + + def get_search_fields(self): + search_fields = self.search_fields or "name" + search_fields = [d.strip() for d in search_fields.split(",")] + if "name" not in search_fields: + search_fields.append("name") + + return search_fields + + def get_fields_to_fetch(self, link_fieldname=None): + """Returns a list of docfield objects for fields whose values + are to be fetched and updated for a particular link field + + These fields are of type Data, Link, Text, Readonly and their + fetch_from property is set as `link_fieldname`.`source_fieldname`""" + + out = [] + + if not link_fieldname: + link_fields = [df.fieldname for df in self.get_link_fields()] + + for df in self.fields: + if df.fieldtype not in no_value_fields and getattr(df, "fetch_from", None): + if link_fieldname: + if df.fetch_from.startswith(link_fieldname + "."): + out.append(df) + else: + if "." in df.fetch_from: + fieldname = df.fetch_from.split(".", 1)[0] + if fieldname in link_fields: + out.append(df) + + return out + + def get_list_fields(self): + list_fields = ["name"] + [ + d.fieldname for d in self.fields if (d.in_list_view and d.fieldtype in data_fieldtypes) + ] + if self.title_field and self.title_field not in list_fields: + list_fields.append(self.title_field) + return list_fields + + def get_custom_fields(self): + return [d for d in self.fields if getattr(d, "is_custom_field", False)] + + def get_title_field(self): + """Return the title field of this doctype, + explict via `title_field`, or `title` or `name`""" + title_field = getattr(self, "title_field", None) + if not title_field and self.has_field("title"): + title_field = "title" + if not title_field: + title_field = "name" + + return title_field + + def get_translatable_fields(self): + """Return all fields that are translation enabled""" + return [d.fieldname for d in self.fields if d.translatable] + + def is_translatable(self, fieldname): + """Return true of false given a field""" + + if field := self.get_field(fieldname): + return field.translatable + + def get_workflow(self): + return get_workflow_name(self.name) + + def get_naming_series_options(self) -> list[str]: + """Get list naming series options.""" + + if field := self.get_field("naming_series"): + options = field.options or "" + return options.split("\n") + + return [] + + def add_custom_fields(self): + if not xhiveframework.db.table_exists("Custom Field"): + return + + custom_fields = xhiveframework.db.get_values( + "Custom Field", + filters={"dt": self.name}, + fieldname="*", + as_dict=True, + order_by="idx", + update={"is_custom_field": 1}, + ) + + if not custom_fields: + return + + self.extend("fields", custom_fields) + + def apply_property_setters(self): + """ + Property Setters are set via Customize Form. They override standard properties + of the doctype or its child properties like fields, links etc. This method + applies the customized properties over the standard meta object + """ + if not xhiveframework.db.table_exists("Property Setter"): + return + + property_setters = xhiveframework.db.get_values( + "Property Setter", + filters={"doc_type": self.name}, + fieldname="*", + as_dict=True, + ) + + if not property_setters: + return + + for ps in property_setters: + if ps.doctype_or_field == "DocType": + self.set(ps.property, cast(ps.property_type, ps.value)) + + elif ps.doctype_or_field == "DocField": + for d in self.fields: + if d.fieldname == ps.field_name: + d.set(ps.property, cast(ps.property_type, ps.value)) + break + + elif ps.doctype_or_field == "DocType Link": + for d in self.links: + if d.name == ps.row_name: + d.set(ps.property, cast(ps.property_type, ps.value)) + break + + elif ps.doctype_or_field == "DocType Action": + for d in self.actions: + if d.name == ps.row_name: + d.set(ps.property, cast(ps.property_type, ps.value)) + break + + elif ps.doctype_or_field == "DocType State": + for d in self.states: + if d.name == ps.row_name: + d.set(ps.property, cast(ps.property_type, ps.value)) + break + + def add_custom_links_and_actions(self): + for doctype, fieldname in ( + ("DocType Link", "links"), + ("DocType Action", "actions"), + ("DocType State", "states"), + ): + # ignore_ddl because the `custom` column was added later via a patch + for d in xhiveframework.get_all( + doctype, fields="*", filters=dict(parent=self.name, custom=1), ignore_ddl=True + ): + self.append(fieldname, d) + + # set the fields in order if specified + # order is saved as `links_order` + order = json.loads(self.get(f"{fieldname}_order") or "[]") + if order: + name_map = {d.name: d for d in self.get(fieldname)} + new_list = [name_map[name] for name in order if name in name_map] + # add the missing items that have not be added + # maybe these items were added to the standard product + # after the customization was done + for d in self.get(fieldname): + if d not in new_list: + new_list.append(d) + + self.set(fieldname, new_list) + + def init_field_caches(self): + # field map + self._fields = {field.fieldname: field for field in self.fields} + + # table fields + if self.name == "DocType": + self._table_fields = DOCTYPE_TABLE_FIELDS + else: + self._table_fields = self.get("fields", {"fieldtype": ["in", table_fields]}) + + def sort_fields(self): + """ + Sort fields on the basis of following rules (priority descending): + - `field_order` property setter + - `insert_after` computed based on default order for standard fields + - `insert_after` property for custom fields + """ + + if field_order := getattr(self, "field_order", []): + field_order = [fieldname for fieldname in json.loads(field_order) if fieldname in self._fields] + + # all fields match, best case scenario + if len(field_order) == len(self.fields): + self._update_fields_based_on_order(field_order) + return + + # if the first few standard fields are not in the field order, prepare to prepend them + if self.fields[0].fieldname not in field_order: + fields_to_prepend = [] + standard_field_found = False + + for fieldname, field in self._fields.items(): + if getattr(field, "is_custom_field", False): + # all custom fields from here on + break + + if fieldname in field_order: + standard_field_found = True + break + + fields_to_prepend.append(fieldname) + + if standard_field_found: + field_order = fields_to_prepend + field_order + else: + # worst case scenario, invalidate field_order + field_order = fields_to_prepend + + existing_fields = set(field_order) if field_order else False + insert_after_map = {} + + for index, field in enumerate(self.fields): + if existing_fields and field.fieldname in existing_fields: + continue + + if not getattr(field, "is_custom_field", False): + if existing_fields: + # compute insert_after from previous field + insert_after_map.setdefault(self.fields[index - 1].fieldname, []).append(field.fieldname) + else: + field_order.append(field.fieldname) + + elif insert_after := getattr(field, "insert_after", None): + insert_after_map.setdefault(insert_after, []).append(field.fieldname) + + else: + # if custom field is at the top, insert after is None + field_order.insert(0, field.fieldname) + + if insert_after_map: + _update_field_order_based_on_insert_after(field_order, insert_after_map) + + self._update_fields_based_on_order(field_order) + + def _update_fields_based_on_order(self, field_order): + sorted_fields = [] + + for idx, fieldname in enumerate(field_order, 1): + field = self._fields[fieldname] + field.idx = idx + sorted_fields.append(field) + + self.fields = sorted_fields + + def set_custom_permissions(self): + """Reset `permissions` with Custom DocPerm if exists""" + if xhiveframework.flags.in_patch or xhiveframework.flags.in_install: + return + + if not self.istable and self.name not in ("DocType", "DocField", "DocPerm", "Custom DocPerm"): + custom_perms = xhiveframework.get_all( + "Custom DocPerm", + fields="*", + filters=dict(parent=self.name), + update=dict(doctype="Custom DocPerm"), + ) + if custom_perms: + self.permissions = [Document(d) for d in custom_perms] + + def get_fieldnames_with_value(self, with_field_meta=False, with_virtual_fields=False): + def is_value_field(docfield): + return not ( + not with_virtual_fields + and docfield.get("is_virtual") + or docfield.fieldtype in no_value_fields + ) + + if with_field_meta: + return [df for df in self.fields if is_value_field(df)] + + return [df.fieldname for df in self.fields if is_value_field(df)] + + def get_fields_to_check_permissions(self, user_permission_doctypes): + fields = self.get( + "fields", + { + "fieldtype": "Link", + "parent": self.name, + "ignore_user_permissions": ("!=", 1), + "options": ("in", user_permission_doctypes), + }, + ) + + if self.name in user_permission_doctypes: + fields.append(xhiveframework._dict({"label": "Name", "fieldname": "name", "options": self.name})) + + return fields + + def get_high_permlevel_fields(self): + """Build list of fields with high perm level and all the higher perm levels defined.""" + if not hasattr(self, "high_permlevel_fields"): + self.high_permlevel_fields = [df for df in self.fields if df.permlevel > 0] + return self.high_permlevel_fields + + def get_permitted_fieldnames( + self, + parenttype=None, + *, + user=None, + permission_type="read", + with_virtual_fields=True, + ): + """Build list of `fieldname` with read perm level and all the higher perm levels defined. + + Note: If permissions are not defined for DocType, return all the fields with value. + """ + permitted_fieldnames = [] + + if self.istable and not parenttype: + return permitted_fieldnames + + if not permission_type: + permission_type = "select" if xhiveframework.only_has_select_perm(self.name, user=user) else "read" + + if permission_type == "select": + return self.get_search_fields() + + if not self.get_permissions(parenttype=parenttype): + return self.get_fieldnames_with_value() + + permlevel_access = set( + self.get_permlevel_access(permission_type=permission_type, parenttype=parenttype, user=user) + ) + + if 0 not in permlevel_access and permission_type in ("read", "select"): + if xhiveframework.share.get_shared(self.name, user, rights=[permission_type], limit=1): + permlevel_access.add(0) + + permitted_fieldnames.extend( + df.fieldname + for df in self.get_fieldnames_with_value( + with_field_meta=True, with_virtual_fields=with_virtual_fields + ) + if df.permlevel in permlevel_access + ) + return permitted_fieldnames + + def get_permlevel_access(self, permission_type="read", parenttype=None, *, user=None): + has_access_to = [] + roles = xhiveframework.get_roles(user) + for perm in self.get_permissions(parenttype): + if perm.role in roles and perm.get(permission_type): + if perm.permlevel not in has_access_to: + has_access_to.append(perm.permlevel) + + return has_access_to + + def get_permissions(self, parenttype=None): + if self.istable and parenttype: + # use parent permissions + permissions = xhiveframework.get_meta(parenttype).permissions + else: + permissions = self.get("permissions", []) + + return permissions + + def get_dashboard_data(self): + """Returns dashboard setup related to this doctype. + + This method will return the `data` property in the `[doctype]_dashboard.py` + file in the doctype's folder, along with any overrides or extensions + implemented in other XhiveFramework applications via hooks. + """ + data = xhiveframework._dict() + if not self.custom: + try: + module = load_doctype_module(self.name, suffix="_dashboard") + if hasattr(module, "get_data"): + data = xhiveframework._dict(module.get_data()) + except ImportError: + pass + + self.add_doctype_links(data) + + if not self.custom: + for hook in xhiveframework.get_hooks("override_doctype_dashboards", {}).get(self.name, []): + data = xhiveframework._dict(xhiveframework.get_attr(hook)(data=data)) + + return data + + def add_doctype_links(self, data): + """add `links` child table in standard link dashboard format""" + dashboard_links = [] + + if getattr(self, "links", None): + dashboard_links.extend(self.links) + + if not data.transactions: + # init groups + data.transactions = [] + + if not data.non_standard_fieldnames: + data.non_standard_fieldnames = {} + + if not data.internal_links: + data.internal_links = {} + + for link in dashboard_links: + link.added = False + if link.hidden: + continue + + for group in data.transactions: + group = xhiveframework._dict(group) + + # For internal links parent doctype will be the key + doctype = link.parent_doctype or link.link_doctype + # group found + if link.group and _(group.label) == _(link.group): + if doctype not in group.get("items"): + group.get("items").append(doctype) + link.added = True + + if not link.added: + # group not found, make a new group + data.transactions.append( + dict(label=link.group, items=[link.parent_doctype or link.link_doctype]) + ) + + if not data.fieldname and link.link_fieldname: + data.fieldname = link.link_fieldname + + if not link.is_child_table: + data.non_standard_fieldnames[link.link_doctype] = link.link_fieldname + elif link.is_child_table: + data.internal_links[link.parent_doctype] = [link.table_fieldname, link.link_fieldname] + + def get_row_template(self): + return self.get_web_template(suffix="_row") + + def get_list_template(self): + return self.get_web_template(suffix="_list") + + def get_web_template(self, suffix=""): + """Returns the relative path of the row template for this doctype""" + module_name = xhiveframework.scrub(self.module) + doctype = xhiveframework.scrub(self.name) + template_path = xhiveframework.get_module_path( + module_name, "doctype", doctype, "templates", doctype + suffix + ".html" + ) + if os.path.exists(template_path): + return f"{module_name}/doctype/{doctype}/templates/{doctype}{suffix}.html" + return None + + def is_nested_set(self): + return self.has_field("lft") and self.has_field("rgt") + + +####### + + +def is_single(doctype): + try: + return xhiveframework.db.get_value("DocType", doctype, "issingle") + except IndexError: + raise Exception("Cannot determine whether %s is single" % doctype) + + +def get_parent_dt(dt): + if not xhiveframework.is_table(dt): + return "" + + return ( + xhiveframework.db.get_value( + "DocField", + {"fieldtype": ("in", xhiveframework.model.table_fields), "options": dt}, + "parent", + ) + or "" + ) + + +def set_fieldname(field_id, fieldname): + xhiveframework.db.set_value("DocField", field_id, "fieldname", fieldname) + + +def get_field_currency(df, doc=None): + """get currency based on DocField options and fieldvalue in doc""" + currency = None + + if not df.get("options"): + return None + + if not doc: + return None + + if not getattr(xhiveframework.local, "field_currency", None): + xhiveframework.local.field_currency = xhiveframework._dict() + + if not ( + xhiveframework.local.field_currency.get((doc.doctype, doc.name), {}).get(df.fieldname) + or ( + doc.get("parent") + and xhiveframework.local.field_currency.get((doc.doctype, doc.parent), {}).get(df.fieldname) + ) + ): + ref_docname = doc.get("parent") or doc.name + + if ":" in cstr(df.get("options")): + split_opts = df.get("options").split(":") + if len(split_opts) == 3 and doc.get(split_opts[1]): + currency = xhiveframework.get_cached_value(split_opts[0], doc.get(split_opts[1]), split_opts[2]) + else: + currency = doc.get(df.get("options")) + if doc.get("parenttype"): + if currency: + ref_docname = doc.name + else: + if xhiveframework.get_meta(doc.parenttype).has_field(df.get("options")): + # only get_value if parent has currency field + currency = xhiveframework.db.get_value(doc.parenttype, doc.parent, df.get("options")) + + if currency: + xhiveframework.local.field_currency.setdefault((doc.doctype, ref_docname), xhiveframework._dict()).setdefault( + df.fieldname, currency + ) + + return xhiveframework.local.field_currency.get((doc.doctype, doc.name), {}).get(df.fieldname) or ( + doc.get("parent") and xhiveframework.local.field_currency.get((doc.doctype, doc.parent), {}).get(df.fieldname) + ) + + +def get_field_precision(df, doc=None, currency=None): + """get precision based on DocField options and fieldvalue in doc""" + from xhiveframework.utils import get_number_format_info + + if df.precision: + precision = cint(df.precision) + + elif df.fieldtype == "Currency": + precision = cint(xhiveframework.db.get_default("currency_precision")) + if not precision: + number_format = xhiveframework.db.get_default("number_format") or "#,###.##" + decimal_str, comma_str, precision = get_number_format_info(number_format) + else: + precision = cint(xhiveframework.db.get_default("float_precision")) or 3 + + return precision + + +def get_default_df(fieldname): + if fieldname in (default_fields + child_table_fields): + if fieldname in ("creation", "modified"): + return xhiveframework._dict(fieldname=fieldname, fieldtype="Datetime") + + elif fieldname in ("idx", "docstatus"): + return xhiveframework._dict(fieldname=fieldname, fieldtype="Int") + + return xhiveframework._dict(fieldname=fieldname, fieldtype="Data") + + +def trim_tables(doctype=None, dry_run=False, quiet=False): + """ + Removes database fields that don't exist in the doctype (json or custom field). This may be needed + as maintenance since removing a field in a DocType doesn't automatically + delete the db field. + """ + UPDATED_TABLES = {} + filters = {"issingle": 0, "is_virtual": 0} + if doctype: + filters["name"] = doctype + + for doctype in xhiveframework.get_all("DocType", filters=filters, pluck="name"): + try: + dropped_columns = trim_table(doctype, dry_run=dry_run) + if dropped_columns: + UPDATED_TABLES[doctype] = dropped_columns + except xhiveframework.db.TableMissingError: + if quiet: + continue + click.secho(f"Ignoring missing table for DocType: {doctype}", fg="yellow", err=True) + click.secho(f"Consider removing record in the DocType table for {doctype}", fg="yellow", err=True) + except Exception as e: + if quiet: + continue + click.echo(e, err=True) + + return UPDATED_TABLES + + +def trim_table(doctype, dry_run=True): + xhiveframework.cache.hdel("table_columns", f"tab{doctype}") + ignore_fields = default_fields + optional_fields + child_table_fields + columns = xhiveframework.db.get_table_columns(doctype) + fields = xhiveframework.get_meta(doctype, cached=False).get_fieldnames_with_value() + + def is_internal(field): + return field not in ignore_fields and not field.startswith("_") + + columns_to_remove = [f for f in list(set(columns) - set(fields)) if is_internal(f)] + DROPPED_COLUMNS = columns_to_remove[:] + + if columns_to_remove and not dry_run: + columns_to_remove = ", ".join(f"DROP `{c}`" for c in columns_to_remove) + xhiveframework.db.sql_ddl(f"ALTER TABLE `tab{doctype}` {columns_to_remove}") + + return DROPPED_COLUMNS + + +def _update_field_order_based_on_insert_after(field_order, insert_after_map): + """Update the field order based on insert_after_map""" + + retry_field_insertion = True + + while retry_field_insertion: + retry_field_insertion = False + + for fieldname in list(insert_after_map): + if fieldname not in field_order: + continue + + custom_field_index = field_order.index(fieldname) + for custom_field_name in insert_after_map.pop(fieldname): + custom_field_index += 1 + field_order.insert(custom_field_index, custom_field_name) + + retry_field_insertion = True + + if insert_after_map: + # insert_after is an invalid fieldname, add these fields to the end + for fields in insert_after_map.values(): + field_order.extend(fields) diff --git a/xhiveframework/model/naming.py b/xhiveframework/model/naming.py new file mode 100644 index 0000000..2268f25 --- /dev/null +++ b/xhiveframework/model/naming.py @@ -0,0 +1,544 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import datetime +import re +from collections.abc import Callable +from typing import TYPE_CHECKING, Optional + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model import log_types +from xhiveframework.query_builder import DocType +from xhiveframework.utils import cint, cstr, now_datetime + +if TYPE_CHECKING: + from xhiveframework.model.document import Document + from xhiveframework.model.meta import Meta + + +NAMING_SERIES_PATTERN = re.compile(r"^[\w\- \/.#{}]+$", re.UNICODE) +BRACED_PARAMS_PATTERN = re.compile(r"(\{[\w | #]+\})") + + +# Types that can be using in naming series fields +NAMING_SERIES_PART_TYPES = ( + int, + str, + datetime.datetime, + datetime.date, + datetime.time, + datetime.timedelta, +) + + +class InvalidNamingSeriesError(xhiveframework.ValidationError): + pass + + +class NamingSeries: + __slots__ = ("series",) + + def __init__(self, series: str): + self.series = series + + # Add default number part if missing + if "#" not in self.series: + self.series += ".#####" + + def validate(self): + if "." not in self.series: + xhiveframework.throw( + _("Invalid naming series {}: dot (.) missing").format(xhiveframework.bold(self.series)), + exc=InvalidNamingSeriesError, + ) + + if not NAMING_SERIES_PATTERN.match(self.series): + xhiveframework.throw( + _( + "Special Characters except '-', '#', '.', '/', '{{' and '}}' not allowed in naming series {0}" + ).format(xhiveframework.bold(self.series)), + exc=InvalidNamingSeriesError, + ) + + def generate_next_name(self, doc: "Document", *, ignore_validate=False) -> str: + if not ignore_validate: + self.validate() + + parts = self.series.split(".") + return parse_naming_series(parts, doc=doc) + + def get_prefix(self) -> str: + """Naming series stores prefix to maintain a counter in DB. This prefix can be used to update counter or validations. + + e.g. `SINV-.YY.-.####` has prefix of `SINV-22-` in database for year 2022. + """ + + prefix = None + + def fake_counter_backend(partial_series, digits): + nonlocal prefix + prefix = partial_series + return "#" * digits + + # This function evaluates all parts till we hit numerical parts and then + # sends prefix + digits to DB to find next number. + # Instead of reimplementing the whole parsing logic in multiple places we + # can just ask this function to give us the prefix. + parse_naming_series(self.series, number_generator=fake_counter_backend) + + if prefix is None: + xhiveframework.throw(_("Invalid Naming Series: {}").format(self.series)) + + return prefix + + def get_preview(self, doc=None) -> list[str]: + """Generate preview of naming series without using DB counters""" + generated_names = [] + for count in range(1, 4): + + def fake_counter(_prefix, digits): + # ignore B023: binding `count` is not necessary because + # function is evaluated immediately and it can not be done + # because of function signature requirement + return str(count).zfill(digits) + + generated_names.append(parse_naming_series(self.series, doc=doc, number_generator=fake_counter)) + return generated_names + + def update_counter(self, new_count: int) -> None: + """Warning: Incorrectly updating series can result in unusable transactions""" + Series = xhiveframework.qb.DocType("Series") + prefix = self.get_prefix() + + # Initialize if not present in DB + if xhiveframework.db.get_value("Series", prefix, "name", order_by="name") is None: + xhiveframework.qb.into(Series).insert(prefix, 0).columns("name", "current").run() + + (xhiveframework.qb.update(Series).set(Series.current, cint(new_count)).where(Series.name == prefix)).run() + + def get_current_value(self) -> int: + prefix = self.get_prefix() + return cint(xhiveframework.db.get_value("Series", prefix, "current", order_by="name")) + + +def set_new_name(doc): + """ + Sets the `name` property for the document based on various rules. + + 1. If amended doc, set suffix. + 2. If `autoname` method is declared, then call it. + 3. If `autoname` property is set in the DocType (`meta`), then build it using the `autoname` property. + 4. If no rule defined, use hash. + + :param doc: Document to be named. + """ + + doc.run_method("before_naming") + + meta = xhiveframework.get_meta(doc.doctype) + autoname = meta.autoname or "" + + if autoname.lower() != "prompt" and not xhiveframework.flags.in_import: + doc.name = None + + if is_autoincremented(doc.doctype, meta): + doc.name = xhiveframework.db.get_next_sequence_val(doc.doctype) + return + + if getattr(doc, "amended_from", None): + _set_amended_name(doc) + if doc.name: + return + + elif getattr(doc.meta, "issingle", False): + doc.name = doc.doctype + + if not doc.name: + set_naming_from_document_naming_rule(doc) + + if not doc.name: + doc.run_method("autoname") + + if not doc.name and autoname: + set_name_from_naming_options(autoname, doc) + + # at this point, we fall back to name generation with the hash option + if not doc.name: + doc.name = make_autoname("hash", doc.doctype) + + doc.name = validate_name(doc.doctype, doc.name) + + +def is_autoincremented(doctype: str, meta: Optional["Meta"] = None) -> bool: + """Checks if the doctype has autoincrement autoname set""" + + if not meta: + meta = xhiveframework.get_meta(doctype) + + if not getattr(meta, "issingle", False) and meta.autoname == "autoincrement": + return True + + return False + + +def set_name_from_naming_options(autoname, doc): + """ + Get a name based on the autoname field option + """ + + _autoname = autoname.lower() + + if _autoname.startswith("field:"): + doc.name = _field_autoname(autoname, doc) + + # if the autoname option is 'field:' and no name was derived, we need to + # notify + if not doc.name: + fieldname = autoname[6:] + xhiveframework.throw(_("{0} is required").format(doc.meta.get_label(fieldname))) + + elif _autoname.startswith("naming_series:"): + set_name_by_naming_series(doc) + elif _autoname.startswith("prompt"): + _prompt_autoname(autoname, doc) + elif _autoname.startswith("format:"): + doc.name = _format_autoname(autoname, doc) + elif "#" in autoname: + doc.name = make_autoname(autoname, doc=doc) + + +def set_naming_from_document_naming_rule(doc): + """ + Evaluate rules based on "Document Naming Series" doctype + """ + from xhiveframework.model.base_document import DOCTYPES_FOR_DOCTYPE + + IGNORED_DOCTYPES = {*log_types, *DOCTYPES_FOR_DOCTYPE, "DefaultValue", "Patch Log"} + + if doc.doctype in IGNORED_DOCTYPES: + return + + document_naming_rules = xhiveframework.cache_manager.get_doctype_map( + "Document Naming Rule", + doc.doctype, + filters={"document_type": doc.doctype, "disabled": 0}, + order_by="priority desc", + ) + + for d in document_naming_rules: + xhiveframework.get_cached_doc("Document Naming Rule", d.name).apply(doc) + if doc.name: + break + + +def set_name_by_naming_series(doc): + """Sets name by the `naming_series` property""" + if not doc.naming_series: + doc.naming_series = get_default_naming_series(doc.doctype) + + if not doc.naming_series: + xhiveframework.throw(xhiveframework._("Naming Series mandatory")) + + doc.name = make_autoname(doc.naming_series + ".#####", "", doc) + + +def make_autoname(key="", doctype="", doc="", *, ignore_validate=False): + """ + Creates an autoname from the given key: + + **Autoname rules:** + + * The key is separated by '.' + * '####' represents a series. The string before this part becomes the prefix: + Example: ABC.#### creates a series ABC0001, ABC0002 etc + * 'MM' represents the current month + * 'YY' and 'YYYY' represent the current year + + + *Example:* + + * DE./.YY./.MM./.##### will create a series like + DE/09/01/00001 where 09 is the year, 01 is the month and 00001 is the series + """ + if key == "hash": + return xhiveframework.generate_hash(length=10) + + series = NamingSeries(key) + return series.generate_next_name(doc, ignore_validate=ignore_validate) + + +def parse_naming_series( + parts: list[str] | str, + doctype=None, + doc: Optional["Document"] = None, + number_generator: Callable[[str, int], str] | None = None, +) -> str: + """Parse the naming series and get next name. + + args: + parts: naming series parts (split by `.`) + doc: document to use for series that have parts using fieldnames + number_generator: Use different counter backend other than `tabSeries`. Primarily used for testing. + """ + + name = "" + _sentinel = object() + if isinstance(parts, str): + parts = parts.split(".") + + if not number_generator: + number_generator = getseries + + series_set = False + today = now_datetime() + for e in parts: + if not e: + continue + + part = "" + if e.startswith("#"): + if not series_set: + digits = len(e) + part = number_generator(name, digits) + series_set = True + elif e == "YY": + part = today.strftime("%y") + elif e == "MM": + part = today.strftime("%m") + elif e == "DD": + part = today.strftime("%d") + elif e == "YYYY": + part = today.strftime("%Y") + elif e == "WW": + part = determine_consecutive_week_number(today) + elif e == "timestamp": + part = str(today) + elif doc and (e.startswith("{") or doc.get(e, _sentinel) is not _sentinel): + e = e.replace("{", "").replace("}", "") + part = doc.get(e) + elif method := has_custom_parser(e): + part = xhiveframework.get_attr(method[0])(doc, e) + else: + part = e + + if isinstance(part, str): + name += part + elif isinstance(part, NAMING_SERIES_PART_TYPES): + name += cstr(part).strip() + + return name + + +def has_custom_parser(e): + """Returns true if the naming series part has a custom parser""" + return xhiveframework.get_hooks("naming_series_variables", {}).get(e) + + +def determine_consecutive_week_number(datetime): + """Determines the consecutive calendar week""" + m = datetime.month + # ISO 8601 calandar week + w = datetime.strftime("%V") + # Ensure consecutiveness for the first and last days of a year + if m == 1 and int(w) >= 52: + w = "00" + elif m == 12 and int(w) <= 1: + w = "53" + return w + + +def getseries(key, digits): + # series created ? + # Using xhiveframework.qb as xhiveframework.get_values does not allow order_by=None + series = DocType("Series") + current = (xhiveframework.qb.from_(series).where(series.name == key).for_update().select("current")).run() + + if current and current[0][0] is not None: + current = current[0][0] + # yes, update it + xhiveframework.db.sql("UPDATE `tabSeries` SET `current` = `current` + 1 WHERE `name`=%s", (key,)) + current = cint(current) + 1 + else: + # no, create it + xhiveframework.db.sql("INSERT INTO `tabSeries` (`name`, `current`) VALUES (%s, 1)", (key,)) + current = 1 + return ("%0" + str(digits) + "d") % current + + +def revert_series_if_last(key, name, doc=None): + """ + Reverts the series for particular naming series: + * key is naming series - SINV-.YYYY-.#### + * name is actual name - SINV-2021-0001 + + 1. This function split the key into two parts prefix (SINV-YYYY) & hashes (####). + 2. Use prefix to get the current index of that naming series from Series table + 3. Then revert the current index. + + *For custom naming series:* + 1. hash can exist anywhere, if it exist in hashes then it take normal flow. + 2. If hash doesn't exit in hashes, we get the hash from prefix, then update name and prefix accordingly. + + *Example:* + 1. key = SINV-.YYYY.- + * If key doesn't have hash it will add hash at the end + * prefix will be SINV-YYYY based on this will get current index from Series table. + 2. key = SINV-.####.-2021 + * now prefix = SINV-#### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = SINV- + 3. key = ####.-2021 + * prefix = #### and hashes = 2021 (hash doesn't exist) + * will search hash in key then accordingly get prefix = "" + """ + if ".#" in key: + prefix, hashes = key.rsplit(".", 1) + if "#" not in hashes: + # get the hash part from the key + hash = re.search("#+", key) + if not hash: + return + name = name.replace(hashes, "") + prefix = prefix.replace(hash.group(), "") + else: + prefix = key + + if "." in prefix: + prefix = parse_naming_series(prefix.split("."), doc=doc) + + count = cint(name.replace(prefix, "")) + series = DocType("Series") + current = (xhiveframework.qb.from_(series).where(series.name == prefix).for_update().select("current")).run() + + if current and current[0][0] == count: + xhiveframework.db.sql("UPDATE `tabSeries` SET `current` = `current` - 1 WHERE `name`=%s", prefix) + + +def get_default_naming_series(doctype: str) -> str | None: + """get default value for `naming_series` property""" + naming_series_options = xhiveframework.get_meta(doctype).get_naming_series_options() + + # Return first truthy options + # Empty strings are used to avoid populating forms by default + for option in naming_series_options: + if option: + return option + + +def validate_name(doctype: str, name: int | str): + if not name: + xhiveframework.throw(_("No Name Specified for {0}").format(doctype)) + + if isinstance(name, int): + if is_autoincremented(doctype): + # this will set the sequence value to be the provided name/value and set it to be used + # so that the sequence will start from the next value + xhiveframework.db.set_next_sequence_val(doctype, name, is_val_used=True) + return name + + xhiveframework.throw(_("Invalid name type (integer) for varchar name column"), xhiveframework.NameError) + + if name.startswith("New " + doctype): + xhiveframework.throw( + _("There were some errors setting the name, please contact the administrator"), xhiveframework.NameError + ) + name = name.strip() + + if not xhiveframework.get_meta(doctype).get("issingle") and (doctype == name) and (name != "DocType"): + xhiveframework.throw(_("Name of {0} cannot be {1}").format(doctype, name), xhiveframework.NameError) + + special_characters = "<>" + if re.findall(f"[{special_characters}]+", name): + message = ", ".join(f"'{c}'" for c in special_characters) + xhiveframework.throw(_("Name cannot contain special characters like {0}").format(message), xhiveframework.NameError) + + return name + + +def append_number_if_name_exists(doctype, value, fieldname="name", separator="-", filters=None): + if not filters: + filters = dict() + filters.update({fieldname: value}) + exists = xhiveframework.db.exists(doctype, filters) + + regex = f"^{re.escape(value)}{separator}\\d+$" + + if exists: + last = xhiveframework.db.sql( + f"""SELECT `{fieldname}` FROM `tab{doctype}` + WHERE `{fieldname}` {xhiveframework.db.REGEX_CHARACTER} %s + ORDER BY length({fieldname}) DESC, + `{fieldname}` DESC LIMIT 1""", + regex, + ) + + if last: + count = str(cint(last[0][0].rsplit(separator, 1)[1]) + 1) + else: + count = "1" + + value = f"{value}{separator}{count}" + + return value + + +def _set_amended_name(doc): + amend_naming_rule = xhiveframework.db.get_value( + "Amended Document Naming Settings", {"document_type": doc.doctype}, "action", cache=True + ) + if not amend_naming_rule: + amend_naming_rule = xhiveframework.db.get_single_value( + "Document Naming Settings", "default_amend_naming", cache=True + ) + + if amend_naming_rule == "Default Naming": + return + + am_id = 1 + am_prefix = doc.amended_from + if xhiveframework.db.get_value(doc.doctype, doc.amended_from, "amended_from"): + am_id = cint(doc.amended_from.split("-")[-1]) + 1 + am_prefix = "-".join(doc.amended_from.split("-")[:-1]) # except the last hyphen + + doc.name = am_prefix + "-" + str(am_id) + return doc.name + + +def _field_autoname(autoname, doc, skip_slicing=None): + """ + Generate a name using `DocType` field. This is called when the doctype's + `autoname` field starts with 'field:' + """ + fieldname = autoname if skip_slicing else autoname[6:] + return (cstr(doc.get(fieldname)) or "").strip() + + +def _prompt_autoname(autoname, doc): + """ + Generate a name using Prompt option. This simply means the user will have to set the name manually. + This is called when the doctype's `autoname` field starts with 'prompt'. + """ + # set from __newname in save.py + if not doc.name: + xhiveframework.throw(_("Please set the document name")) + + +def _format_autoname(autoname: str, doc): + """ + Generate autoname by replacing all instances of braced params (fields, date params ('DD', 'MM', 'YY'), series) + Independent of remaining string or separators. + + Example pattern: 'format:LOG-{MM}-{fieldname1}-{fieldname2}-{#####}' + """ + + first_colon_index = autoname.find(":") + autoname_value = autoname[first_colon_index + 1 :] + + def get_param_value_for_match(match): + param = match.group() + return parse_naming_series([param[1:-1]], doc=doc) + + # Replace braced params with their parsed value + name = BRACED_PARAMS_PATTERN.sub(get_param_value_for_match, autoname_value) + + return name diff --git a/xhiveframework/model/rename_doc.py b/xhiveframework/model/rename_doc.py new file mode 100644 index 0000000..be6d62f --- /dev/null +++ b/xhiveframework/model/rename_doc.py @@ -0,0 +1,693 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +from types import NoneType +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework import _, bold +from xhiveframework.model.document import Document +from xhiveframework.model.dynamic_links import get_dynamic_link_map +from xhiveframework.model.naming import validate_name +from xhiveframework.model.utils.user_settings import sync_user_settings, update_user_settings_data +from xhiveframework.query_builder import Field +from xhiveframework.utils.data import sbool +from xhiveframework.utils.password import rename_password +from xhiveframework.utils.scheduler import is_scheduler_inactive + +if TYPE_CHECKING: + from xhiveframework.model.meta import Meta + + +@xhiveframework.whitelist() +def update_document_title( + *, + doctype: str, + docname: str, + title: str | None = None, + name: str | None = None, + merge: bool = False, + enqueue: bool = False, + **kwargs, +) -> str: + """ + Update the name or title of a document. Returns `name` if document was renamed, + `docname` if renaming operation was queued. + + :param doctype: DocType of the document + :param docname: Name of the document + :param title: New Title of the document + :param name: New Name of the document + :param merge: Merge the current Document with the existing one if exists + :param enqueue: Enqueue the rename operation, title is updated in current process + """ + + # to maintain backwards API compatibility + updated_title = kwargs.get("new_title") or title + updated_name = kwargs.get("new_name") or name + + # TODO: omit this after runtime type checking (ref: https://lab.membtech.com/xhiveframework/xhiveframework15/pull/14927) + for obj in [docname, updated_title, updated_name]: + if not isinstance(obj, str | NoneType): + xhiveframework.throw(f"{obj=} must be of type str or None") + + # handle bad API usages + merge = sbool(merge) + enqueue = sbool(enqueue) + action_enqueued = enqueue and not is_scheduler_inactive() + + doc = xhiveframework.get_doc(doctype, docname) + doc.check_permission(permtype="write") + + title_field = doc.meta.get_title_field() + + title_updated = updated_title and (title_field != "name") and (updated_title != doc.get(title_field)) + name_updated = updated_name and (updated_name != doc.name) + + queue = kwargs.get("queue") or "default" + + if name_updated: + if action_enqueued: + current_name = doc.name + + # before_name hook may have DocType specific validations or transformations + transformed_name = doc.run_method("before_rename", current_name, updated_name, merge) + if isinstance(transformed_name, dict): + transformed_name = transformed_name.get("new") + transformed_name = transformed_name or updated_name + + # run rename validations before queueing + # use savepoints to avoid partial renames / commits + validate_rename( + doctype=doctype, + old=current_name, + new=transformed_name, + meta=doc.meta, + merge=merge, + save_point=True, + ) + + doc.queue_action("rename", name=transformed_name, merge=merge, queue=queue) + else: + doc.rename(updated_name, merge=merge) + + if title_updated: + if action_enqueued and name_updated: + xhiveframework.enqueue( + "xhiveframework.client.set_value", + doctype=doc.doctype, + name=updated_name, + fieldname=title_field, + value=updated_title, + ) + else: + try: + setattr(doc, title_field, updated_title) + doc.save() + xhiveframework.msgprint(_("Saved"), alert=True, indicator="green") + except Exception as e: + if xhiveframework.db.is_duplicate_entry(e): + xhiveframework.throw( + _("{0} {1} already exists").format(doctype, xhiveframework.bold(docname)), + title=_("Duplicate Name"), + exc=xhiveframework.DuplicateEntryError, + ) + raise + + return doc.name + + +def rename_doc( + doctype: str | None = None, + old: str | None = None, + new: str | None = None, + force: bool = False, + merge: bool = False, + ignore_permissions: bool = False, + ignore_if_exists: bool = False, + show_alert: bool = True, + rebuild_search: bool = True, + doc: Document | None = None, + validate: bool = True, +) -> str: + """Rename a doc(dt, old) to doc(dt, new) and update all linked fields of type "Link". + + doc: Document object to be renamed. + new: New name for the record. If None, and doctype is specified, new name may be automatically generated via before_rename hooks. + doctype: DocType of the document. Not required if doc is passed. + old: Current name of the document. Not required if doc is passed. + force: Allow even if document is not allowed to be renamed. + merge: Merge with existing document of new name. + ignore_permissions: Ignore user permissions while renaming. + ignore_if_exists: Don't raise exception if document with new name already exists. This will quietely overwrite the existing document. + show_alert: Display alert if document is renamed successfully. + rebuild_search: Rebuild linked doctype search after renaming. + validate: Validate before renaming. If False, it is assumed that the caller has already validated. + """ + old_usage_style = doctype and old and new + new_usage_style = doc and new + + if not (new_usage_style or old_usage_style): + raise TypeError( + "{doctype, old, new} or {doc, new} are required arguments for xhiveframework.model.rename_doc" + ) + + old = old or doc.name + doctype = doctype or doc.doctype + force = sbool(force) + merge = sbool(merge) + meta = xhiveframework.get_meta(doctype) + + if validate: + old_doc = doc or xhiveframework.get_doc(doctype, old) + out = old_doc.run_method("before_rename", old, new, merge) or {} + new = (out.get("new") or new) if isinstance(out, dict) else (out or new) + new = validate_rename( + doctype=doctype, + old=old, + new=new, + meta=meta, + merge=merge, + force=force, + ignore_permissions=ignore_permissions, + ignore_if_exists=ignore_if_exists, + ) + + if not merge: + rename_parent_and_child(doctype, old, new, meta) + else: + update_assignments(old, new, doctype) + + # update link fields' values + link_fields = get_link_fields(doctype) + update_link_field_values(link_fields, old, new, doctype) + + rename_dynamic_links(doctype, old, new) + + # save the user settings in the db + update_user_settings(old, new, link_fields) + + if doctype == "DocType": + rename_doctype(doctype, old, new) + update_customizations(old, new) + + update_attachments(doctype, old, new) + + rename_versions(doctype, old, new) + + rename_eps_records(doctype, old, new) + + # call after_rename + new_doc = xhiveframework.get_doc(doctype, new) + + if validate: + # copy any flags if required + new_doc._local = getattr(old_doc, "_local", None) + + new_doc.run_method("after_rename", old, new, merge) + + if not merge: + rename_password(doctype, old, new) + + if merge: + new_doc.add_comment("Edit", _("merged {0} into {1}").format(xhiveframework.bold(old), xhiveframework.bold(new))) + else: + new_doc.add_comment("Edit", _("renamed from {0} to {1}").format(xhiveframework.bold(old), xhiveframework.bold(new))) + + if merge: + xhiveframework.delete_doc(doctype, old) + + new_doc.clear_cache() + xhiveframework.clear_cache() + if rebuild_search: + xhiveframework.enqueue("xhiveframework.utils.global_search.rebuild_for_doctype", doctype=doctype) + + if show_alert: + xhiveframework.msgprint( + _("Document renamed from {0} to {1}").format(bold(old), bold(new)), + alert=True, + indicator="green", + ) + + return new + + +def update_assignments(old: str, new: str, doctype: str) -> None: + old_assignments = xhiveframework.parse_json(xhiveframework.db.get_value(doctype, old, "_assign")) or [] + new_assignments = xhiveframework.parse_json(xhiveframework.db.get_value(doctype, new, "_assign")) or [] + common_assignments = list(set(old_assignments).intersection(new_assignments)) + + for user in common_assignments: + # delete todos linked to old doc + todos = xhiveframework.get_all( + "ToDo", + { + "owner": user, + "reference_type": doctype, + "reference_name": old, + }, + ["name", "description"], + ) + + for todo in todos: + xhiveframework.delete_doc("ToDo", todo.name) + + unique_assignments = list(set(old_assignments + new_assignments)) + xhiveframework.db.set_value(doctype, new, "_assign", xhiveframework.as_json(unique_assignments, indent=0)) + + +def update_user_settings(old: str, new: str, link_fields: list[dict]) -> None: + """ + Update the user settings of all the linked doctypes while renaming. + """ + + # store the user settings data from the redis to db + sync_user_settings() + + if not link_fields: + return + + # find the user settings for the linked doctypes + linked_doctypes = {d.parent for d in link_fields if not d.issingle} + UserSettings = xhiveframework.qb.Table("__UserSettings") + + user_settings_details = ( + xhiveframework.qb.from_(UserSettings) + .select("user", "doctype", "data") + .where(UserSettings.data.like(old) & UserSettings.doctype.isin(linked_doctypes)) + .run(as_dict=True) + ) + + # create the dict using the doctype name as key and values as list of the user settings + from collections import defaultdict + + user_settings_dict = defaultdict(list) + for user_setting in user_settings_details: + user_settings_dict[user_setting.doctype].append(user_setting) + + # update the name in linked doctype whose user settings exists + for fields in link_fields: + user_settings = user_settings_dict.get(fields.parent) + if user_settings: + for user_setting in user_settings: + update_user_settings_data(user_setting, "value", old, new, "docfield", fields.fieldname) + else: + continue + + +def update_customizations(old: str, new: str) -> None: + xhiveframework.db.set_value("Custom DocPerm", {"parent": old}, "parent", new, update_modified=False) + + +def update_attachments(doctype: str, old: str, new: str) -> None: + if doctype != "DocType": + File = xhiveframework.qb.DocType("File") + + xhiveframework.qb.update(File).set(File.attached_to_name, new).where( + (File.attached_to_name == old) & (File.attached_to_doctype == doctype) + ).run() + + +def rename_versions(doctype: str, old: str, new: str) -> None: + Version = xhiveframework.qb.DocType("Version") + + xhiveframework.qb.update(Version).set(Version.docname, new).where( + (Version.docname == old) & (Version.ref_doctype == doctype) + ).run() + + +def rename_eps_records(doctype: str, old: str, new: str) -> None: + EPL = xhiveframework.qb.DocType("Energy Point Log") + + xhiveframework.qb.update(EPL).set(EPL.reference_name, new).where( + (EPL.reference_doctype == doctype) & (EPL.reference_name == old) + ).run() + + +def rename_parent_and_child(doctype: str, old: str, new: str, meta: "Meta") -> None: + xhiveframework.qb.update(doctype).set("name", new).where(Field("name") == old).run() + + update_autoname_field(doctype, new, meta) + update_child_docs(old, new, meta) + + +def update_autoname_field(doctype: str, new: str, meta: "Meta") -> None: + # update the value of the autoname field on rename of the docname + if meta.get("autoname"): + field = meta.get("autoname").split(":") + if field and field[0] == "field": + xhiveframework.qb.update(doctype).set(field[1], new).where(Field("name") == new).run() + + +def validate_rename( + doctype: str, + old: str, + new: str, + meta: "Meta", + merge: bool, + force: bool = False, + ignore_permissions: bool = False, + ignore_if_exists: bool = False, + save_point=False, +) -> str: + # using for update so that it gets locked and someone else cannot edit it while this rename is going on! + if save_point: + _SAVE_POINT = f"validate_rename_{xhiveframework.generate_hash(length=8)}" + xhiveframework.db.savepoint(_SAVE_POINT) + + exists = xhiveframework.qb.from_(doctype).where(Field("name") == new).for_update().select("name").run(pluck=True) + exists = exists[0] if exists else None + + if not xhiveframework.db.exists(doctype, old): + xhiveframework.throw(_("Can't rename {0} to {1} because {0} doesn't exist.").format(old, new)) + + if old == new: + xhiveframework.throw(_("No changes made because old and new name are the same.").format(old, new)) + + if exists and exists != new: + # for fixing case, accents + exists = None + + if merge and not exists: + xhiveframework.throw(_("{0} {1} does not exist, select a new target to merge").format(doctype, new)) + + if not merge and exists and not ignore_if_exists: + xhiveframework.throw(_("Another {0} with name {1} exists, select another name").format(doctype, new)) + + if not (ignore_permissions or xhiveframework.permissions.has_permission(doctype, "write", raise_exception=False)): + xhiveframework.throw(_("You need write permission to rename")) + + if not (force or ignore_permissions) and not meta.allow_rename: + xhiveframework.throw(_("{0} not allowed to be renamed").format(_(doctype))) + + # validate naming like it's done in doc.py + new = validate_name(doctype, new) + + if save_point: + xhiveframework.db.rollback(save_point=_SAVE_POINT) + + return new + + +def rename_doctype(doctype: str, old: str, new: str) -> None: + # change options for fieldtype Table, Table MultiSelect and Link + fields_with_options = ("Link", *xhiveframework.model.table_fields) + + for fieldtype in fields_with_options: + update_options_for_fieldtype(fieldtype, old, new) + + # change parenttype for fieldtype Table + update_parenttype_values(old, new) + + +def update_child_docs(old: str, new: str, meta: "Meta") -> None: + # update "parent" + for df in meta.get_table_fields(): + ( + xhiveframework.qb.update(df.options) + .set("parent", new) + .where((Field("parent") == old) & (Field("parenttype") == meta.name)) + ).run() + + +def update_link_field_values(link_fields: list[dict], old: str, new: str, doctype: str) -> None: + for field in link_fields: + if field["issingle"]: + try: + single_doc = xhiveframework.get_doc(field["parent"]) + if single_doc.get(field["fieldname"]) == old: + single_doc.set(field["fieldname"], new) + # update single docs using ORM rather then query + # as single docs also sometimes sets defaults! + single_doc.flags.ignore_mandatory = True + single_doc.save(ignore_permissions=True) + except ImportError: + # fails in patches where the doctype has been renamed + # or no longer exists + pass + else: + parent = field["parent"] + docfield = field["fieldname"] + + # Handles the case where one of the link fields belongs to + # the DocType being renamed. + # Here this field could have the current DocType as its value too. + + # In this case while updating link field value, the field's parent + # or the current DocType table name hasn't been renamed yet, + # so consider it's old name. + if parent == new and doctype == "DocType": + parent = old + + xhiveframework.db.set_value(parent, {docfield: old}, docfield, new, update_modified=False) + + # update cached link_fields as per new + if doctype == "DocType" and field["parent"] == old: + field["parent"] = new + + +def get_link_fields(doctype: str) -> list[dict]: + # get link fields from tabDocField + if not xhiveframework.flags.link_fields: + xhiveframework.flags.link_fields = {} + + if doctype not in xhiveframework.flags.link_fields: + virtual_doctypes = xhiveframework.get_all("DocType", {"is_virtual": 1}, pluck="name") + + dt = xhiveframework.qb.DocType("DocType") + df = xhiveframework.qb.DocType("DocField") + cf = xhiveframework.qb.DocType("Custom Field") + ps = xhiveframework.qb.DocType("Property Setter") + + standard_fields = ( + xhiveframework.qb.from_(df) + .inner_join(dt) + .on(df.parent == dt.name) + .select(df.parent, df.fieldname, dt.issingle.as_("issingle")) + .where((df.options == doctype) & (df.fieldtype == "Link") & (dt.is_virtual == 0)) + .run(as_dict=True) + ) + + cf_issingle = xhiveframework.qb.from_(dt).select(dt.issingle).where(dt.name == cf.dt).as_("issingle") + custom_fields = ( + xhiveframework.qb.from_(cf) + .select(cf.dt.as_("parent"), cf.fieldname, cf_issingle) + .where((cf.options == doctype) & (cf.fieldtype == "Link") & (cf.dt.notin(virtual_doctypes))) + .run(as_dict=True) + ) + + ps_issingle = xhiveframework.qb.from_(dt).select(dt.issingle).where(dt.name == ps.doc_type).as_("issingle") + property_setter_fields = ( + xhiveframework.qb.from_(ps) + .select(ps.doc_type.as_("parent"), ps.field_name.as_("fieldname"), ps_issingle) + .where( + (ps.property == "options") + & (ps.value == doctype) + & (ps.field_name.notnull()) + & (ps.doc_type.notin(virtual_doctypes)) + ) + .run(as_dict=True) + ) + + xhiveframework.flags.link_fields[doctype] = standard_fields + custom_fields + property_setter_fields + + return xhiveframework.flags.link_fields[doctype] + + +def update_options_for_fieldtype(fieldtype: str, old: str, new: str) -> None: + CustomField = xhiveframework.qb.DocType("Custom Field") + PropertySetter = xhiveframework.qb.DocType("Property Setter") + + if xhiveframework.conf.developer_mode: + for name in xhiveframework.get_all("DocField", filters={"options": old}, pluck="parent"): + if name in (old, new): + continue + + doctype = xhiveframework.get_doc("DocType", name) + save = False + for f in doctype.fields: + if f.options == old: + f.options = new + save = True + if save: + doctype.save() + + DocField = xhiveframework.qb.DocType("DocField") + xhiveframework.qb.update(DocField).set(DocField.options, new).where( + (DocField.fieldtype == fieldtype) & (DocField.options == old) + ).run() + + xhiveframework.qb.update(CustomField).set(CustomField.options, new).where( + (CustomField.fieldtype == fieldtype) & (CustomField.options == old) + ).run() + + xhiveframework.qb.update(PropertySetter).set(PropertySetter.value, new).where( + (PropertySetter.property == "options") & (PropertySetter.value == old) + ).run() + + +def get_select_fields(old: str, new: str) -> list[dict]: + """ + get select type fields where doctype's name is hardcoded as + new line separated list + """ + df = xhiveframework.qb.DocType("DocField") + dt = xhiveframework.qb.DocType("DocType") + cf = xhiveframework.qb.DocType("Custom Field") + ps = xhiveframework.qb.DocType("Property Setter") + + # get link fields from tabDocField + st_issingle = xhiveframework.qb.from_(dt).select(dt.issingle).where(dt.name == df.parent).as_("issingle") + standard_fields = ( + xhiveframework.qb.from_(df) + .select(df.parent, df.fieldname, st_issingle) + .where( + (df.parent != new) + & (df.fieldname != "fieldtype") + & (df.fieldtype == "Select") + & (df.options.like(f"%{old}%")) + ) + .run(as_dict=True) + ) + + # get link fields from tabCustom Field + cf_issingle = xhiveframework.qb.from_(dt).select(dt.issingle).where(dt.name == cf.dt).as_("issingle") + custom_select_fields = ( + xhiveframework.qb.from_(cf) + .select(cf.dt.as_("parent"), cf.fieldname, cf_issingle) + .where((cf.dt != new) & (cf.fieldtype == "Select") & (cf.options.like(f"%{old}%"))) + .run(as_dict=True) + ) + + # remove fields whose options have been changed using property setter + ps_issingle = xhiveframework.qb.from_(dt).select(dt.issingle).where(dt.name == ps.doc_type).as_("issingle") + property_setter_select_fields = ( + xhiveframework.qb.from_(ps) + .select(ps.doc_type.as_("parent"), ps.field_name.as_("fieldname"), ps_issingle) + .where( + (ps.doc_type != new) + & (ps.property == "options") + & (ps.field_name.notnull()) + & (ps.value.like(f"%{old}%")) + ) + .run(as_dict=True) + ) + + return standard_fields + custom_select_fields + property_setter_select_fields + + +def update_select_field_values(old: str, new: str): + from xhiveframework.query_builder.functions import Replace + + DocField = xhiveframework.qb.DocType("DocField") + CustomField = xhiveframework.qb.DocType("Custom Field") + PropertySetter = xhiveframework.qb.DocType("Property Setter") + + xhiveframework.qb.update(DocField).set(DocField.options, Replace(DocField.options, old, new)).where( + (DocField.fieldtype == "Select") + & (DocField.parent != new) + & (DocField.options.like(f"%\n{old}%") | DocField.options.like(f"%{old}\n%")) + ).run() + + xhiveframework.qb.update(CustomField).set(CustomField.options, Replace(CustomField.options, old, new)).where( + (CustomField.fieldtype == "Select") + & (CustomField.dt != new) + & (CustomField.options.like(f"%\n{old}%") | CustomField.options.like(f"%{old}\n%")) + ).run() + + xhiveframework.qb.update(PropertySetter).set(PropertySetter.value, Replace(PropertySetter.value, old, new)).where( + (PropertySetter.property == "options") + & (PropertySetter.field_name.notnull()) + & (PropertySetter.doc_type != new) + & (PropertySetter.value.like(f"%\n{old}%") | PropertySetter.value.like(f"%{old}\n%")) + ).run() + + +def update_parenttype_values(old: str, new: str): + child_doctypes = xhiveframework.get_all( + "DocField", + fields=["options", "fieldname"], + filters={"parent": new, "fieldtype": ["in", xhiveframework.model.table_fields]}, + ) + + custom_child_doctypes = xhiveframework.get_all( + "Custom Field", + fields=["options", "fieldname"], + filters={"dt": new, "fieldtype": ["in", xhiveframework.model.table_fields]}, + ) + + child_doctypes += custom_child_doctypes + fields = [d["fieldname"] for d in child_doctypes] + + property_setter_child_doctypes = xhiveframework.get_all( + "Property Setter", + filters={"doc_type": new, "property": "options", "field_name": ("in", fields)}, + pluck="value", + ) + + child_doctypes = set(list(d["options"] for d in child_doctypes) + property_setter_child_doctypes) + + for doctype in child_doctypes: + table = xhiveframework.qb.DocType(doctype) + xhiveframework.qb.update(table).set(table.parenttype, new).where(table.parenttype == old).run() + + +def rename_dynamic_links(doctype: str, old: str, new: str): + Singles = xhiveframework.qb.DocType("Singles") + for df in get_dynamic_link_map().get(doctype, []): + # dynamic link in single, just one value to check + meta = xhiveframework.get_meta(df.parent) + if meta.is_virtual: + continue + if meta.issingle: + refdoc = xhiveframework.db.get_singles_dict(df.parent) + if refdoc.get(df.options) == doctype and refdoc.get(df.fieldname) == old: + xhiveframework.qb.update(Singles).set(Singles.value, new).where( + (Singles.field == df.fieldname) & (Singles.doctype == df.parent) & (Singles.value == old) + ).run() + else: + # because the table hasn't been renamed yet! + parent = df.parent if df.parent != new else old + + xhiveframework.qb.update(parent).set(df.fieldname, new).where( + (Field(df.options) == doctype) & (Field(df.fieldname) == old) + ).run() + + +def bulk_rename(doctype: str, rows: list[list] | None = None, via_console: bool = False) -> list[str] | None: + """Bulk rename documents + + :param doctype: DocType to be renamed + :param rows: list of documents as `((oldname, newname, merge(optional)), ..)`""" + if not rows: + xhiveframework.throw(_("Please select a valid csv file with data")) + + if not via_console: + max_rows = 500 + if len(rows) > max_rows: + xhiveframework.throw(_("Maximum {0} rows allowed").format(max_rows)) + + rename_log = [] + for row in rows: + # if row has some content + if len(row) > 1 and row[0] and row[1]: + merge = len(row) > 2 and (row[2] == "1" or row[2].lower() == "true") + try: + if rename_doc(doctype, row[0], row[1], merge=merge, rebuild_search=False): + msg = _("Successful: {0} to {1}").format(row[0], row[1]) + xhiveframework.db.commit() + else: + msg = None + except Exception as e: + msg = _("** Failed: {0} to {1}: {2}").format(row[0], row[1], repr(e)) + xhiveframework.db.rollback() + + if msg: + if via_console: + print(msg) + else: + rename_log.append(msg) + + xhiveframework.enqueue("xhiveframework.utils.global_search.rebuild_for_doctype", doctype=doctype) + + if not via_console: + return rename_log diff --git a/xhiveframework/model/sync.py b/xhiveframework/model/sync.py new file mode 100644 index 0000000..baba0c6 --- /dev/null +++ b/xhiveframework/model/sync.py @@ -0,0 +1,176 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +""" + Sync's doctype and docfields from txt files to database + perms will get synced only if none exist +""" +import os + +import xhiveframework +from xhiveframework.cache_manager import clear_controller_cache +from xhiveframework.model.base_document import get_controller +from xhiveframework.modules.import_file import import_file_by_path +from xhiveframework.modules.patch_handler import _patch_mode +from xhiveframework.utils import update_progress_bar + +IMPORTABLE_DOCTYPES = [ + ("core", "doctype"), + ("core", "page"), + ("core", "report"), + ("desk", "dashboard_chart_source"), + ("printing", "print_format"), + ("website", "web_page"), + ("website", "website_theme"), + ("website", "web_form"), + ("website", "web_template"), + ("email", "notification"), + ("printing", "print_style"), + ("desk", "workspace"), + ("desk", "onboarding_step"), + ("desk", "module_onboarding"), + ("desk", "form_tour"), + ("custom", "client_script"), + ("core", "server_script"), + ("custom", "custom_field"), + ("custom", "property_setter"), +] + + +def sync_all(force=0, reset_permissions=False): + _patch_mode(True) + + for app in xhiveframework.get_installed_apps(): + sync_for(app, force, reset_permissions=reset_permissions) + + _patch_mode(False) + + xhiveframework.clear_cache() + + +def sync_for(app_name, force=0, reset_permissions=False): + files = [] + + if app_name == "xhiveframework": + # these need to go first at time of install + + XHIVEFRAMEWORK_PATH = xhiveframework.get_app_path("xhiveframework") + + for core_module in [ + "docfield", + "docperm", + "doctype_action", + "doctype_link", + "doctype_state", + "role", + "has_role", + "doctype", + ]: + files.append(os.path.join(XHIVEFRAMEWORK_PATH, "core", "doctype", core_module, f"{core_module}.json")) + + for custom_module in ["custom_field", "property_setter"]: + files.append( + os.path.join(XHIVEFRAMEWORK_PATH, "custom", "doctype", custom_module, f"{custom_module}.json") + ) + + for website_module in ["web_form", "web_template", "web_form_field", "portal_menu_item"]: + files.append( + os.path.join(XHIVEFRAMEWORK_PATH, "website", "doctype", website_module, f"{website_module}.json") + ) + + for desk_module in [ + "number_card", + "dashboard_chart", + "dashboard", + "onboarding_permission", + "onboarding_step", + "onboarding_step_map", + "module_onboarding", + "workspace_link", + "workspace_chart", + "workspace_shortcut", + "workspace_quick_list", + "workspace_number_card", + "workspace_custom_block", + "workspace", + ]: + files.append(os.path.join(XHIVEFRAMEWORK_PATH, "desk", "doctype", desk_module, f"{desk_module}.json")) + + for module_name, document_type in IMPORTABLE_DOCTYPES: + file = os.path.join(XHIVEFRAMEWORK_PATH, module_name, "doctype", document_type, f"{document_type}.json") + if file not in files: + files.append(file) + + for module_name in xhiveframework.local.app_modules.get(app_name) or []: + folder = os.path.dirname(xhiveframework.get_module(app_name + "." + module_name).__file__) + files = get_doc_files(files=files, start_path=folder) + + l = len(files) + + if l: + for i, doc_path in enumerate(files): + import_file_by_path( + doc_path, force=force, ignore_version=True, reset_permissions=reset_permissions + ) + + xhiveframework.db.commit() + + # show progress bar + update_progress_bar(f"Updating DocTypes for {app_name}", i, l) + + # print each progress bar on new line + print() + + +def get_doc_files(files, start_path): + """walk and sync all doctypes and pages""" + + files = files or [] + + for _module, doctype in IMPORTABLE_DOCTYPES: + doctype_path = os.path.join(start_path, doctype) + if os.path.exists(doctype_path): + for docname in os.listdir(doctype_path): + if os.path.isdir(os.path.join(doctype_path, docname)): + doc_path = os.path.join(doctype_path, docname, docname) + ".json" + if os.path.exists(doc_path): + if doc_path not in files: + files.append(doc_path) + + return files + + +def remove_orphan_doctypes(): + """Find and remove any orphaned doctypes. + + These are doctypes for which code and schema file is + deleted but entry is present in DocType table. + + Note: Deleting the entry doesn't delete any data. + So this is supposed to be non-destrictive operation. + """ + + doctype_names = xhiveframework.get_all("DocType", {"custom": 0}, pluck="name") + orphan_doctypes = [] + + clear_controller_cache() + class_overrides = xhiveframework.get_hooks("override_doctype_class", {}) + + for doctype in doctype_names: + if doctype in class_overrides: + continue + try: + get_controller(doctype=doctype) + except ImportError: + orphan_doctypes.append(doctype) + except Exception: + continue + + if not orphan_doctypes: + return + + print(f"Orphaned DocType(s) found: {', '.join(orphan_doctypes)}") + for i, name in enumerate(orphan_doctypes): + xhiveframework.delete_doc("DocType", name, force=True, ignore_missing=True) + update_progress_bar("Deleting orphaned DocTypes", i, len(orphan_doctypes)) + xhiveframework.db.commit() + print() diff --git a/xhiveframework/model/utils/__init__.py b/xhiveframework/model/utils/__init__.py new file mode 100644 index 0000000..703fa8e --- /dev/null +++ b/xhiveframework/model/utils/__init__.py @@ -0,0 +1,143 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import re + +import xhiveframework +from xhiveframework.build import html_to_js_template +from xhiveframework.utils import cstr +from xhiveframework.utils.caching import site_cache + +STANDARD_FIELD_CONVERSION_MAP = { + "name": "Link", + "owner": "Data", + "idx": "Int", + "creation": "Data", + "modified": "Data", + "modified_by": "Data", + "_user_tags": "Data", + "_liked_by": "Data", + "_comments": "Text", + "_assign": "Text", + "docstatus": "Int", +} +INCLUDE_DIRECTIVE_PATTERN = re.compile(r"""{% include\s['"](.*)['"]\s%}""") + + +def set_default(doc, key): + """Set is_default property of given doc and unset all others filtered by given key.""" + if not doc.is_default: + xhiveframework.db.set(doc, "is_default", 1) + + xhiveframework.db.sql( + """update `tab{}` set `is_default`=0 + where `{}`={} and name!={}""".format(doc.doctype, key, "%s", "%s"), + (doc.get(key), doc.name), + ) + + +def set_field_property(filters, key, value): + """utility set a property in all fields of a particular type""" + docs = [ + xhiveframework.get_doc("DocType", d.parent) + for d in xhiveframework.get_all("DocField", fields=["parent"], filters=filters) + ] + + for d in docs: + d.get("fields", filters)[0].set(key, value) + d.save() + print(f"Updated {d.name}") + + xhiveframework.db.commit() + + +class InvalidIncludePath(xhiveframework.ValidationError): + pass + + +def render_include(content): + """render {% raw %}{% include "app/path/filename" %}{% endraw %} in js file""" + + content = cstr(content) + + # try 5 levels of includes + for _ in range(5): + if "{% include" in content: + paths = INCLUDE_DIRECTIVE_PATTERN.findall(content) + if not paths: + xhiveframework.throw(_("Invalid include path"), InvalidIncludePath) + + for path in paths: + app, app_path = path.split("/", 1) + with open(xhiveframework.get_app_path(app, app_path), encoding="utf-8") as f: + include = f.read() + if path.endswith(".html"): + include = html_to_js_template(path, include) + + content = re.sub(rf"""{{% include\s['"]{path}['"]\s%}}""", include, content) + + else: + break + + return content + + +def get_fetch_values(doctype, fieldname, value): + """Returns fetch value dict for the given object + + :param doctype: Target doctype + :param fieldname: Link fieldname selected + :param value: Value selected + """ + + result = xhiveframework._dict() + meta = xhiveframework.get_meta(doctype) + + # fieldname in target doctype: fieldname in source doctype + fields_to_fetch = { + df.fieldname: df.fetch_from.split(".", 1)[1] for df in meta.get_fields_to_fetch(fieldname) + } + + # nothing to fetch + if not fields_to_fetch: + return result + + # initialise empty values for target fields + for target_fieldname in fields_to_fetch: + result[target_fieldname] = None + + # fetch only if Link field has a truthy value + if not value: + return result + + db_values = xhiveframework.db.get_value( + meta.get_options(fieldname), # source doctype + value, + tuple(set(fields_to_fetch.values())), # unique source fieldnames + as_dict=True, + ) + + # if value doesn't exist in source doctype, get_value returns None + if not db_values: + return result + + for target_fieldname, source_fieldname in fields_to_fetch.items(): + result[target_fieldname] = db_values.get(source_fieldname) + + return result + + +@site_cache() +def is_virtual_doctype(doctype: str): + if xhiveframework.db.has_column("DocType", "is_virtual"): + return xhiveframework.db.get_value("DocType", doctype, "is_virtual") + return False + + +@site_cache() +def is_single_doctype(doctype: str) -> bool: + from xhiveframework.model.base_document import DOCTYPES_FOR_DOCTYPE + + if doctype in DOCTYPES_FOR_DOCTYPE: + return False + + return xhiveframework.db.get_value("DocType", doctype, "issingle") diff --git a/xhiveframework/model/utils/link_count.py b/xhiveframework/model/utils/link_count.py new file mode 100644 index 0000000..bcf4dd8 --- /dev/null +++ b/xhiveframework/model/utils/link_count.py @@ -0,0 +1,78 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from collections import defaultdict + +import xhiveframework + +ignore_doctypes = { + "DocType", + "Print Format", + "Role", + "Module Def", + "Communication", + "ToDo", + "Version", + "Error Log", + "Scheduled Job Log", + "Event Sync Log", + "Event Update Log", + "Access Log", + "View Log", + "Activity Log", + "Energy Point Log", + "Notification Log", + "Email Queue", + "DocShare", + "Document Follow", + "Console Log", + "User", +} + + +def notify_link_count(doctype, name): + """updates link count for given document""" + + if doctype in ignore_doctypes or not xhiveframework.request: + return + + if not hasattr(xhiveframework.local, "_link_count"): + xhiveframework.local._link_count = defaultdict(int) + xhiveframework.db.after_commit.add(flush_local_link_count) + + xhiveframework.local._link_count[(doctype, name)] += 1 + + +def flush_local_link_count(): + """flush from local before ending request""" + new_links = getattr(xhiveframework.local, "_link_count", None) + if not new_links: + return + + link_count = xhiveframework.cache.get_value("_link_count") or {} + + for key, value in new_links.items(): + if key in link_count: + link_count[key] += value + else: + link_count[key] = value + + xhiveframework.cache.set_value("_link_count", link_count) + new_links.clear() + + +def update_link_count(): + """increment link count in the `idx` column for the given document""" + link_count = xhiveframework.cache.get_value("_link_count") + + if link_count: + for (doctype, name), count in link_count.items(): + try: + table = xhiveframework.qb.DocType(doctype) + xhiveframework.qb.update(table).set(table.idx, table.idx + count).where(table.name == name).run() + xhiveframework.db.commit() + except Exception as e: + if not xhiveframework.db.is_table_missing(e): # table not found, single + raise e + # reset the count + xhiveframework.cache.delete_value("_link_count") diff --git a/xhiveframework/model/utils/rename_doc.py b/xhiveframework/model/utils/rename_doc.py new file mode 100644 index 0000000..8819a9b --- /dev/null +++ b/xhiveframework/model/utils/rename_doc.py @@ -0,0 +1,63 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from itertools import product + +import xhiveframework +from xhiveframework.model.rename_doc import get_link_fields + + +def update_linked_doctypes( + doctype: str, docname: str, linked_to: str, value: str, ignore_doctypes: list | None = None +): + """ + linked_doctype_info_list = list formed by get_fetch_fields() function + docname = Master DocType's name in which modification are made + value = Value for the field thats set in other DocType's by fetching from Master DocType + """ + linked_doctype_info_list = get_fetch_fields(doctype, linked_to, ignore_doctypes) + + for d in linked_doctype_info_list: + xhiveframework.db.set_value( + d.doctype, + { + d.master_fieldname: docname, + d.linked_to_fieldname: ("!=", value), + }, + d.linked_to_fieldname, + value, + ) + + +def get_fetch_fields(doctype: str, linked_to: str, ignore_doctypes: list | None = None) -> list[dict]: + """ + doctype = Master DocType in which the changes are being made + linked_to = DocType name of the field thats being updated in Master + This function fetches list of all DocType where both doctype and linked_to is found + as link fields. + Forms a list of dict in the form - + [{doctype: , master_fieldname: , linked_to_fieldname: ] + where + doctype = DocType where changes need to be made + master_fieldname = Fieldname where options = doctype + linked_to_fieldname = Fieldname where options = linked_to + """ + + out = [] + master_list = get_link_fields(doctype) + linked_to_list = get_link_fields(linked_to) + product_list = product(master_list, linked_to_list) + + for d in product_list: + linked_doctype_info = xhiveframework._dict() + if ( + d[0]["parent"] == d[1]["parent"] + and (not ignore_doctypes or d[0]["parent"] not in ignore_doctypes) + and not d[1]["issingle"] + ): + linked_doctype_info.doctype = d[0]["parent"] + linked_doctype_info.master_fieldname = d[0]["fieldname"] + linked_doctype_info.linked_to_fieldname = d[1]["fieldname"] + out.append(linked_doctype_info) + + return out diff --git a/xhiveframework/model/utils/rename_field.py b/xhiveframework/model/utils/rename_field.py new file mode 100644 index 0000000..55961ce --- /dev/null +++ b/xhiveframework/model/utils/rename_field.py @@ -0,0 +1,176 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import json + +import xhiveframework +from xhiveframework.model import no_value_fields, table_fields +from xhiveframework.model.utils.user_settings import sync_user_settings, update_user_settings_data +from xhiveframework.utils.password import rename_password_field + + +def rename_field(doctype, old_fieldname, new_fieldname, validate=True): + """This functions assumes that doctype is already synced""" + + meta = xhiveframework.get_meta(doctype, cached=False) + new_field = meta.get_field(new_fieldname) + + if validate: + if not new_field: + print("rename_field: " + (new_fieldname) + " not found in " + doctype) + return + + if not meta.issingle and not xhiveframework.db.has_column(doctype, old_fieldname): + print("rename_field: " + (old_fieldname) + " not found in table for: " + doctype) + # never had the field? + return + + if new_field.fieldtype in table_fields: + # change parentfield of table mentioned in options + xhiveframework.db.sql( + """update `tab{}` set parentfield={} + where parentfield={}""".format(new_field.options.split("\n", 1)[0], "%s", "%s"), + (new_fieldname, old_fieldname), + ) + + elif new_field.fieldtype not in no_value_fields: + if meta.issingle: + xhiveframework.db.sql( + """update `tabSingles` set field=%s + where doctype=%s and field=%s""", + (new_fieldname, doctype, old_fieldname), + ) + else: + # copy field value + xhiveframework.db.sql(f"""update `tab{doctype}` set `{new_fieldname}`=`{old_fieldname}`""") + + update_reports(doctype, old_fieldname, new_fieldname) + update_users_report_view_settings(doctype, old_fieldname, new_fieldname) + + if new_field.fieldtype == "Password": + rename_password_field(doctype, old_fieldname, new_fieldname) + + # update in property setter + update_property_setters(doctype, old_fieldname, new_fieldname) + + # update in user settings + update_user_settings(doctype, old_fieldname, new_fieldname) + + +def update_reports(doctype, old_fieldname, new_fieldname): + def _get_new_sort_by(report_dict, report, key): + sort_by = report_dict.get(key) or "" + if sort_by: + sort_by = sort_by.split(".") + if len(sort_by) > 1: + if sort_by[0] == doctype and sort_by[1] == old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True + elif report.ref_doctype == doctype and sort_by[0] == old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True + + if isinstance(sort_by, list): + sort_by = ".".join(sort_by) + + return sort_by + + reports = xhiveframework.db.sql( + """select name, ref_doctype, json from tabReport + where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No' + and json like %s and json like %s""", + ("%%%s%%" % old_fieldname, "%%%s%%" % doctype), + as_dict=True, + ) + + for r in reports: + report_dict = json.loads(r.json) + + # update filters + new_filters = [] + if report_dict.get("filters"): + for f in report_dict.get("filters"): + if f and len(f) > 1 and f[0] == doctype and f[1] == old_fieldname: + new_filters.append([doctype, new_fieldname, f[2], f[3]]) + report_dict["updated"] = True + else: + new_filters.append(f) + + # update columns + new_columns = [] + if report_dict.get("columns"): + for c in report_dict.get("columns"): + if c and len(c) > 1 and c[0] == old_fieldname and c[1] == doctype: + new_columns.append([new_fieldname, doctype]) + report_dict["updated"] = True + else: + new_columns.append(c) + + # update sort by + new_sort_by = _get_new_sort_by(report_dict, r, "sort_by") + new_sort_by_next = _get_new_sort_by(report_dict, r, "sort_by_next") + + if report_dict.get("updated"): + new_val = json.dumps( + { + "filters": new_filters, + "columns": new_columns, + "sort_by": new_sort_by, + "sort_order": report_dict.get("sort_order"), + "sort_by_next": new_sort_by_next, + "sort_order_next": report_dict.get("sort_order_next"), + } + ) + + xhiveframework.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name)) + + +def update_users_report_view_settings(doctype, ref_fieldname, new_fieldname): + user_report_cols = xhiveframework.db.sql( + """select defkey, defvalue from `tabDefaultValue` where + defkey like '_list_settings:%'""" + ) + for key, value in user_report_cols: + new_columns = [] + columns_modified = False + for field, field_doctype in json.loads(value): + if field == ref_fieldname and field_doctype == doctype: + new_columns.append([new_fieldname, field_doctype]) + columns_modified = True + else: + new_columns.append([field, field_doctype]) + + if columns_modified: + xhiveframework.db.sql( + """update `tabDefaultValue` set defvalue={} + where defkey={}""".format("%s", "%s"), + (json.dumps(new_columns), key), + ) + + +def update_property_setters(doctype, old_fieldname, new_fieldname): + xhiveframework.db.sql( + """update `tabProperty Setter` set field_name = %s + where doc_type=%s and field_name=%s""", + (new_fieldname, doctype, old_fieldname), + ) + + xhiveframework.db.sql( + """update `tabCustom Field` set insert_after=%s + where insert_after=%s and dt=%s""", + (new_fieldname, old_fieldname, doctype), + ) + + +def update_user_settings(doctype, old_fieldname, new_fieldname): + # store the user settings data from the redis to db + sync_user_settings() + + user_settings = xhiveframework.db.sql( + ''' select user, doctype, data from `__UserSettings` + where doctype=%s and data like "%%%s%%"''', + (doctype, old_fieldname), + as_dict=1, + ) + + for user_setting in user_settings: + update_user_settings_data(user_setting, "docfield", old_fieldname, new_fieldname) diff --git a/xhiveframework/model/utils/user_settings.py b/xhiveframework/model/utils/user_settings.py new file mode 100644 index 0000000..7fe5678 --- /dev/null +++ b/xhiveframework/model/utils/user_settings.py @@ -0,0 +1,105 @@ +# Settings saved per user basis +# such as page_limit, filters, last_view + +import json + +import xhiveframework +from xhiveframework import safe_decode + +# dict for mapping the index and index type for the filters of different views +filter_dict = {"doctype": 0, "docfield": 1, "operator": 2, "value": 3} + + +def get_user_settings(doctype, for_update=False): + user_settings = xhiveframework.cache.hget("_user_settings", f"{doctype}::{xhiveframework.session.user}") + + if user_settings is None: + user_settings = xhiveframework.db.sql( + """select data from `__UserSettings` + where `user`=%s and `doctype`=%s""", + (xhiveframework.session.user, doctype), + ) + user_settings = user_settings and user_settings[0][0] or "{}" + + if not for_update: + update_user_settings(doctype, user_settings, True) + + return user_settings or "{}" + + +def update_user_settings(doctype, user_settings, for_update=False): + """update user settings in cache""" + + if for_update: + current = json.loads(user_settings) + else: + current = json.loads(get_user_settings(doctype, for_update=True)) + + if isinstance(current, str): + # corrupt due to old code, remove this in a future release + current = {} + + current.update(user_settings) + + xhiveframework.cache.hset("_user_settings", f"{doctype}::{xhiveframework.session.user}", json.dumps(current)) + + +def sync_user_settings(): + """Sync from cache to database (called asynchronously via the browser)""" + for key, data in xhiveframework.cache.hgetall("_user_settings").items(): + key = safe_decode(key) + doctype, user = key.split("::") # WTF? + xhiveframework.db.multisql( + { + "mariadb": """INSERT INTO `__UserSettings`(`user`, `doctype`, `data`) + VALUES (%s, %s, %s) + ON DUPLICATE key UPDATE `data`=%s""", + "postgres": """INSERT INTO `__UserSettings` (`user`, `doctype`, `data`) + VALUES (%s, %s, %s) + ON CONFLICT ("user", "doctype") DO UPDATE SET `data`=%s""", + }, + (user, doctype, data, data), + as_dict=1, + ) + + +@xhiveframework.whitelist() +def save(doctype, user_settings): + user_settings = json.loads(user_settings or "{}") + update_user_settings(doctype, user_settings) + return user_settings + + +@xhiveframework.whitelist() +def get(doctype): + return get_user_settings(doctype) + + +def update_user_settings_data( + user_setting, fieldname, old, new, condition_fieldname=None, condition_values=None +): + data = user_setting.get("data") + if data: + update = False + data = json.loads(data) + for view in ["List", "Gantt", "Kanban", "Calendar", "Image", "Inbox", "Report"]: + view_settings = data.get(view) + if view_settings and view_settings.get("filters"): + view_filters = view_settings.get("filters") + for view_filter in view_filters: + if ( + condition_fieldname + and view_filter[filter_dict[condition_fieldname]] != condition_values + ): + continue + if view_filter[filter_dict[fieldname]] == old: + view_filter[filter_dict[fieldname]] = new + update = True + if update: + xhiveframework.db.sql( + "update __UserSettings set data=%s where doctype=%s and user=%s", + (json.dumps(data), user_setting.doctype, user_setting.user), + ) + + # clear that user settings from the redis cache + xhiveframework.cache.hset("_user_settings", f"{user_setting.doctype}::{user_setting.user}", None) diff --git a/xhiveframework/model/virtual_doctype.py b/xhiveframework/model/virtual_doctype.py new file mode 100644 index 0000000..36fc73a --- /dev/null +++ b/xhiveframework/model/virtual_doctype.py @@ -0,0 +1,93 @@ +import inspect +from typing import Protocol, runtime_checkable + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.base_document import get_controller + + +@runtime_checkable +class VirtualDoctype(Protocol): + """This class documents requirements that must be met by a doctype controller to function as virtual doctype + + + Additional requirements: + - DocType controller has to inherit from `xhiveframework.model.document.Document` class + + Note: + - "Backend" here means any storage service, it can be a database, flat file or network call to API. + """ + + # ============ class/static methods ============ + + @staticmethod + def get_list(args) -> list[xhiveframework._dict]: + """Similar to reportview.get_list""" + ... + + @staticmethod + def get_count(args) -> int: + """Similar to reportview.get_count, return total count of documents on listview.""" + ... + + @staticmethod + def get_stats(args): + """Similar to reportview.get_stats, return sidebar stats.""" + ... + + # ============ instance methods ============ + + def db_insert(self, *args, **kwargs) -> None: + """Serialize the `Document` object and insert it in backend.""" + ... + + def load_from_db(self) -> None: + """Using self.name initialize current document from backend data. + + This is responsible for updatinng __dict__ of class with all the fields on doctype.""" + ... + + def db_update(self, *args, **kwargs) -> None: + """Serialize the `Document` object and update existing document in backend.""" + ... + + def delete(self, *args, **kwargs) -> None: + """Delete the current document from backend""" + ... + + +def validate_controller(doctype: str) -> None: + try: + controller = get_controller(doctype) + except ImportError: + xhiveframework.msgprint(_("Failed to import virtual doctype {}, is controller file present?").format(doctype)) + return + + def _as_str(method): + if hasattr(method, "__module__"): + return f"{method.__module__}.{method.__qualname__}" + return "None" + + expected_static_method = ["get_list", "get_count", "get_stats"] + for m in expected_static_method: + method = inspect.getattr_static(controller, m, None) + if not isinstance(method, staticmethod): + xhiveframework.msgprint( + _("Virtual DocType {} requires a static method called {} found {}").format( + xhiveframework.bold(doctype), xhiveframework.bold(m), xhiveframework.bold(_as_str(method)) + ), + title=_("Incomplete Virtual Doctype Implementation"), + ) + + expected_instance_methods = ["db_insert", "db_update", "load_from_db", "delete"] + parent_class = controller.mro()[1] + for m in expected_instance_methods: + method = getattr(controller, m, None) + original_method = getattr(parent_class, m, None) + if method == original_method: + xhiveframework.msgprint( + _("Virtual DocType {} requires overriding an instance method called {} found {}").format( + xhiveframework.bold(doctype), xhiveframework.bold(m), xhiveframework.bold(_as_str(method)) + ), + title=_("Incomplete Virtual Doctype Implementation"), + ) diff --git a/xhiveframework/model/workflow.py b/xhiveframework/model/workflow.py new file mode 100644 index 0000000..56e2069 --- /dev/null +++ b/xhiveframework/model/workflow.py @@ -0,0 +1,369 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import json +from collections import defaultdict +from typing import TYPE_CHECKING, Union + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.docstatus import DocStatus +from xhiveframework.utils import cint + +if TYPE_CHECKING: + from xhiveframework.model.document import Document + from xhiveframework.workflow.doctype.workflow.workflow import Workflow + + +class WorkflowStateError(xhiveframework.ValidationError): + pass + + +class WorkflowTransitionError(xhiveframework.ValidationError): + pass + + +class WorkflowPermissionError(xhiveframework.ValidationError): + pass + + +def get_workflow_name(doctype): + workflow_name = xhiveframework.cache.hget("workflow", doctype) + if workflow_name is None: + workflow_name = xhiveframework.db.get_value("Workflow", {"document_type": doctype, "is_active": 1}, "name") + xhiveframework.cache.hset("workflow", doctype, workflow_name or "") + + return workflow_name + + +@xhiveframework.whitelist() +def get_transitions( + doc: Union["Document", str, dict], workflow: "Workflow" = None, raise_exception: bool = False +) -> list[dict]: + """Return list of possible transitions for the given doc""" + from xhiveframework.model.document import Document + + if not isinstance(doc, Document): + doc = xhiveframework.get_doc(xhiveframework.parse_json(doc)) + doc.load_from_db() + + if doc.is_new(): + return [] + + doc.check_permission("read") + + workflow = workflow or get_workflow(doc.doctype) + current_state = doc.get(workflow.workflow_state_field) + + if not current_state: + if raise_exception: + raise WorkflowStateError + else: + xhiveframework.throw(_("Workflow State not set"), WorkflowStateError) + + transitions = [] + roles = xhiveframework.get_roles() + + for transition in workflow.transitions: + if transition.state == current_state and transition.allowed in roles: + if not is_transition_condition_satisfied(transition, doc): + continue + transitions.append(transition.as_dict()) + + return transitions + + +def get_workflow_safe_globals(): + # access to xhiveframework.db.get_value, xhiveframework.db.get_list, and date time utils. + return dict( + xhiveframework=xhiveframework._dict( + db=xhiveframework._dict(get_value=xhiveframework.db.get_value, get_list=xhiveframework.db.get_list), + session=xhiveframework.session, + utils=xhiveframework._dict( + now_datetime=xhiveframework.utils.now_datetime, + add_to_date=xhiveframework.utils.add_to_date, + get_datetime=xhiveframework.utils.get_datetime, + now=xhiveframework.utils.now, + ), + ) + ) + + +def is_transition_condition_satisfied(transition, doc) -> bool: + if not transition.condition: + return True + else: + return xhiveframework.safe_eval(transition.condition, get_workflow_safe_globals(), dict(doc=doc.as_dict())) + + +@xhiveframework.whitelist() +def apply_workflow(doc, action): + """Allow workflow action on the current doc""" + doc = xhiveframework.get_doc(xhiveframework.parse_json(doc)) + doc.load_from_db() + workflow = get_workflow(doc.doctype) + transitions = get_transitions(doc, workflow) + user = xhiveframework.session.user + + # find the transition + transition = None + for t in transitions: + if t.action == action: + transition = t + + if not transition: + xhiveframework.throw(_("Not a valid Workflow Action"), WorkflowTransitionError) + + if not has_approval_access(user, doc, transition): + xhiveframework.throw(_("Self approval is not allowed")) + + # update workflow state field + doc.set(workflow.workflow_state_field, transition.next_state) + + # find settings for the next state + next_state = next(d for d in workflow.states if d.state == transition.next_state) + + # update any additional field + if next_state.update_field: + doc.set(next_state.update_field, next_state.update_value) + + new_docstatus = cint(next_state.doc_status) + if doc.docstatus.is_draft() and new_docstatus == DocStatus.draft(): + doc.save() + elif doc.docstatus.is_draft() and new_docstatus == DocStatus.submitted(): + doc.submit() + elif doc.docstatus.is_submitted() and new_docstatus == DocStatus.submitted(): + doc.save() + elif doc.docstatus.is_submitted() and new_docstatus == DocStatus.cancelled(): + doc.cancel() + else: + xhiveframework.throw(_("Illegal Document Status for {0}").format(next_state.state)) + + doc.add_comment("Workflow", _(next_state.state)) + + return doc + + +@xhiveframework.whitelist() +def can_cancel_document(doctype): + workflow = get_workflow(doctype) + cancelling_states = [s.state for s in workflow.states if s.doc_status == "2"] + if not cancelling_states: + return True + + for transition in workflow.transitions: + if transition.next_state in cancelling_states: + return False + return True + + +def validate_workflow(doc): + """Validate Workflow State and Transition for the current user. + + - Check if user is allowed to edit in current state + - Check if user is allowed to transition to the next state (if changed) + """ + workflow = get_workflow(doc.doctype) + + current_state = None + if getattr(doc, "_doc_before_save", None): + current_state = doc._doc_before_save.get(workflow.workflow_state_field) + next_state = doc.get(workflow.workflow_state_field) + + if not next_state: + next_state = workflow.states[0].state + doc.set(workflow.workflow_state_field, next_state) + + if not current_state: + current_state = workflow.states[0].state + + state_row = [d for d in workflow.states if d.state == current_state] + if not state_row: + xhiveframework.throw( + _("{0} is not a valid Workflow State. Please update your Workflow and try again.").format( + xhiveframework.bold(current_state) + ) + ) + state_row = state_row[0] + + # if transitioning, check if user is allowed to transition + if current_state != next_state: + bold_current = xhiveframework.bold(current_state) + bold_next = xhiveframework.bold(next_state) + + if not doc._doc_before_save: + # transitioning directly to a state other than the first + # e.g from data import + xhiveframework.throw( + _("Workflow State transition not allowed from {0} to {1}").format(bold_current, bold_next), + WorkflowPermissionError, + ) + + transitions = get_transitions(doc._doc_before_save) + transition = [d for d in transitions if d.next_state == next_state] + if not transition: + xhiveframework.throw( + _("Workflow State transition not allowed from {0} to {1}").format(bold_current, bold_next), + WorkflowPermissionError, + ) + + +def get_workflow(doctype) -> "Workflow": + return xhiveframework.get_cached_doc("Workflow", get_workflow_name(doctype)) + + +def has_approval_access(user, doc, transition): + return user == "Administrator" or transition.get("allow_self_approval") or user != doc.get("owner") + + +def get_workflow_state_field(workflow_name): + return get_workflow_field_value(workflow_name, "workflow_state_field") + + +def send_email_alert(workflow_name): + return get_workflow_field_value(workflow_name, "send_email_alert") + + +def get_workflow_field_value(workflow_name, field): + return xhiveframework.get_cached_value("Workflow", workflow_name, field) + + +@xhiveframework.whitelist() +def bulk_workflow_approval(docnames, doctype, action): + docnames = json.loads(docnames) + if len(docnames) < 20: + _bulk_workflow_action(docnames, doctype, action) + elif len(docnames) <= 500: + xhiveframework.msgprint(_("Bulk {0} is enqueued in background.").format(action), alert=True) + xhiveframework.enqueue( + _bulk_workflow_action, + docnames=docnames, + doctype=doctype, + action=action, + queue="short", + timeout=1000, + ) + else: + xhiveframework.throw(_("Bulk approval only support up to 500 documents."), title=_("Too Many Documents")) + + +def _bulk_workflow_action(docnames, doctype, action): + # dictionaries for logging + failed_transactions = defaultdict(list) + successful_transactions = defaultdict(list) + + xhiveframework.clear_messages() + for idx, docname in enumerate(docnames, 1): + message_dict = {} + try: + show_progress(docnames, _("Applying: {0}").format(action), idx, docname) + apply_workflow(xhiveframework.get_doc(doctype, docname), action) + xhiveframework.db.commit() + except Exception as e: + if not xhiveframework.message_log: + # Exception is raised manually and not from msgprint or throw + message = f"{e.__class__.__name__}" + if e.args: + message += f" : {e.args[0]}" + message_dict = {"docname": docname, "message": message} + failed_transactions[docname].append(message_dict) + + xhiveframework.db.rollback() + xhiveframework.log_error( + title=f"Workflow {action} threw an error for {doctype} {docname}", + reference_doctype="Workflow", + reference_name=action, + ) + finally: + if not message_dict: + if xhiveframework.message_log: + messages = xhiveframework.get_message_log() + for message in messages: + xhiveframework.message_log.pop() + message_dict = {"docname": docname, "message": message.get("message")} + + if message.get("raise_exception", False): + failed_transactions[docname].append(message_dict) + else: + successful_transactions[docname].append(message_dict) + else: + successful_transactions[docname].append({"docname": docname, "message": None}) + + if failed_transactions and successful_transactions: + indicator = "orange" + elif failed_transactions: + indicator = "red" + else: + indicator = "green" + + print_workflow_log(failed_transactions, _("Failed Transactions"), doctype, indicator) + print_workflow_log(successful_transactions, _("Successful Transactions"), doctype, indicator) + + +def print_workflow_log(messages, title, doctype, indicator): + if messages.keys(): + msg = f"

      {title}

      " + + for doc in messages.keys(): + if len(messages[doc]): + html = f"
      {xhiveframework.utils.get_link_to_form(doctype, doc)}" + for log in messages[doc]: + if log.get("message"): + html += "
      {}
      ".format( + log.get("message") + ) + html += "
      " + else: + html = f"
      {doc}
      " + msg += html + + xhiveframework.msgprint( + msg, title=_("Workflow Status"), indicator=indicator, is_minimizable=True, realtime=True + ) + + +@xhiveframework.whitelist() +def get_common_transition_actions(docs, doctype): + common_actions = [] + if isinstance(docs, str): + docs = json.loads(docs) + try: + for i, doc in enumerate(docs, 1): + if not doc.get("doctype"): + doc["doctype"] = doctype + actions = [ + t.get("action") + for t in get_transitions(doc, raise_exception=True) + if has_approval_access(xhiveframework.session.user, doc, t) + ] + if not actions: + return [] + common_actions = actions if i == 1 else set(common_actions).intersection(actions) + if not common_actions: + return [] + except WorkflowStateError: + pass + + return list(common_actions) + + +def show_progress(docnames, message, i, description): + n = len(docnames) + if n >= 5: + xhiveframework.publish_progress(float(i) * 100 / n, title=message, description=description) + + +def set_workflow_state_on_action(doc, workflow_name, action): + workflow = xhiveframework.get_doc("Workflow", workflow_name) + workflow_state_field = workflow.workflow_state_field + + # If workflow state of doc is already correct, don't set workflow state + for state in workflow.states: + if state.state == doc.get(workflow_state_field) and doc.docstatus == cint(state.doc_status): + return + + action_map = {"update_after_submit": "1", "submit": "1", "cancel": "2"} + docstatus = action_map[action] + for state in workflow.states: + if state.doc_status == docstatus: + doc.set(workflow_state_field, state.state) + return diff --git a/xhiveframework/modules.txt b/xhiveframework/modules.txt new file mode 100644 index 0000000..863c448 --- /dev/null +++ b/xhiveframework/modules.txt @@ -0,0 +1,12 @@ +Core +Website +Workflow +Email +Custom +Geo +Desk +Integrations +Printing +Contacts +Social +Automation \ No newline at end of file diff --git a/xhiveframework/modules/__init__.py b/xhiveframework/modules/__init__.py new file mode 100644 index 0000000..16281fe --- /dev/null +++ b/xhiveframework/modules/__init__.py @@ -0,0 +1 @@ +from .utils import * diff --git a/xhiveframework/modules/export_file.py b/xhiveframework/modules/export_file.py new file mode 100644 index 0000000..c7ddc9b --- /dev/null +++ b/xhiveframework/modules/export_file.py @@ -0,0 +1,160 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import os +import shutil + +import xhiveframework +import xhiveframework.model +from xhiveframework.modules import get_module_path, scrub, scrub_dt_dn + + +def export_doc(doc): + write_document_file(doc) + + +def export_to_files(record_list=None, record_module=None, verbose=0, create_init=None): + """ + Export record_list to files. record_list is a list of lists ([doctype, docname, folder name],) , + """ + if xhiveframework.flags.in_import: + return + + if record_list: + for record in record_list: + folder_name = record[2] if len(record) == 3 else None + write_document_file( + xhiveframework.get_doc(record[0], record[1]), + record_module, + create_init=create_init, + folder_name=folder_name, + ) + + +def write_document_file(doc, record_module=None, create_init=True, folder_name=None): + doc_export = doc.as_dict(no_nulls=True) + doc.run_method("before_export", doc_export) + + doc_export = strip_default_fields(doc, doc_export) + module = record_module or get_module_name(doc) + + # create folder + if folder_name: + folder = create_folder(module, folder_name, doc.name, create_init) + else: + folder = create_folder(module, doc.doctype, doc.name, create_init) + + fname = scrub(doc.name) + write_code_files(folder, fname, doc, doc_export) + + # write the data file + path = os.path.join(folder, f"{fname}.json") + with open(path, "w+") as txtfile: + txtfile.write(xhiveframework.as_json(doc_export)) + print(f"Wrote document file for {doc.doctype} {doc.name} at {path}") + + +def strip_default_fields(doc, doc_export): + # strip out default fields from children + if doc.doctype == "DocType" and doc.migration_hash: + del doc_export["migration_hash"] + + for df in doc.meta.get_table_fields(): + for d in doc_export.get(df.fieldname): + for fieldname in xhiveframework.model.default_fields + xhiveframework.model.child_table_fields: + if fieldname in d: + del d[fieldname] + + return doc_export + + +def write_code_files(folder, fname, doc, doc_export): + """Export code files and strip from values""" + if hasattr(doc, "get_code_fields"): + for key, extn in doc.get_code_fields().items(): + if doc.get(key): + with open(os.path.join(folder, fname + "." + extn), "w+") as txtfile: + txtfile.write(doc.get(key)) + + # remove from exporting + del doc_export[key] + + +def get_module_name(doc): + if doc.doctype == "Module Def": + module = doc.name + elif doc.doctype == "Workflow": + module = xhiveframework.db.get_value("DocType", doc.document_type, "module") + elif hasattr(doc, "module"): + module = doc.module + else: + module = xhiveframework.db.get_value("DocType", doc.doctype, "module") + + return module + + +def delete_folder(module, dt, dn): + if xhiveframework.db.get_value("Module Def", module, "custom"): + module_path = get_custom_module_path(module) + else: + module_path = get_module_path(module) + + dt, dn = scrub_dt_dn(dt, dn) + + # delete folder + folder = os.path.join(module_path, dt, dn) + + if os.path.exists(folder): + shutil.rmtree(folder) + + +def create_folder(module, dt, dn, create_init): + if xhiveframework.db.get_value("Module Def", module, "custom"): + module_path = get_custom_module_path(module) + else: + module_path = get_module_path(module) + + dt, dn = scrub_dt_dn(dt, dn) + + # create folder + folder = os.path.join(module_path, dt, dn) + + xhiveframework.create_folder(folder) + + # create init_py_files + if create_init: + create_init_py(module_path, dt, dn) + + return folder + + +def get_custom_module_path(module): + package = xhiveframework.db.get_value("Module Def", module, "package") + if not package: + xhiveframework.throw(f"Package must be set for custom Module {module}") + + path = os.path.join(get_package_path(package), scrub(module)) + if not os.path.exists(path): + os.makedirs(path) + + return path + + +def get_package_path(package): + path = os.path.join( + xhiveframework.get_site_path("packages"), xhiveframework.db.get_value("Package", package, "package_name") + ) + if not os.path.exists(path): + os.makedirs(path) + return path + + +def create_init_py(module_path, dt, dn): + def create_if_not_exists(path): + initpy = os.path.join(path, "__init__.py") + if not os.path.exists(initpy): + open(initpy, "w").close() + + create_if_not_exists(os.path.join(module_path)) + create_if_not_exists(os.path.join(module_path, dt)) + create_if_not_exists(os.path.join(module_path, dt, dn)) diff --git a/xhiveframework/modules/import_file.py b/xhiveframework/modules/import_file.py new file mode 100644 index 0000000..0209205 --- /dev/null +++ b/xhiveframework/modules/import_file.py @@ -0,0 +1,287 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import hashlib +import json +import os + +import xhiveframework +from xhiveframework.model.base_document import get_controller +from xhiveframework.modules import get_module_path, scrub_dt_dn +from xhiveframework.query_builder import DocType +from xhiveframework.utils import get_datetime, now + + +def calculate_hash(path: str) -> str: + """Calculate md5 hash of the file in binary mode + + Args: + path (str): Path to the file to be hashed + + Returns: + str: The calculated hash + """ + hash_md5 = hashlib.md5(usedforsecurity=False) + with open(path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + + +ignore_values = { + "Report": ["disabled", "prepared_report", "add_total_row"], + "Print Format": ["disabled"], + "Notification": ["enabled"], + "Print Style": ["disabled"], + "Module Onboarding": ["is_complete"], + "Onboarding Step": ["is_complete", "is_skipped"], + "Workspace": ["is_hidden"], +} + +ignore_doctypes = [""] + + +def import_files(module, dt=None, dn=None, force=False, pre_process=None, reset_permissions=False): + if isinstance(module, list): + return [ + import_file( + m[0], + m[1], + m[2], + force=force, + pre_process=pre_process, + reset_permissions=reset_permissions, + ) + for m in module + ] + else: + return import_file( + module, dt, dn, force=force, pre_process=pre_process, reset_permissions=reset_permissions + ) + + +def import_file(module, dt, dn, force=False, pre_process=None, reset_permissions=False): + """Sync a file from txt if modifed, return false if not updated""" + path = get_file_path(module, dt, dn) + return import_file_by_path(path, force, pre_process=pre_process, reset_permissions=reset_permissions) + + +def get_file_path(module, dt, dn): + dt, dn = scrub_dt_dn(dt, dn) + + path = os.path.join(get_module_path(module), os.path.join(dt, dn, f"{dn}.json")) + + return path + + +def import_file_by_path( + path: str, + force: bool = False, + data_import: bool = False, + pre_process=None, + ignore_version: bool | None = None, + reset_permissions: bool = False, +): + """Import file from the given path + + Some conditions decide if a file should be imported or not. + Evaluation takes place in the order they are mentioned below. + + - Check if `force` is true. Import the file. If not, move ahead. + - Get `db_modified_timestamp`(value of the modified field in the database for the file). + If the return is `none,` this file doesn't exist in the DB, so Import the file. If not, move ahead. + - Check if there is a hash in DB for that file. If there is, Calculate the Hash of the file to import and compare it with the one in DB if they are not equal. + Import the file. If Hash doesn't exist, move ahead. + - Check if `db_modified_timestamp` is older than the timestamp in the file; if it is, we import the file. + + If timestamp comparison happens for doctypes, that means the Hash for it doesn't exist. + So, even if the timestamp is newer on DB (When comparing timestamps), we import the file and add the calculated Hash to the DB. + So in the subsequent imports, we can use hashes to compare. As a precautionary measure, the timestamp is updated to the current time as well. + + Args: + path (str): Path to the file. + force (bool, optional): Load the file without checking any conditions. Defaults to False. + data_import (bool, optional): [description]. Defaults to False. + pre_process ([type], optional): Any preprocesing that may need to take place on the doc. Defaults to None. + ignore_version (bool, optional): ignore current version. Defaults to None. + reset_permissions (bool, optional): reset permissions for the file. Defaults to False. + + Returns: + [bool]: True if import takes place. False if it wasn't imported. + """ + try: + docs = read_doc_from_file(path) + except OSError: + print(f"{path} missing") + return + + calculated_hash = calculate_hash(path) + + if docs: + if not isinstance(docs, list): + docs = [docs] + + for doc in docs: + # modified timestamp in db, none if doctype's first import + db_modified_timestamp = xhiveframework.db.get_value(doc["doctype"], doc["name"], "modified") + is_db_timestamp_latest = db_modified_timestamp and ( + get_datetime(doc.get("modified")) <= get_datetime(db_modified_timestamp) + ) + + if not force and db_modified_timestamp: + stored_hash = None + if doc["doctype"] == "DocType": + try: + stored_hash = xhiveframework.db.get_value(doc["doctype"], doc["name"], "migration_hash") + except Exception: + pass + + # if hash exists and is equal no need to update + if stored_hash and stored_hash == calculated_hash: + continue + + # if hash doesn't exist, check if db timestamp is same as json timestamp, add hash if from doctype + if is_db_timestamp_latest and doc["doctype"] != "DocType": + continue + + import_doc( + docdict=doc, + data_import=data_import, + pre_process=pre_process, + ignore_version=ignore_version, + reset_permissions=reset_permissions, + path=path, + ) + + if doc["doctype"] == "DocType": + doctype_table = DocType("DocType") + xhiveframework.qb.update(doctype_table).set(doctype_table.migration_hash, calculated_hash).where( + doctype_table.name == doc["name"] + ).run() + + new_modified_timestamp = doc.get("modified") + + # if db timestamp is newer, hash must have changed, must update db timestamp + if is_db_timestamp_latest and doc["doctype"] == "DocType": + new_modified_timestamp = now() + + if new_modified_timestamp: + update_modified(new_modified_timestamp, doc) + + return True + + +def read_doc_from_file(path): + doc = None + if os.path.exists(path): + with open(path) as f: + try: + doc = json.loads(f.read()) + except ValueError: + print(f"bad json: {path}") + raise + else: + raise OSError("%s missing" % path) + + return doc + + +def update_modified(original_modified, doc): + # since there is a new timestamp on the file, update timestamp in + if doc["doctype"] == doc["name"] and doc["name"] != "DocType": + singles_table = DocType("Singles") + + xhiveframework.qb.update(singles_table).set(singles_table.value, original_modified).where( + singles_table["field"] == "modified", # singles_table.field is a method of pypika Selectable + ).where(singles_table.doctype == doc["name"]).run() + else: + doctype_table = DocType(doc["doctype"]) + + xhiveframework.qb.update(doctype_table).set(doctype_table.modified, original_modified).where( + doctype_table.name == doc["name"] + ).run() + + +def import_doc( + docdict, + data_import=False, + pre_process=None, + ignore_version=None, + reset_permissions=False, + path=None, +): + xhiveframework.flags.in_import = True + docdict["__islocal"] = 1 + + controller = get_controller(docdict["doctype"]) + if controller and hasattr(controller, "prepare_for_import") and callable(controller.prepare_for_import): + controller.prepare_for_import(docdict) + + doc = xhiveframework.get_doc(docdict) + + reset_tree_properties(doc) + load_code_properties(doc, path) + + doc.run_method("before_import") + + doc.flags.ignore_version = ignore_version + if pre_process: + pre_process(doc) + + if xhiveframework.db.exists(doc.doctype, doc.name): + delete_old_doc(doc, reset_permissions) + + doc.flags.ignore_links = True + if not data_import: + doc.flags.ignore_validate = True + doc.flags.ignore_permissions = True + doc.flags.ignore_mandatory = True + + doc.insert() + + xhiveframework.flags.in_import = False + + return doc + + +def load_code_properties(doc, path): + """Load code files stored in separate files with extensions""" + if path: + if hasattr(doc, "get_code_fields"): + dirname, filename = os.path.split(path) + for key, extn in doc.get_code_fields().items(): + codefile = os.path.join(dirname, filename.split(".", 1)[0] + "." + extn) + if os.path.exists(codefile): + with open(codefile) as txtfile: + doc.set(key, txtfile.read()) + + +def delete_old_doc(doc, reset_permissions): + ignore = [] + old_doc = xhiveframework.get_doc(doc.doctype, doc.name) + + if doc.doctype in ignore_values: + # update ignore values + for key in ignore_values.get(doc.doctype) or []: + doc.set(key, old_doc.get(key)) + + # update ignored docs into new doc + for df in doc.meta.get_table_fields(): + if df.options in ignore_doctypes and not reset_permissions: + doc.set(df.fieldname, []) + ignore.append(df.options) + + # delete old + xhiveframework.delete_doc(doc.doctype, doc.name, force=1, ignore_doctypes=ignore, for_reload=True) + + doc.flags.ignore_children_type = ignore + + +def reset_tree_properties(doc): + # Note on Tree DocTypes: + # The tree structure is maintained in the database via the fields "lft" and + # "rgt". They are automatically set and kept up-to-date. Importing them + # would destroy any existing tree structure. + if getattr(doc.meta, "is_tree", None) and any([doc.lft, doc.rgt]): + print(f'Ignoring values of `lft` and `rgt` for {doc.doctype} "{doc.name}"') + doc.lft = None + doc.rgt = None diff --git a/xhiveframework/modules/patch_handler.py b/xhiveframework/modules/patch_handler.py new file mode 100644 index 0000000..2e104e8 --- /dev/null +++ b/xhiveframework/modules/patch_handler.py @@ -0,0 +1,234 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +""" Patch Handler. + +This file manages execution of manaully written patches. Patches are script +that apply changes in database schema or data to accomodate for changes in the +code. + +Ways to specify patches: + +1. patches.txt file specifies patches that run before doctype schema +migration. Each line represents one patch (old format). +2. patches.txt can alternatively also separate pre and post model sync +patches by using INI like file format: + ```patches.txt + [pre_model_sync] + app.module.patch1 + app.module.patch2 + + + [post_model_sync] + app.module.patch3 + ``` + + When different sections are specified patches are executed in this order: + 1. Run pre_model_sync patches + 2. Reload/resync all doctype schema + 3. Run post_model_sync patches + + Hence any patch that just needs to modify data but doesn't depend on + old schema should be added to post_model_sync section of file. + +3. simple python commands can be added by starting line with `execute:` +`execute:` example: `execute:print("hello world")` +""" + +import configparser +import time +from enum import Enum +from textwrap import dedent, indent + +import xhiveframework + + +class PatchError(Exception): + pass + + +class PatchType(Enum): + pre_model_sync = "pre_model_sync" + post_model_sync = "post_model_sync" + + +def run_all(skip_failing: bool = False, patch_type: PatchType | None = None) -> None: + """run all pending patches""" + executed = set(xhiveframework.get_all("Patch Log", filters={"skipped": 0}, fields="patch", pluck="patch")) + + xhiveframework.flags.final_patches = [] + + def run_patch(patch): + try: + if not run_single(patchmodule=patch): + print(patch + ": failed: STOPPED") + raise PatchError(patch) + except Exception: + if not skip_failing: + raise + + print("Failed to execute patch") + update_patch_log(patch, skipped=True) + + patches = get_all_patches(patch_type=patch_type) + + for patch in patches: + if patch and (patch not in executed): + run_patch(patch) + + # patches to be run in the end + for patch in xhiveframework.flags.final_patches: + patch = patch.replace("finally:", "") + run_patch(patch) + + +def get_all_patches(patch_type: PatchType | None = None) -> list[str]: + if patch_type and not isinstance(patch_type, PatchType): + xhiveframework.throw(f"Unsupported patch type specified: {patch_type}") + + patches = [] + for app in xhiveframework.get_installed_apps(): + patches.extend(get_patches_from_app(app, patch_type=patch_type)) + + return patches + + +def get_patches_from_app(app: str, patch_type: PatchType | None = None) -> list[str]: + """Get patches from an app's patches.txt + + patches.txt can be: + 1. ini like file with section for different patch_type + 2. plain text file with each line representing a patch. + """ + patches_file = xhiveframework.get_app_path(app, "patches.txt") + + try: + return parse_as_configfile(patches_file, patch_type) + except configparser.MissingSectionHeaderError: + # treat as old format with each line representing a single patch + # backward compatbility with old patches.txt format + if not patch_type or patch_type == PatchType.pre_model_sync: + return xhiveframework.get_file_items(patches_file) + + return [] + + +def parse_as_configfile(patches_file: str, patch_type: PatchType | None = None) -> list[str]: + # Attempt to parse as ini file with pre/post patches + # allow_no_value: patches are not key value pairs + # delimiters = '\n' to avoid treating default `:` and `=` in execute as k:v delimiter + parser = configparser.ConfigParser(allow_no_value=True, delimiters="\n") + # preserve case + parser.optionxform = str + parser.read(patches_file) + + # empty file + if not parser.sections(): + return [] + + if not patch_type: + return [patch for patch in parser[PatchType.pre_model_sync.value]] + [ + patch for patch in parser[PatchType.post_model_sync.value] + ] + + if patch_type.value in parser.sections(): + return [patch for patch in parser[patch_type.value]] + else: + xhiveframework.throw(xhiveframework._("Patch type {} not found in patches.txt").format(patch_type)) + + +def reload_doc(args): + import xhiveframework.modules + + run_single(method=xhiveframework.modules.reload_doc, methodargs=args) + + +def run_single(patchmodule=None, method=None, methodargs=None, force=False): + from xhiveframework import conf + + # don't write txt files + conf.developer_mode = 0 + + if force or method or not executed(patchmodule): + return execute_patch(patchmodule, method, methodargs) + else: + return True + + +def execute_patch(patchmodule: str, method=None, methodargs=None): + """execute the patch""" + _patch_mode(True) + + if patchmodule.startswith("execute:"): + has_patch_file = False + patch = patchmodule.split("execute:")[1] + docstring = "" + else: + has_patch_file = True + patch = f"{patchmodule.split(maxsplit=1)[0]}.execute" + _patch = xhiveframework.get_attr(patch) + docstring = _patch.__doc__ or "" + + if docstring: + docstring = "\n" + indent(dedent(docstring), "\t") + + print( + f"Executing {patchmodule or methodargs} in {xhiveframework.local.site} ({xhiveframework.db.cur_db_name}){docstring}" + ) + + start_time = time.monotonic() + xhiveframework.db.begin() + xhiveframework.db.auto_commit_on_many_writes = 0 + try: + if patchmodule: + if patchmodule.startswith("finally:"): + # run run patch at the end + xhiveframework.flags.final_patches.append(patchmodule) + else: + if has_patch_file: + _patch() + else: + exec(patch, globals()) + update_patch_log(patchmodule) + + elif method: + method(**methodargs) + + except Exception: + xhiveframework.db.rollback() + raise + + else: + xhiveframework.db.commit() + end_time = time.monotonic() + _patch_mode(False) + print(f"Success: Done in {round(end_time - start_time, 3)}s") + + return True + + +def update_patch_log(patchmodule, skipped=False): + """update patch_file in patch log""" + + patch = xhiveframework.get_doc({"doctype": "Patch Log", "patch": patchmodule}) + + if skipped: + traceback = xhiveframework.get_traceback(with_context=True) + patch.skipped = 1 + patch.traceback = traceback + print(traceback, end="\n\n") + + patch.insert(ignore_permissions=True) + + +def executed(patchmodule): + """return True if is executed""" + if patchmodule.startswith("finally:"): + # patches are saved without the finally: tag + patchmodule = patchmodule.replace("finally:", "") + return xhiveframework.db.get_value("Patch Log", {"patch": patchmodule, "skipped": 0}) + + +def _patch_mode(enable): + """stop/start execution till patch is run""" + xhiveframework.local.flags.in_patch = enable + xhiveframework.db.commit() diff --git a/xhiveframework/modules/utils.py b/xhiveframework/modules/utils.py new file mode 100644 index 0000000..0145909 --- /dev/null +++ b/xhiveframework/modules/utils.py @@ -0,0 +1,335 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +""" + Utilities for using modules +""" +import json +import os +from textwrap import dedent, indent +from typing import TYPE_CHECKING, Union + +import xhiveframework +from xhiveframework import _, get_module_path, scrub +from xhiveframework.utils import cint, cstr, now_datetime + +if TYPE_CHECKING: + from types import ModuleType + + from xhiveframework.model.document import Document + + +doctype_python_modules = {} + + +def export_module_json(doc: "Document", is_standard: bool, module: str) -> str | None: + """Make a folder for the given doc and add its json file (make it a standard + object that will be synced) + + Returns the absolute file_path without the extension. + Eg: For exporting a Print Format "_Test Print Format 1", the return value will be + `/home/gavin/xhiveframework-bench/apps/xhiveframework/xhiveframework/core/print_format/_test_print_format_1/_test_print_format_1` + """ + if not xhiveframework.flags.in_import and is_standard and xhiveframework.conf.developer_mode: + from xhiveframework.modules.export_file import export_to_files + + # json + export_to_files(record_list=[[doc.doctype, doc.name]], record_module=module, create_init=is_standard) + + return os.path.join( + xhiveframework.get_module_path(module), scrub(doc.doctype), scrub(doc.name), scrub(doc.name) + ) + + +def get_doc_module(module: str, doctype: str, name: str) -> "ModuleType": + """Get custom module for given document""" + module_name = "{app}.{module}.{doctype}.{name}.{name}".format( + app=xhiveframework.local.module_app[scrub(module)], + doctype=scrub(doctype), + module=scrub(module), + name=scrub(name), + ) + return xhiveframework.get_module(module_name) + + +@xhiveframework.whitelist() +def export_customizations( + module: str, doctype: str, sync_on_migrate: bool = False, with_permissions: bool = False +): + """Export Custom Field and Property Setter for the current document to the app folder. + This will be synced with bench migrate""" + + sync_on_migrate = cint(sync_on_migrate) + with_permissions = cint(with_permissions) + + if not xhiveframework.conf.developer_mode: + xhiveframework.throw(_("Only allowed to export customizations in developer mode")) + + custom = { + "custom_fields": xhiveframework.get_all("Custom Field", fields="*", filters={"dt": doctype}), + "property_setters": xhiveframework.get_all("Property Setter", fields="*", filters={"doc_type": doctype}), + "custom_perms": [], + "links": xhiveframework.get_all("DocType Link", fields="*", filters={"parent": doctype}), + "doctype": doctype, + "sync_on_migrate": sync_on_migrate, + } + + if with_permissions: + custom["custom_perms"] = xhiveframework.get_all("Custom DocPerm", fields="*", filters={"parent": doctype}) + + # also update the custom fields and property setters for all child tables + for d in xhiveframework.get_meta(doctype).get_table_fields(): + export_customizations(module, d.options, sync_on_migrate, with_permissions) + + if custom["custom_fields"] or custom["property_setters"] or custom["custom_perms"]: + folder_path = os.path.join(get_module_path(module), "custom") + if not os.path.exists(folder_path): + os.makedirs(folder_path) + + path = os.path.join(folder_path, scrub(doctype) + ".json") + with open(path, "w") as f: + f.write(xhiveframework.as_json(custom)) + + xhiveframework.msgprint(_("Customizations for {0} exported to:
      {1}").format(doctype, path)) + return path + + +def sync_customizations(app=None): + """Sync custom fields and property setters from custom folder in each app module""" + + if app: + apps = [app] + else: + apps = xhiveframework.get_installed_apps() + + for app_name in apps: + for module_name in xhiveframework.local.app_modules.get(app_name) or []: + folder = xhiveframework.get_app_path(app_name, module_name, "custom") + if os.path.exists(folder): + for fname in os.listdir(folder): + if fname.endswith(".json"): + with open(os.path.join(folder, fname)) as f: + data = json.loads(f.read()) + if data.get("sync_on_migrate"): + sync_customizations_for_doctype(data, folder, fname) + + +def sync_customizations_for_doctype(data: dict, folder: str, filename: str = ""): + """Sync doctype customzations for a particular data set""" + from xhiveframework.core.doctype.doctype.doctype import validate_fields_for_doctype + + doctype = data["doctype"] + update_schema = False + + def sync(key, custom_doctype, doctype_fieldname): + doctypes = list(set(map(lambda row: row.get(doctype_fieldname), data[key]))) + + # sync single doctype exculding the child doctype + def sync_single_doctype(doc_type): + def _insert(data): + if data.get(doctype_fieldname) == doc_type: + data["doctype"] = custom_doctype + doc = xhiveframework.get_doc(data) + doc.db_insert() + + if custom_doctype != "Custom Field": + xhiveframework.db.delete(custom_doctype, {doctype_fieldname: doc_type}) + + for d in data[key]: + _insert(d) + + else: + for d in data[key]: + field = xhiveframework.db.get_value("Custom Field", {"dt": doc_type, "fieldname": d["fieldname"]}) + if not field: + d["owner"] = "Administrator" + _insert(d) + else: + custom_field = xhiveframework.get_doc("Custom Field", field) + custom_field.flags.ignore_validate = True + custom_field.update(d) + custom_field.db_update() + + for doc_type in doctypes: + # only sync the parent doctype and child doctype if there isn't any other child table json file + if doc_type == doctype or not os.path.exists(os.path.join(folder, scrub(doc_type) + ".json")): + sync_single_doctype(doc_type) + + if not xhiveframework.db.exists("DocType", doctype): + print(_("DocType {0} does not exist.").format(doctype)) + print(_("Skipping fixture syncing for doctype {0} from file {1}").format(doctype, filename)) + return + + if data["custom_fields"]: + sync("custom_fields", "Custom Field", "dt") + update_schema = True + + if data["property_setters"]: + sync("property_setters", "Property Setter", "doc_type") + + print(f"Updating customizations for {doctype}") + if data.get("custom_perms"): + sync("custom_perms", "Custom DocPerm", "parent") + + validate_fields_for_doctype(doctype) + + if update_schema and not xhiveframework.db.get_value("DocType", doctype, "issingle"): + xhiveframework.db.updatedb(doctype) + + +def scrub_dt_dn(dt: str, dn: str) -> tuple[str, str]: + """Returns in lowercase and code friendly names of doctype and name for certain types""" + return scrub(dt), scrub(dn) + + +def get_doc_path(module: str, doctype: str, name: str) -> str: + """Returns path of a doc in a module""" + return os.path.join(get_module_path(module), *scrub_dt_dn(doctype, name)) + + +def reload_doc( + module: str, + dt: str | None = None, + dn: str | None = None, + force: bool = False, + reset_permissions: bool = False, +): + """Reload Document from model (`[module]//[name]/[name].json`) files""" + from xhiveframework.modules.import_file import import_files + + return import_files(module, dt, dn, force=force, reset_permissions=reset_permissions) + + +def export_doc(doctype, name, module=None): + """Write a doc to standard path.""" + from xhiveframework.modules.export_file import write_document_file + + print(f"Exporting Document {doctype} {name}") + module = module or xhiveframework.db.get_value("DocType", name, "module") + write_document_file(xhiveframework.get_doc(doctype, name), module) + + +def get_doctype_module(doctype: str) -> str: + """Returns **Module Def** name of given doctype.""" + doctype_module_map = xhiveframework.cache.get_value( + "doctype_modules", + generator=lambda: dict(xhiveframework.qb.from_("DocType").select("name", "module").run()), + ) + + if module_name := doctype_module_map.get(doctype): + return module_name + else: + xhiveframework.throw(_("DocType {} not found").format(doctype), exc=xhiveframework.DoesNotExistError) + + +def load_doctype_module(doctype, module=None, prefix="", suffix=""): + """Returns the module object for given doctype. + + Note: This will return the standard defined module object for the doctype irrespective + of the `override_doctype_class` hook. + """ + module = module or get_doctype_module(doctype) + app = get_module_app(module) + key = (app, doctype, prefix, suffix) + module_name = get_module_name(doctype, module, prefix, suffix) + + if key not in doctype_python_modules: + try: + doctype_python_modules[key] = xhiveframework.get_module(module_name) + except ImportError as e: + msg = f"Module import failed for {doctype}, the DocType you're trying to open might be deleted." + msg += f"
      Error: {e}" + raise ImportError(msg) from e + + return doctype_python_modules[key] + + +def get_module_name(doctype: str, module: str, prefix: str = "", suffix: str = "", app: str | None = None): + app = scrub(app or get_module_app(module)) + module = scrub(module) + doctype = scrub(doctype) + return f"{app}.{module}.doctype.{doctype}.{prefix}{doctype}{suffix}" + + +def get_module_app(module: str) -> str: + app = xhiveframework.local.module_app.get(scrub(module)) + if app is None: + xhiveframework.throw(_("Module {} not found").format(module), exc=xhiveframework.DoesNotExistError) + return app + + +def get_app_publisher(module: str) -> str: + app = get_module_app(module) + if not app: + xhiveframework.throw(_("App not found for module: {0}").format(module)) + return xhiveframework.get_hooks(hook="app_publisher", app_name=app)[0] + + +def make_boilerplate( + template: str, doc: Union["Document", "xhiveframework._dict"], opts: Union[dict, "xhiveframework._dict"] = None +): + target_path = get_doc_path(doc.module, doc.doctype, doc.name) + template_name = template.replace("controller", scrub(doc.name)) + if template_name.endswith("._py"): + template_name = template_name[:-4] + ".py" + target_file_path = os.path.join(target_path, template_name) + template_file_path = os.path.join( + get_module_path("core"), "doctype", scrub(doc.doctype), "boilerplate", template + ) + + if os.path.exists(target_file_path): + print(f"{target_file_path} already exists, skipping...") + return + + doc = doc or xhiveframework._dict() + opts = opts or xhiveframework._dict() + app_publisher = get_app_publisher(doc.module) + base_class = "Document" + base_class_import = "from xhiveframework.model.document import Document" + controller_body = "pass" + + if doc.get("is_tree"): + base_class = "NestedSet" + base_class_import = "from xhiveframework.utils.nestedset import NestedSet" + + if doc.get("is_virtual"): + controller_body = indent( + dedent( + """ + def db_insert(self, *args, **kwargs): + pass + + def load_from_db(self): + pass + + def db_update(self): + pass + + @staticmethod + def get_list(args): + pass + + @staticmethod + def get_count(args): + pass + + @staticmethod + def get_stats(args): + pass + """ + ), + "\t", + ) + + with open(target_file_path, "w") as target, open(template_file_path) as source: + template = source.read() + controller_file_content = cstr(template).format( + app_publisher=app_publisher, + year=now_datetime().year, + classname=doc.name.replace(" ", "").replace("-", ""), + base_class_import=base_class_import, + base_class=base_class, + doctype=doc.name, + **opts, + custom_controller=controller_body, + ) + target.write(xhiveframework.as_unicode(controller_file_content)) diff --git a/xhiveframework/monitor.py b/xhiveframework/monitor.py new file mode 100644 index 0000000..9d73f12 --- /dev/null +++ b/xhiveframework/monitor.py @@ -0,0 +1,135 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json +import os +import traceback +import uuid +from datetime import datetime + +import rq + +import xhiveframework + +MONITOR_REDIS_KEY = "monitor-transactions" +MONITOR_MAX_ENTRIES = 1000000 + + +def start(transaction_type="request", method=None, kwargs=None): + if xhiveframework.conf.monitor: + xhiveframework.local.monitor = Monitor(transaction_type, method, kwargs) + + +def stop(response=None): + if hasattr(xhiveframework.local, "monitor"): + xhiveframework.local.monitor.dump(response) + + +def add_data_to_monitor(**kwargs) -> None: + """Add additional custom key-value pairs along with monitor log. + Note: Key-value pairs should be simple JSON exportable types.""" + if hasattr(xhiveframework.local, "monitor"): + xhiveframework.local.monitor.add_custom_data(**kwargs) + + +def get_trace_id() -> str | None: + """Get unique ID for current transaction.""" + if monitor := getattr(xhiveframework.local, "monitor", None): + return monitor.data.uuid + + +def log_file(): + return os.path.join(xhiveframework.utils.get_bench_path(), "logs", "monitor.json.log") + + +class Monitor: + __slots__ = ("data",) + + def __init__(self, transaction_type, method, kwargs): + try: + self.data = xhiveframework._dict( + { + "site": xhiveframework.local.site, + "timestamp": datetime.utcnow(), + "transaction_type": transaction_type, + "uuid": str(uuid.uuid4()), + } + ) + + if transaction_type == "request": + self.collect_request_meta() + else: + self.collect_job_meta(method, kwargs) + except Exception: + traceback.print_exc() + + def collect_request_meta(self): + self.data.request = xhiveframework._dict( + { + "ip": xhiveframework.local.request_ip, + "method": xhiveframework.request.method, + "path": xhiveframework.request.path, + } + ) + + if request_id := xhiveframework.request.headers.get("X-XhiveFramework-Request-Id"): + self.data.uuid = request_id + + def collect_job_meta(self, method, kwargs): + self.data.job = xhiveframework._dict({"method": method, "scheduled": False, "wait": 0}) + if "run_scheduled_job" in method: + self.data.job.method = kwargs["job_type"] + self.data.job.scheduled = True + + if job := rq.get_current_job(): + self.data.uuid = job.id + waitdiff = self.data.timestamp - job.enqueued_at + self.data.job.wait = int(waitdiff.total_seconds() * 1000000) + + def add_custom_data(self, **kwargs): + if self.data: + self.data.update(kwargs) + + def dump(self, response=None): + try: + timediff = datetime.utcnow() - self.data.timestamp + # Obtain duration in microseconds + self.data.duration = int(timediff.total_seconds() * 1000000) + + if self.data.transaction_type == "request": + if response: + self.data.request.status_code = response.status_code + self.data.request.response_length = int(response.headers.get("Content-Length", 0)) + else: + self.data.request.status_code = 500 + + if hasattr(xhiveframework.local, "rate_limiter"): + limiter = xhiveframework.local.rate_limiter + self.data.request.counter = limiter.counter + if limiter.rejected: + self.data.request.reset = limiter.reset + + self.store() + except Exception: + traceback.print_exc() + + def store(self): + if xhiveframework.cache.llen(MONITOR_REDIS_KEY) > MONITOR_MAX_ENTRIES: + xhiveframework.cache.ltrim(MONITOR_REDIS_KEY, 1, -1) + serialized = json.dumps(self.data, sort_keys=True, default=str, separators=(",", ":")) + xhiveframework.cache.rpush(MONITOR_REDIS_KEY, serialized) + + +def flush(): + try: + # Fetch all the logs without removing from cache + logs = xhiveframework.cache.lrange(MONITOR_REDIS_KEY, 0, -1) + if logs: + logs = list(map(xhiveframework.safe_decode, logs)) + with open(log_file(), "a", os.O_NONBLOCK) as f: + f.write("\n".join(logs)) + f.write("\n") + # Remove fetched entries from cache + xhiveframework.cache.ltrim(MONITOR_REDIS_KEY, len(logs) - 1, -1) + except Exception: + traceback.print_exc() diff --git a/xhiveframework/oauth.py b/xhiveframework/oauth.py new file mode 100644 index 0000000..a3bad53 --- /dev/null +++ b/xhiveframework/oauth.py @@ -0,0 +1,592 @@ +import base64 +import datetime +import hashlib +import re +from http import cookies +from urllib.parse import unquote, urljoin, urlparse + +import jwt +import pytz +from oauthlib.openid import RequestValidator + +import xhiveframework +from xhiveframework.auth import LoginManager +from xhiveframework.utils.data import get_system_timezone, now_datetime + + +class OAuthWebRequestValidator(RequestValidator): + # Pre- and post-authorization. + def validate_client_id(self, client_id, request, *args, **kwargs): + # Simple validity check, does client exist? Not banned? + cli_id = xhiveframework.db.get_value("OAuth Client", {"name": client_id}) + if cli_id: + request.client = xhiveframework.get_doc("OAuth Client", client_id).as_dict() + return True + else: + return False + + def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs): + # Is the client allowed to use the supplied redirect_uri? i.e. has + # the client previously registered this EXACT redirect uri. + + redirect_uris = xhiveframework.db.get_value("OAuth Client", client_id, "redirect_uris").split( + get_url_delimiter() + ) + + if redirect_uri in redirect_uris: + return True + else: + return False + + def get_default_redirect_uri(self, client_id, request, *args, **kwargs): + # The redirect used if none has been supplied. + # Prefer your clients to pre register a redirect uri rather than + # supplying one on each authorization request. + return xhiveframework.db.get_value("OAuth Client", client_id, "default_redirect_uri") + + def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): + # Is the client allowed to access the requested scopes? + allowed_scopes = get_client_scopes(client_id) + return all(scope in allowed_scopes for scope in scopes) + + def get_default_scopes(self, client_id, request, *args, **kwargs): + # Scopes a client will authorize for if none are supplied in the + # authorization request. + scopes = get_client_scopes(client_id) + request.scopes = scopes # Apparently this is possible. + return scopes + + def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs): + allowed_response_types = [ + # From OAuth Client response_type field + client.response_type.lower(), + # OIDC + "id_token", + "id_token token", + "code id_token", + "code token id_token", + ] + + return response_type in allowed_response_types + + # Post-authorization + + def save_authorization_code(self, client_id, code, request, *args, **kwargs): + cookie_dict = get_cookie_dict_from_headers(request) + + oac = xhiveframework.new_doc("OAuth Authorization Code") + oac.scopes = get_url_delimiter().join(request.scopes) + oac.redirect_uri_bound_to_authorization_code = request.redirect_uri + oac.client = client_id + oac.user = unquote(cookie_dict["user_id"].value) + oac.authorization_code = code["code"] + + if request.nonce: + oac.nonce = request.nonce + + if request.code_challenge and request.code_challenge_method: + oac.code_challenge = request.code_challenge + oac.code_challenge_method = request.code_challenge_method.lower() + + oac.save(ignore_permissions=True) + xhiveframework.db.commit() + + def authenticate_client(self, request, *args, **kwargs): + # Get ClientID in URL + if request.client_id: + oc = xhiveframework.get_doc("OAuth Client", request.client_id) + else: + # Extract token, instantiate OAuth Bearer Token and use clientid from there. + if "refresh_token" in xhiveframework.form_dict: + oc = xhiveframework.get_doc( + "OAuth Client", + xhiveframework.db.get_value( + "OAuth Bearer Token", + {"refresh_token": xhiveframework.form_dict["refresh_token"]}, + "client", + ), + ) + elif "token" in xhiveframework.form_dict: + oc = xhiveframework.get_doc( + "OAuth Client", + xhiveframework.db.get_value("OAuth Bearer Token", xhiveframework.form_dict["token"], "client"), + ) + else: + oc = xhiveframework.get_doc( + "OAuth Client", + xhiveframework.db.get_value( + "OAuth Bearer Token", + xhiveframework.get_request_header("Authorization").split(" ")[1], + "client", + ), + ) + try: + request.client = request.client or oc.as_dict() + except Exception as e: + return generate_json_error_response(e) + + cookie_dict = get_cookie_dict_from_headers(request) + user_id = unquote(cookie_dict.get("user_id").value) if "user_id" in cookie_dict else "Guest" + return xhiveframework.session.user == user_id + + def authenticate_client_id(self, client_id, request, *args, **kwargs): + cli_id = xhiveframework.db.get_value("OAuth Client", client_id, "name") + if not cli_id: + # Don't allow public (non-authenticated) clients + return False + else: + request["client"] = xhiveframework.get_doc("OAuth Client", cli_id) + return True + + def validate_code(self, client_id, code, client, request, *args, **kwargs): + # Validate the code belongs to the client. Add associated scopes, + # state and user to request.scopes and request.user. + + validcodes = xhiveframework.get_all( + "OAuth Authorization Code", + filters={"client": client_id, "validity": "Valid"}, + ) + + if code in [vcode["name"] for vcode in validcodes]: + request.scopes = xhiveframework.db.get_value("OAuth Authorization Code", code, "scopes").split( + get_url_delimiter() + ) + request.user = xhiveframework.db.get_value("OAuth Authorization Code", code, "user") + code_challenge_method = xhiveframework.db.get_value( + "OAuth Authorization Code", code, "code_challenge_method" + ) + code_challenge = xhiveframework.db.get_value("OAuth Authorization Code", code, "code_challenge") + + if code_challenge and not request.code_verifier: + if xhiveframework.db.exists("OAuth Authorization Code", code): + xhiveframework.delete_doc("OAuth Authorization Code", code, ignore_permissions=True) + xhiveframework.db.commit() + return False + + if code_challenge_method == "s256": + m = hashlib.sha256() + m.update(bytes(request.code_verifier, "utf-8")) + code_verifier = base64.b64encode(m.digest()).decode("utf-8") + code_verifier = re.sub(r"\+", "-", code_verifier) + code_verifier = re.sub(r"\/", "_", code_verifier) + code_verifier = re.sub(r"=", "", code_verifier) + return code_challenge == code_verifier + + elif code_challenge_method == "plain": + return code_challenge == request.code_verifier + + return True + + return False + + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs): + saved_redirect_uri = xhiveframework.db.get_value("OAuth Client", client_id, "default_redirect_uri") + + redirect_uris = xhiveframework.db.get_value("OAuth Client", client_id, "redirect_uris") + + if redirect_uris: + redirect_uris = redirect_uris.split(get_url_delimiter()) + return redirect_uri in redirect_uris + + return saved_redirect_uri == redirect_uri + + def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs): + # Clients should only be allowed to use one type of grant. + # In this case, it must be "authorization_code" or "refresh_token" + return grant_type in ["authorization_code", "refresh_token", "password"] + + def save_bearer_token(self, token, request, *args, **kwargs): + # Remember to associate it with request.scopes, request.user and + # request.client. The two former will be set when you validate + # the authorization code. Don't forget to save both the + # access_token and the refresh_token and set expiration for the + # access_token to now + expires_in seconds. + + otoken = xhiveframework.new_doc("OAuth Bearer Token") + otoken.client = request.client["name"] + try: + otoken.user = ( + request.user + if request.user + else xhiveframework.db.get_value( + "OAuth Bearer Token", + {"refresh_token": request.body.get("refresh_token")}, + "user", + ) + ) + except Exception: + otoken.user = xhiveframework.session.user + + otoken.scopes = get_url_delimiter().join(request.scopes) + otoken.access_token = token["access_token"] + otoken.refresh_token = token.get("refresh_token") + otoken.expires_in = token["expires_in"] + otoken.save(ignore_permissions=True) + xhiveframework.db.commit() + + return xhiveframework.db.get_value("OAuth Client", request.client["name"], "default_redirect_uri") + + def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): + # Authorization codes are use once, invalidate it when a Bearer token + # has been acquired. + + xhiveframework.db.set_value("OAuth Authorization Code", code, "validity", "Invalid") + xhiveframework.db.commit() + + # Protected resource request + + def validate_bearer_token(self, token, scopes, request): + # Remember to check expiration and scope membership + otoken = xhiveframework.get_doc("OAuth Bearer Token", token) + is_token_valid = (now_datetime() < otoken.expiration_time) and otoken.status != "Revoked" + client_scopes = xhiveframework.db.get_value("OAuth Client", otoken.client, "scopes").split( + get_url_delimiter() + ) + are_scopes_valid = True + for scp in scopes: + are_scopes_valid = are_scopes_valid and True if scp in client_scopes else False + + return is_token_valid and are_scopes_valid + + # Token refresh request + + def get_original_scopes(self, refresh_token, request, *args, **kwargs): + # Obtain the token associated with the given refresh_token and + # return its scopes, these will be passed on to the refreshed + # access token if the client did not specify a scope during the + # request. + obearer_token = xhiveframework.get_doc("OAuth Bearer Token", {"refresh_token": refresh_token}) + return obearer_token.scopes + + def revoke_token(self, token, token_type_hint, request, *args, **kwargs): + """Revoke an access or refresh token. + + :param token: The token string. + :param token_type_hint: access_token or refresh_token. + :param request: The HTTP Request (oauthlib.common.Request) + + Method is used by: + - Revocation Endpoint + """ + if token_type_hint == "access_token": + xhiveframework.db.set_value("OAuth Bearer Token", token, "status", "Revoked") + elif token_type_hint == "refresh_token": + xhiveframework.db.set_value("OAuth Bearer Token", {"refresh_token": token}, "status", "Revoked") + else: + xhiveframework.db.set_value("OAuth Bearer Token", token, "status", "Revoked") + xhiveframework.db.commit() + + def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs): + """Ensure the Bearer token is valid and authorized access to scopes. + + OBS! The request.user attribute should be set to the resource owner + associated with this refresh token. + + :param refresh_token: Unicode refresh token + :param client: Client object set by you, see authenticate_client. + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False + + Method is used by: + - Authorization Code Grant (indirectly by issuing refresh tokens) + - Resource Owner Password Credentials Grant (also indirectly) + - Refresh Token Grant + """ + + otoken = xhiveframework.get_doc("OAuth Bearer Token", {"refresh_token": refresh_token, "status": "Active"}) + + if not otoken: + return False + else: + return True + + # OpenID Connect + + def finalize_id_token(self, id_token, token, token_handler, request): + # Check whether xhiveframework server URL is set + id_token_header = {"typ": "jwt", "alg": "HS256"} + + user = xhiveframework.get_doc("User", request.user) + + if request.nonce: + id_token["nonce"] = request.nonce + + userinfo = get_userinfo(user) + + id_token["exp"] = id_token.get("iat") + token.get("expires_in") + + if userinfo.get("iss"): + id_token["iss"] = userinfo.get("iss") + + if "openid" in request.scopes: + id_token.update(userinfo) + + id_token_encoded = jwt.encode( + payload=id_token, + key=request.client.client_secret, + algorithm="HS256", + headers=id_token_header, + ) + + return xhiveframework.safe_decode(id_token_encoded) + + def get_authorization_code_nonce(self, client_id, code, redirect_uri, request): + if xhiveframework.get_value("OAuth Authorization Code", code, "validity") == "Valid": + return xhiveframework.get_value("OAuth Authorization Code", code, "nonce") + + return None + + def get_authorization_code_scopes(self, client_id, code, redirect_uri, request): + scope = xhiveframework.get_value("OAuth Client", client_id, "scopes") + if not scope: + scope = [] + else: + scope = scope.split(get_url_delimiter()) + + return scope + + def get_jwt_bearer_token(self, token, token_handler, request): + now = datetime.datetime.now() + + id_token = dict( + aud=token.client_id, + iat=round(now.timestamp()), + at_hash=calculate_at_hash(token.access_token, hashlib.sha256), + ) + return self.finalize_id_token(id_token, token, token_handler, request) + + def get_userinfo_claims(self, request): + user = xhiveframework.get_doc("User", xhiveframework.session.user) + return get_userinfo(user) + + def validate_id_token(self, token, scopes, request): + try: + id_token = xhiveframework.get_doc("OAuth Bearer Token", token) + if id_token.status == "Active": + return True + except Exception: + return False + + return False + + def validate_jwt_bearer_token(self, token, scopes, request): + try: + jwt = xhiveframework.get_doc("OAuth Bearer Token", token) + if jwt.status == "Active": + return True + except Exception: + return False + + return False + + def validate_silent_authorization(self, request): + """Ensure the logged in user has authorized silent OpenID authorization. + + Silent OpenID authorization allows access tokens and id tokens to be + granted to clients without any user prompt or interaction. + + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False + + Method is used by: + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid + """ + if request.prompt == "login": + return False + else: + return True + + def validate_silent_login(self, request): + """Ensure session user has authorized silent OpenID login. + + If no user is logged in or has not authorized silent login, this + method should return False. + + If the user is logged in but associated with multiple accounts and + not selected which one to link to the token then this method should + raise an oauthlib.oauth2.AccountSelectionRequired error. + + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False + + Method is used by: + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid + """ + if xhiveframework.session.user == "Guest" or request.prompt.lower() == "login": + return False + else: + return True + + def validate_user_match(self, id_token_hint, scopes, claims, request): + """Ensure client supplied user id hint matches session user. + + If the sub claim or id_token_hint is supplied then the session + user must match the given ID. + + :param id_token_hint: User identifier string. + :param scopes: List of OAuth 2 scopes and OpenID claims (strings). + :param claims: OpenID Connect claims dict. + :param request: The HTTP Request (oauthlib.common.Request) + :rtype: True or False + + Method is used by: + - OpenIDConnectAuthCode + - OpenIDConnectImplicit + - OpenIDConnectHybrid + """ + if id_token_hint: + try: + user = None + payload = jwt.decode( + id_token_hint, + algorithms=["HS256"], + options={ + "verify_signature": False, + "verify_aud": False, + }, + ) + client_id, client_secret = xhiveframework.get_value( + "OAuth Client", + payload.get("aud"), + ["client_id", "client_secret"], + ) + + if payload.get("sub") and client_id and client_secret: + user = xhiveframework.db.get_value( + "User Social Login", + {"userid": payload.get("sub"), "provider": "xhiveframework"}, + "parent", + ) + user = xhiveframework.get_doc("User", user) + verified_payload = jwt.decode( + id_token_hint, + key=client_secret, + audience=client_id, + algorithms=["HS256"], + options={ + "verify_exp": False, + }, + ) + + if verified_payload: + return user.name == xhiveframework.session.user + + except Exception: + return False + + elif xhiveframework.session.user != "Guest": + return True + + return False + + def validate_user(self, username, password, client, request, *args, **kwargs): + """Ensure the username and password is valid. + + Method is used by: + - Resource Owner Password Credentials Grant + """ + login_manager = LoginManager() + login_manager.authenticate(username, password) + + if login_manager.user == "Guest": + return False + + request.user = login_manager.user + return True + + +def get_cookie_dict_from_headers(r): + cookie = cookies.BaseCookie() + if r.headers.get("Cookie"): + cookie.load(r.headers.get("Cookie")) + return cookie + + +def calculate_at_hash(access_token, hash_alg): + """Helper method for calculating an access token + hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken + Its value is the base64url encoding of the left-most half of the hash of the octets + of the ASCII representation of the access_token value, where the hash algorithm + used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE + Header. For instance, if the alg is RS256, hash the access_token value with SHA-256, + then take the left-most 128 bits and base64url encode them. The at_hash value is a + case sensitive string. + Args: + access_token (str): An access token string. + hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256 + """ + hash_digest = hash_alg(access_token.encode("utf-8")).digest() + cut_at = int(len(hash_digest) / 2) + truncated = hash_digest[:cut_at] + from jwt.utils import base64url_encode + + at_hash = base64url_encode(truncated) + return at_hash.decode("utf-8") + + +def delete_oauth2_data(): + xhiveframework.db.delete("OAuth Authorization Code", {"validity": "Invalid"}) + xhiveframework.db.delete("OAuth Bearer Token", {"status": "Revoked"}) + + +def get_client_scopes(client_id): + scopes_string = xhiveframework.db.get_value("OAuth Client", client_id, "scopes") + return scopes_string.split() + + +def get_userinfo(user): + picture = None + xhiveframework_server_url = get_server_url() + valid_url_schemes = ("http", "https", "ftp", "ftps") + + if user.user_image: + if xhiveframework.utils.validate_url(user.user_image, valid_schemes=valid_url_schemes): + picture = user.user_image + else: + picture = urljoin(xhiveframework_server_url, user.user_image) + + return xhiveframework._dict( + { + "sub": xhiveframework.db.get_value( + "User Social Login", + {"parent": user.name, "provider": "xhiveframework"}, + "userid", + ), + "name": " ".join(filter(None, [user.first_name, user.last_name])), + "given_name": user.first_name, + "family_name": user.last_name, + "email": user.email, + "picture": picture, + "roles": xhiveframework.get_roles(user.name), + "iss": xhiveframework_server_url, + } + ) + + +def get_url_delimiter(separator_character=" "): + return separator_character + + +def generate_json_error_response(e): + if not e: + e = xhiveframework._dict({}) + + xhiveframework.local.response = xhiveframework._dict( + { + "description": getattr(e, "description", "Internal Server Error"), + "status_code": getattr(e, "status_code", 500), + "error": getattr(e, "error", "internal_server_error"), + } + ) + xhiveframework.local.response["http_status_code"] = getattr(e, "status_code", 500) + return + + +def get_server_url(): + request_url = urlparse(xhiveframework.request.url) + request_url = f"{request_url.scheme}://{request_url.netloc}" + return xhiveframework.get_value("Social Login Key", "xhiveframework", "base_url") or request_url diff --git a/xhiveframework/parallel_test_runner.py b/xhiveframework/parallel_test_runner.py new file mode 100644 index 0000000..69fd770 --- /dev/null +++ b/xhiveframework/parallel_test_runner.py @@ -0,0 +1,313 @@ +import faulthandler +import json +import os +import re +import signal +import sys +import time +import unittest + +import click +import requests + +import xhiveframework + +from .test_runner import SLOW_TEST_THRESHOLD, make_test_records + +click_ctx = click.get_current_context(True) +if click_ctx: + click_ctx.color = True + + +class ParallelTestRunner: + def __init__(self, app, site, build_number=1, total_builds=1, dry_run=False): + self.app = app + self.site = site + self.build_number = xhiveframework.utils.cint(build_number) or 1 + self.total_builds = xhiveframework.utils.cint(total_builds) + self.dry_run = dry_run + self.setup_test_site() + self.run_tests() + + def setup_test_site(self): + xhiveframework.init(site=self.site) + if not xhiveframework.db: + xhiveframework.connect() + + if self.dry_run: + return + + xhiveframework.flags.in_test = True + xhiveframework.clear_cache() + xhiveframework.utils.scheduler.disable_scheduler() + self.before_test_setup() + + def before_test_setup(self): + start_time = time.monotonic() + for fn in xhiveframework.get_hooks("before_tests", app_name=self.app): + xhiveframework.get_attr(fn)() + + test_module = xhiveframework.get_module(f"{self.app}.tests") + + if hasattr(test_module, "global_test_dependencies"): + for doctype in test_module.global_test_dependencies: + make_test_records(doctype, commit=True) + + elapsed = time.monotonic() - start_time + elapsed = click.style(f" ({elapsed:.03}s)", fg="red") + click.echo(f"Before Test {elapsed}") + + def run_tests(self): + self.test_result = ParallelTestResult(stream=sys.stderr, descriptions=True, verbosity=2) + + for test_file_info in self.get_test_file_list(): + self.run_tests_for_file(test_file_info) + + self.print_result() + + def run_tests_for_file(self, file_info): + if not file_info: + return + + if self.dry_run: + print("running tests from", "/".join(file_info)) + return + + xhiveframework.set_user("Administrator") + path, filename = file_info + module = self.get_module(path, filename) + self.create_test_dependency_records(module, path, filename) + test_suite = unittest.TestSuite() + module_test_cases = unittest.TestLoader().loadTestsFromModule(module) + test_suite.addTest(module_test_cases) + test_suite(self.test_result) + + def create_test_dependency_records(self, module, path, filename): + if hasattr(module, "test_dependencies"): + for doctype in module.test_dependencies: + make_test_records(doctype, commit=True) + + if os.path.basename(os.path.dirname(path)) == "doctype": + # test_data_migration_connector.py > data_migration_connector.json + test_record_filename = re.sub("^test_", "", filename).replace(".py", ".json") + test_record_file_path = os.path.join(path, test_record_filename) + if os.path.exists(test_record_file_path): + with open(test_record_file_path) as f: + doc = json.loads(f.read()) + doctype = doc["name"] + make_test_records(doctype, commit=True) + + def get_module(self, path, filename): + app_path = xhiveframework.get_app_path(self.app) + relative_path = os.path.relpath(path, app_path) + if relative_path == ".": + module_name = self.app + else: + relative_path = relative_path.replace("/", ".") + module_name = os.path.splitext(filename)[0] + module_name = f"{self.app}.{relative_path}.{module_name}" + + return xhiveframework.get_module(module_name) + + def print_result(self): + # XXX: Added to debug tests getting stuck AFTER completion + # the process should terminate before this, we don't need to reset the signal. + signal.alarm(60) + faulthandler.register(signal.SIGALRM) + + self.test_result.printErrors() + click.echo(self.test_result) + if self.test_result.failures or self.test_result.errors: + if os.environ.get("CI"): + sys.exit(1) + + def get_test_file_list(self): + # Load balance based on total # of tests ~ each runner should get roughly same # of tests. + test_list = get_all_tests(self.app) + + test_counts = [self.get_test_count(test) for test in test_list] + test_chunks = split_by_weight(test_list, test_counts, chunk_count=self.total_builds) + + return test_chunks[self.build_number - 1] + + @staticmethod + def get_test_count(test): + """Get approximate count of tests inside a file""" + file_name = "/".join(test) + + with open(file_name) as f: + test_count = f.read().count("def test_") + + return test_count + + +def split_by_weight(work, weights, chunk_count): + """Roughly split work by respective weight while keep ordering.""" + expected_weight = sum(weights) // chunk_count + + chunks = [[] for _ in range(chunk_count)] + + chunk_no = 0 + chunk_weight = 0 + + for task, weight in zip(work, weights, strict=False): + if chunk_weight > expected_weight: + chunk_weight = 0 + chunk_no += 1 + assert chunk_no < chunk_count + + chunks[chunk_no].append(task) + chunk_weight += weight + + assert len(work) == sum(len(chunk) for chunk in chunks) + assert len(chunks) == chunk_count + + return chunks + + +class ParallelTestResult(unittest.TextTestResult): + def startTest(self, test): + self.tb_locals = True + self._started_at = time.monotonic() + super(unittest.TextTestResult, self).startTest(test) + test_class = unittest.util.strclass(test.__class__) + if not hasattr(self, "current_test_class") or self.current_test_class != test_class: + click.echo(f"\n{unittest.util.strclass(test.__class__)}") + self.current_test_class = test_class + + def getTestMethodName(self, test): + return test._testMethodName if hasattr(test, "_testMethodName") else str(test) + + def addSuccess(self, test): + super(unittest.TextTestResult, self).addSuccess(test) + elapsed = time.monotonic() - self._started_at + threshold_passed = elapsed >= SLOW_TEST_THRESHOLD + elapsed = click.style(f" ({elapsed:.03}s)", fg="red") if threshold_passed else "" + click.echo(f" {click.style(' ✔ ', fg='green')} {self.getTestMethodName(test)}{elapsed}") + + def addError(self, test, err): + super(unittest.TextTestResult, self).addError(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addFailure(self, test, err): + super(unittest.TextTestResult, self).addFailure(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addSkip(self, test, reason): + super(unittest.TextTestResult, self).addSkip(test, reason) + click.echo(f" {click.style(' = ', fg='white')} {self.getTestMethodName(test)}") + + def addExpectedFailure(self, test, err): + super(unittest.TextTestResult, self).addExpectedFailure(test, err) + click.echo(f" {click.style(' ✖ ', fg='red')} {self.getTestMethodName(test)}") + + def addUnexpectedSuccess(self, test): + super(unittest.TextTestResult, self).addUnexpectedSuccess(test) + click.echo(f" {click.style(' ✔ ', fg='green')} {self.getTestMethodName(test)}") + + def printErrors(self): + click.echo("\n") + self.printErrorList(" ERROR ", self.errors, "red") + self.printErrorList(" FAIL ", self.failures, "red") + + def printErrorList(self, flavour, errors, color): + for test, err in errors: + click.echo(self.separator1) + click.echo(f"{click.style(flavour, bg=color)} {self.getDescription(test)}") + click.echo(self.separator2) + click.echo(err) + + def __str__(self): + return f"Tests: {self.testsRun}, Failing: {len(self.failures)}, Errors: {len(self.errors)}" + + +def get_all_tests(app): + test_file_list = [] + for path, folders, files in os.walk(xhiveframework.get_app_path(app)): + for dontwalk in ("locals", ".git", "public", "__pycache__"): + if dontwalk in folders: + folders.remove(dontwalk) + + # for predictability + folders.sort() + files.sort() + + if os.path.sep.join(["doctype", "doctype", "boilerplate"]) in path: + # in /doctype/doctype/boilerplate/ + continue + + test_file_list.extend( + [path, filename] + for filename in files + if filename.startswith("test_") and filename.endswith(".py") and filename != "test_runner.py" + ) + return test_file_list + + +class ParallelTestWithOrchestrator(ParallelTestRunner): + """ + This can be used to balance-out test time across multiple instances + This is dependent on external orchestrator which returns next test to run + + orchestrator endpoints + - register-instance (, , test_spec_list) + - get-next-test-spec (, ) + - test-completed (, ) + """ + + def __init__(self, app, site): + self.orchestrator_url = os.environ.get("ORCHESTRATOR_URL") + if not self.orchestrator_url: + click.echo("ORCHESTRATOR_URL environment variable not found!") + click.echo("Pass public URL after hosting https://lab.membtech.com/xhiveframework/test-orchestrator") + sys.exit(1) + + self.ci_build_id = os.environ.get("CI_BUILD_ID") + self.ci_instance_id = os.environ.get("CI_INSTANCE_ID") or xhiveframework.generate_hash(length=10) + if not self.ci_build_id: + click.echo("CI_BUILD_ID environment variable not found!") + sys.exit(1) + + ParallelTestRunner.__init__(self, app, site) + + def run_tests(self): + self.test_status = "ongoing" + self.register_instance() + super().run_tests() + + def get_test_file_list(self): + while self.test_status == "ongoing": + yield self.get_next_test() + + def register_instance(self): + test_spec_list = get_all_tests(self.app) + response_data = self.call_orchestrator("register-instance", data={"test_spec_list": test_spec_list}) + self.is_master = response_data.get("is_master") + + def get_next_test(self): + response_data = self.call_orchestrator("get-next-test-spec") + self.test_status = response_data.get("status") + return response_data.get("next_test") + + def print_result(self): + self.call_orchestrator("test-completed") + return super().print_result() + + def call_orchestrator(self, endpoint, data=None): + if data is None: + data = {} + # add repo token header + # build id in header + headers = { + "CI-BUILD-ID": self.ci_build_id, + "CI-INSTANCE-ID": self.ci_instance_id, + "REPO-TOKEN": "2948288382838DE", + } + url = f"{self.orchestrator_url}/{endpoint}" + res = requests.get(url, json=data, headers=headers) + res.raise_for_status() + response_data = {} + if "application/json" in res.headers.get("content-type"): + response_data = res.json() + + return response_data diff --git a/xhiveframework/patches.txt b/xhiveframework/patches.txt new file mode 100644 index 0000000..f5b07b6 --- /dev/null +++ b/xhiveframework/patches.txt @@ -0,0 +1,236 @@ +[pre_model_sync] +xhiveframework.patches.v15_0.remove_implicit_primary_key +xhiveframework.patches.v12_0.remove_deprecated_fields_from_doctype #3 +execute:xhiveframework.utils.global_search.setup_global_search_table() +execute:xhiveframework.reload_doc('core', 'doctype', 'doctype_action', force=True) #2019-09-23 +execute:xhiveframework.reload_doc('core', 'doctype', 'doctype_link', force=True) #2020-10-17 +execute:xhiveframework.reload_doc('core', 'doctype', 'doctype_state', force=True) #2021-12-15 +execute:xhiveframework.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22 +execute:xhiveframework.reload_doc('core', 'doctype', 'docfield', force=True) #2018-02-20 +xhiveframework.patches.v11_0.drop_column_apply_user_permissions +execute:xhiveframework.reload_doc('core', 'doctype', 'custom_docperm') +execute:xhiveframework.reload_doc('core', 'doctype', 'docperm') #2018-05-29 +execute:xhiveframework.reload_doc('core', 'doctype', 'comment') +execute:xhiveframework.reload_doc('core', 'doctype', 'document_naming_rule', force=True) +execute:xhiveframework.reload_doc('core', 'doctype', 'module_def') #2020-08-28 +execute:xhiveframework.reload_doc('core', 'doctype', 'version') #2017-04-01 +execute:xhiveframework.reload_doc('email', 'doctype', 'document_follow') +execute:xhiveframework.reload_doc('core', 'doctype', 'communication_link') #2019-10-02 +execute:xhiveframework.reload_doc('core', 'doctype', 'has_role') +execute:xhiveframework.reload_doc('core', 'doctype', 'communication') #2019-10-02 +execute:xhiveframework.reload_doc('core', 'doctype', 'server_script') +xhiveframework.patches.v11_0.replicate_old_user_permissions +xhiveframework.patches.v11_0.reload_and_rename_view_log #2019-01-03 +xhiveframework.patches.v11_0.copy_fetch_data_from_options +xhiveframework.patches.v11_0.change_email_signature_fieldtype +execute:xhiveframework.reload_doc('core', 'doctype', 'activity_log') +execute:xhiveframework.reload_doc('core', 'doctype', 'deleted_document') +execute:xhiveframework.reload_doc('core', 'doctype', 'domain_settings') +xhiveframework.patches.v13_0.rename_custom_client_script +execute:xhiveframework.reload_doc('core', 'doctype', 'role') #2017-05-23 +execute:xhiveframework.reload_doc('core', 'doctype', 'user') #2017-10-27 +execute:xhiveframework.reload_doc('core', 'doctype', 'report_column') +execute:xhiveframework.reload_doc('core', 'doctype', 'report_filter') +execute:xhiveframework.reload_doc('core', 'doctype', 'report') #2020-08-25 +execute:xhiveframework.get_doc("User", "Guest").save() +execute:xhiveframework.delete_doc("DocType", "Control Panel", force=1) +execute:xhiveframework.delete_doc("DocType", "Tag") +execute:xhiveframework.db.sql("delete from `tabProperty Setter` where `property` in ('idx', '_idx')") +execute:xhiveframework.db.sql("update tabUser set new_password='' where ifnull(new_password, '')!=''") +execute:xhiveframework.permissions.reset_perms("DocType") +execute:xhiveframework.db.sql("delete from `tabProperty Setter` where `property` = 'idx'") +execute:xhiveframework.db.sql("delete from tabSessions where user is null") +execute:xhiveframework.delete_doc("DocType", "Backup Manager") +execute:xhiveframework.permissions.reset_perms("Web Page") +execute:xhiveframework.db.sql("delete from `tabWeb Page` where ifnull(template_path, '')!=''") +execute:xhiveframework.core.doctype.language.language.update_language_names() # 2017-04-12 +execute:xhiveframework.db.set_value("Print Settings", "Print Settings", "add_draft_heading", 1) +execute:xhiveframework.db.set_default('language', '') +execute:xhiveframework.db.sql("update tabCommunication set communication_date = creation where time(communication_date) = 0") +execute:xhiveframework.rename_doc('Country', 'Macedonia, Republic of', 'Macedonia', ignore_if_exists=True) +execute:xhiveframework.rename_doc('Country', 'Iran, Islamic Republic of', 'Iran', ignore_if_exists=True) +execute:xhiveframework.rename_doc('Country', 'Tanzania, United Republic of', 'Tanzania', ignore_if_exists=True) +execute:xhiveframework.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_exists=True) +execute:xhiveframework.reload_doc('desk', 'doctype', 'notification_log') +execute:xhiveframework.db.sql('update tabReport set module="Desk" where name="ToDo"') +execute:xhiveframework.delete_doc('Page', 'data-import-tool', ignore_missing=True) +xhiveframework.patches.v10_0.reload_countries_and_currencies # 2021-02-03 +xhiveframework.patches.v10_0.refactor_social_login_keys +xhiveframework.patches.v10_0.enable_chat_by_default_within_system_settings +xhiveframework.patches.v10_0.remove_custom_field_for_disabled_domain +execute:xhiveframework.delete_doc("Page", "chat") +xhiveframework.patches.v11_0.rename_standard_reply_to_email_template +execute:xhiveframework.delete_doc_if_exists('Page', 'user-permissions') +xhiveframework.patches.v10_0.set_no_copy_to_workflow_state +xhiveframework.patches.v10_0.increase_single_table_column_length +xhiveframework.patches.v11_0.create_contact_for_user +xhiveframework.patches.v11_0.update_list_user_settings +xhiveframework.patches.v11_0.rename_workflow_action_to_workflow_action_master #13-06-2018 +xhiveframework.patches.v11_0.rename_email_alert_to_notification #13-06-2018 +xhiveframework.patches.v11_0.delete_duplicate_user_permissions +xhiveframework.patches.v11_0.set_dropbox_file_backup +xhiveframework.patches.v10_0.set_default_locking_time +xhiveframework.patches.v10_0.modify_smallest_currency_fraction +xhiveframework.patches.v10_0.modify_naming_series_table +xhiveframework.patches.v10_0.enhance_security +xhiveframework.patches.v11_0.multiple_references_in_events +xhiveframework.patches.v11_0.set_allow_self_approval_in_workflow +xhiveframework.patches.v11_0.remove_skip_for_doctype +xhiveframework.patches.v11_0.migrate_report_settings_for_new_listview +xhiveframework.patches.v11_0.delete_all_prepared_reports +xhiveframework.patches.v11_0.fix_order_by_in_reports_json +execute:xhiveframework.delete_doc('Page', 'applications', ignore_missing=True) +xhiveframework.patches.v11_0.set_missing_creation_and_modified_value_for_user_permissions +xhiveframework.patches.v11_0.set_default_letter_head_source +xhiveframework.patches.v12_0.set_primary_key_in_series +execute:xhiveframework.delete_doc("Page", "modules", ignore_missing=True) +xhiveframework.patches.v12_0.setup_comments_from_communications +xhiveframework.patches.v12_0.replace_null_values_in_tables +xhiveframework.patches.v12_0.reset_home_settings +xhiveframework.patches.v12_0.update_print_format_type +xhiveframework.patches.v11_0.remove_doctype_user_permissions_for_page_and_report #2019-05-01 +xhiveframework.patches.v11_0.apply_customization_to_custom_doctype +xhiveframework.patches.v12_0.remove_feedback_rating +xhiveframework.patches.v12_0.move_form_attachments_to_attachments_folder +xhiveframework.patches.v12_0.move_timeline_links_to_dynamic_links +xhiveframework.patches.v12_0.delete_feedback_request_if_exists #1 +xhiveframework.patches.v12_0.rename_events_repeat_on +xhiveframework.patches.v12_0.fix_public_private_files +xhiveframework.patches.v12_0.move_email_and_phone_to_child_table +xhiveframework.patches.v12_0.delete_duplicate_indexes # 2022-12-15 +xhiveframework.patches.v12_0.set_default_incoming_email_port +xhiveframework.patches.v12_0.update_global_search +xhiveframework.patches.v12_0.setup_tags +xhiveframework.patches.v12_0.update_auto_repeat_status_and_not_submittable +xhiveframework.patches.v12_0.create_notification_settings_for_user +xhiveframework.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26 +xhiveframework.patches.v12_0.setup_email_linking +xhiveframework.patches.v12_0.change_existing_dashboard_chart_filters +xhiveframework.patches.v12_0.set_correct_assign_value_in_docs #2020-07-13 +execute:xhiveframework.delete_doc('DocType', 'Test Runner') # 2022-05-19 +execute:xhiveframework.delete_doc_if_exists('DocType', 'Google Maps Settings') +execute:xhiveframework.db.set_default('desktop:home_page', 'workspace') +execute:xhiveframework.delete_doc_if_exists('DocType', 'GSuite Settings') +execute:xhiveframework.delete_doc_if_exists('DocType', 'GSuite Templates') +execute:xhiveframework.delete_doc_if_exists('DocType', 'GCalendar Account') +execute:xhiveframework.delete_doc_if_exists('DocType', 'GCalendar Settings') +xhiveframework.patches.v12_0.remove_example_email_thread_notify +execute:from xhiveframework.desk.page.setup_wizard.install_fixtures import update_genders;update_genders() +xhiveframework.patches.v12_0.set_correct_url_in_files +execute:xhiveframework.reload_doc('core', 'doctype', 'doctype') #2022-06-21 +execute:xhiveframework.reload_doc('custom', 'doctype', 'property_setter') +xhiveframework.patches.v13_0.remove_invalid_options_for_data_fields +xhiveframework.patches.v13_0.website_theme_custom_scss +xhiveframework.patches.v13_0.make_user_type +xhiveframework.patches.v13_0.set_existing_dashboard_charts_as_public +xhiveframework.patches.v13_0.set_path_for_homepage_in_web_page_view +xhiveframework.patches.v13_0.migrate_translation_column_data +xhiveframework.patches.v13_0.set_read_times +xhiveframework.patches.v13_0.remove_web_view +xhiveframework.patches.v13_0.site_wise_logging +xhiveframework.patches.v13_0.set_unique_for_page_view +xhiveframework.patches.v13_0.remove_tailwind_from_page_builder +xhiveframework.patches.v13_0.rename_onboarding +xhiveframework.patches.v13_0.email_unsubscribe +execute:xhiveframework.delete_doc("Web Template", "Section with Left Image", force=1) +execute:xhiveframework.delete_doc("DocType", "Onboarding Slide") +execute:xhiveframework.delete_doc("DocType", "Onboarding Slide Field") +execute:xhiveframework.delete_doc("DocType", "Onboarding Slide Help Link") +xhiveframework.patches.v13_0.update_date_filters_in_user_settings +xhiveframework.patches.v13_0.update_duration_options +xhiveframework.patches.v13_0.replace_old_data_import # 2020-06-24 +xhiveframework.patches.v13_0.create_custom_dashboards_cards_and_charts +xhiveframework.patches.v13_0.rename_is_custom_field_in_dashboard_chart +xhiveframework.patches.v13_0.add_standard_navbar_items # 2020-12-15 +xhiveframework.patches.v13_0.generate_theme_files_in_public_folder +xhiveframework.patches.v13_0.increase_password_length +xhiveframework.patches.v12_0.fix_email_id_formatting +xhiveframework.patches.v13_0.add_toggle_width_in_navbar_settings +xhiveframework.patches.v13_0.rename_notification_fields +xhiveframework.patches.v13_0.remove_duplicate_navbar_items +xhiveframework.patches.v13_0.set_social_icons +xhiveframework.patches.v12_0.set_default_password_reset_limit +xhiveframework.patches.v13_0.set_route_for_blog_category +xhiveframework.patches.v13_0.enable_custom_script +xhiveframework.patches.v13_0.update_newsletter_content_type +execute:xhiveframework.db.set_value('Website Settings', 'Website Settings', {'navbar_template': 'Standard Navbar', 'footer_template': 'Standard Footer'}) +xhiveframework.patches.v13_0.web_template_set_module #2020-10-05 +xhiveframework.patches.v13_0.remove_custom_link +execute:xhiveframework.delete_doc("DocType", "Footer Item") +execute:xhiveframework.reload_doctype('user') +execute:xhiveframework.reload_doctype('docperm') +xhiveframework.patches.v13_0.replace_field_target_with_open_in_new_tab +xhiveframework.patches.v13_0.add_switch_theme_to_navbar_settings +xhiveframework.patches.v13_0.update_icons_in_customized_desk_pages +execute:xhiveframework.db.set_default('desktop:home_page', 'space') +execute:xhiveframework.delete_doc_if_exists('Page', 'workspace') +execute:xhiveframework.delete_doc_if_exists('Page', 'dashboard', force=1) +xhiveframework.core.doctype.page.patches.drop_unused_pages +xhiveframework.patches.v13_0.remove_chat +xhiveframework.patches.v13_0.rename_desk_page_to_workspace # 02.02.2021 +xhiveframework.patches.v13_0.delete_package_publish_tool +xhiveframework.patches.v13_0.rename_list_view_setting_to_list_view_settings +xhiveframework.patches.v13_0.remove_twilio_settings +xhiveframework.patches.v12_0.rename_uploaded_files_with_proper_name +xhiveframework.patches.v13_0.queryreport_columns +xhiveframework.patches.v13_0.jinja_hook +xhiveframework.patches.v13_0.update_notification_channel_if_empty +xhiveframework.patches.v13_0.set_first_day_of_the_week +xhiveframework.patches.v13_0.encrypt_2fa_secrets +xhiveframework.patches.v13_0.reset_corrupt_defaults +xhiveframework.patches.v13_0.remove_share_for_std_users +execute:xhiveframework.reload_doc('custom', 'doctype', 'custom_field') +xhiveframework.email.doctype.email_queue.patches.drop_search_index_on_message_id +xhiveframework.patches.v14_0.save_ratings_in_fraction #23-12-2021 +xhiveframework.patches.v14_0.transform_todo_schema +xhiveframework.patches.v14_0.remove_post_and_post_comment +xhiveframework.patches.v14_0.reset_creation_datetime +xhiveframework.patches.v14_0.remove_is_first_startup +xhiveframework.patches.v14_0.clear_long_pending_stale_logs +xhiveframework.patches.v14_0.log_settings_migration +xhiveframework.patches.v14_0.setup_likes_from_feedback +xhiveframework.patches.v14_0.update_webforms +xhiveframework.patches.v14_0.delete_payment_gateways +xhiveframework.patches.v15_0.remove_event_streaming +xhiveframework.patches.v15_0.copy_disable_prepared_report_to_prepared_report +execute:xhiveframework.reload_doc("desk", "doctype", "Form Tour") +execute:xhiveframework.delete_doc('Page', 'recorder', ignore_missing=True, force=True) +xhiveframework.patches.v14_0.modify_value_column_size_for_singles + +[post_model_sync] +execute:xhiveframework.get_doc('Role', 'Guest').save() # remove desk access +xhiveframework.core.doctype.role.patches.v13_set_default_desk_properties +xhiveframework.patches.v14_0.update_workspace2 # 06.06.2023 +xhiveframework.patches.v14_0.drop_data_import_legacy +xhiveframework.patches.v14_0.copy_mail_data #08.03.21 +xhiveframework.patches.v14_0.update_github_endpoints #08-11-2021 +xhiveframework.patches.v14_0.remove_db_aggregation +xhiveframework.patches.v14_0.update_color_names_in_kanban_board_column +xhiveframework.patches.v14_0.update_is_system_generated_flag +xhiveframework.patches.v14_0.update_auto_account_deletion_duration +xhiveframework.patches.v14_0.update_integration_request +xhiveframework.patches.v14_0.set_document_expiry_default +xhiveframework.patches.v14_0.delete_data_migration_tool +xhiveframework.patches.v14_0.set_suspend_email_queue_default +xhiveframework.patches.v14_0.different_encryption_key +xhiveframework.patches.v14_0.update_multistep_webforms +execute:xhiveframework.delete_doc('Page', 'background_jobs', ignore_missing=True, force=True) +xhiveframework.patches.v14_0.drop_unused_indexes +xhiveframework.patches.v15_0.drop_modified_index +xhiveframework.patches.v14_0.update_attachment_comment +xhiveframework.patches.v15_0.set_contact_full_name +execute:xhiveframework.delete_doc("Page", "activity", force=1) +xhiveframework.patches.v14_0.disable_email_accounts_with_oauth +execute:xhiveframework.delete_doc("Page", "translation-tool", force=1) +xhiveframework.patches.v15_0.remove_prepared_report_settings_from_system_settings +xhiveframework.patches.v14_0.remove_manage_subscriptions_from_navbar +xhiveframework.patches.v15_0.remove_background_jobs_from_dropdown +xhiveframework.desk.doctype.form_tour.patches.introduce_ui_tours +execute:xhiveframework.delete_doc_if_exists("Workspace", "Customization") +execute:xhiveframework.db.set_single_value("Document Naming Settings", "default_amend_naming", "Amend Counter") +xhiveframework.patches.v15_0.move_event_cancelled_to_status +xhiveframework.patches.v15_0.set_file_type +xhiveframework.core.doctype.data_import.patches.remove_stale_docfields_from_legacy_version +xhiveframework.patches.v15_0.validate_newsletter_recipients +xhiveframework.patches.v15_0.sanitize_workspace_titles +xhiveframework.custom.doctype.property_setter.patches.remove_invalid_fetch_from_expressions diff --git a/xhiveframework/patches/__init__.py b/xhiveframework/patches/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v10_0/__init__.py b/xhiveframework/patches/v10_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v10_0/enable_chat_by_default_within_system_settings.py b/xhiveframework/patches/v10_0/enable_chat_by_default_within_system_settings.py new file mode 100644 index 0000000..2fe25b9 --- /dev/null +++ b/xhiveframework/patches/v10_0/enable_chat_by_default_within_system_settings.py @@ -0,0 +1,13 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("System Settings") + doc = xhiveframework.get_single("System Settings") + doc.enable_chat = 1 + + # Changes prescribed by Nabin Hait (nabin@xhiveframework.io) + doc.flags.ignore_mandatory = True + doc.flags.ignore_permissions = True + + doc.save() diff --git a/xhiveframework/patches/v10_0/enhance_security.py b/xhiveframework/patches/v10_0/enhance_security.py new file mode 100644 index 0000000..028f12e --- /dev/null +++ b/xhiveframework/patches/v10_0/enhance_security.py @@ -0,0 +1,32 @@ +import xhiveframework +from xhiveframework.utils import cint + + +def execute(): + """ + The motive of this patch is to increase the overall security in xhiveframework framework + + Existing passwords won't be affected, however, newly created accounts + will have to adheare to the new password policy guidelines. Once can always + loosen up the security by modifying the values in System Settings, however, + we strongly advice against doing so! + + Security is something we take very seriously at xhiveframework, + and hence we chose to make security tighter by default. + """ + doc = xhiveframework.get_single("System Settings") + + # Enforce a Password Policy + if cint(doc.enable_password_policy) == 0: + doc.enable_password_policy = 1 + + # Enforce a password score as calculated by zxcvbn + if cint(doc.minimum_password_score) <= 2: + doc.minimum_password_score = 2 + + # Disallow more than 3 consecutive login attempts in a span of 60 seconds + if cint(doc.allow_consecutive_login_attempts) <= 3: + doc.allow_consecutive_login_attempts = 3 + + doc.flags.ignore_mandatory = True + doc.save() diff --git a/xhiveframework/patches/v10_0/increase_single_table_column_length.py b/xhiveframework/patches/v10_0/increase_single_table_column_length.py new file mode 100644 index 0000000..43adbe9 --- /dev/null +++ b/xhiveframework/patches/v10_0/increase_single_table_column_length.py @@ -0,0 +1,9 @@ +""" +Run this after updating country_info.json and or +""" +import xhiveframework + + +def execute(): + for col in ("field", "doctype"): + xhiveframework.db.sql_ddl(f"alter table `tabSingles` modify column `{col}` varchar(255)") diff --git a/xhiveframework/patches/v10_0/modify_naming_series_table.py b/xhiveframework/patches/v10_0/modify_naming_series_table.py new file mode 100644 index 0000000..14840ee --- /dev/null +++ b/xhiveframework/patches/v10_0/modify_naming_series_table.py @@ -0,0 +1,10 @@ +""" + Modify the Integer 10 Digits Value to BigInt 20 Digit value + to generate long Naming Series + +""" +import xhiveframework + + +def execute(): + xhiveframework.db.sql(""" ALTER TABLE `tabSeries` MODIFY current BIGINT """) diff --git a/xhiveframework/patches/v10_0/modify_smallest_currency_fraction.py b/xhiveframework/patches/v10_0/modify_smallest_currency_fraction.py new file mode 100644 index 0000000..21c6255 --- /dev/null +++ b/xhiveframework/patches/v10_0/modify_smallest_currency_fraction.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.db.set_value("Currency", "USD", "smallest_currency_fraction_value", "0.01") diff --git a/xhiveframework/patches/v10_0/refactor_social_login_keys.py b/xhiveframework/patches/v10_0/refactor_social_login_keys.py new file mode 100644 index 0000000..dd14ccb --- /dev/null +++ b/xhiveframework/patches/v10_0/refactor_social_login_keys.py @@ -0,0 +1,153 @@ +import xhiveframework +from xhiveframework.utils import cstr + + +def execute(): + # Update Social Logins in User + run_patch() + + # Create Social Login Key(s) from Social Login Keys + xhiveframework.reload_doc("integrations", "doctype", "social_login_key", force=True) + + if not xhiveframework.db.exists("DocType", "Social Login Keys"): + return + + social_login_keys = xhiveframework.get_doc("Social Login Keys", "Social Login Keys") + if social_login_keys.get("facebook_client_id") or social_login_keys.get("facebook_client_secret"): + facebook_login_key = xhiveframework.new_doc("Social Login Key") + facebook_login_key.get_social_login_provider("Facebook", initialize=True) + facebook_login_key.social_login_provider = "Facebook" + facebook_login_key.client_id = social_login_keys.get("facebook_client_id") + facebook_login_key.client_secret = social_login_keys.get("facebook_client_secret") + if not (facebook_login_key.client_secret and facebook_login_key.client_id): + facebook_login_key.enable_social_login = 0 + facebook_login_key.save() + + if social_login_keys.get("xhiveframework_server_url"): + xhiveframework_login_key = xhiveframework.new_doc("Social Login Key") + xhiveframework_login_key.get_social_login_provider("XhiveFramework", initialize=True) + xhiveframework_login_key.social_login_provider = "XhiveFramework" + xhiveframework_login_key.base_url = social_login_keys.get("xhiveframework_server_url") + xhiveframework_login_key.client_id = social_login_keys.get("xhiveframework_client_id") + xhiveframework_login_key.client_secret = social_login_keys.get("xhiveframework_client_secret") + if not (xhiveframework_login_key.client_secret and xhiveframework_login_key.client_id and xhiveframework_login_key.base_url): + xhiveframework_login_key.enable_social_login = 0 + xhiveframework_login_key.save() + + if social_login_keys.get("github_client_id") or social_login_keys.get("github_client_secret"): + github_login_key = xhiveframework.new_doc("Social Login Key") + github_login_key.get_social_login_provider("GitHub", initialize=True) + github_login_key.social_login_provider = "GitHub" + github_login_key.client_id = social_login_keys.get("github_client_id") + github_login_key.client_secret = social_login_keys.get("github_client_secret") + if not (github_login_key.client_secret and github_login_key.client_id): + github_login_key.enable_social_login = 0 + github_login_key.save() + + if social_login_keys.get("google_client_id") or social_login_keys.get("google_client_secret"): + google_login_key = xhiveframework.new_doc("Social Login Key") + google_login_key.get_social_login_provider("Google", initialize=True) + google_login_key.social_login_provider = "Google" + google_login_key.client_id = social_login_keys.get("google_client_id") + google_login_key.client_secret = social_login_keys.get("google_client_secret") + if not (google_login_key.client_secret and google_login_key.client_id): + google_login_key.enable_social_login = 0 + google_login_key.save() + + xhiveframework.delete_doc("DocType", "Social Login Keys") + + +def run_patch(): + xhiveframework.reload_doc("core", "doctype", "user", force=True) + xhiveframework.reload_doc("core", "doctype", "user_social_login", force=True) + + users = xhiveframework.get_all("User", fields=["*"], filters={"name": ("not in", ["Administrator", "Guest"])}) + + for user in users: + idx = 0 + if user.xhiveframework_userid: + insert_user_social_login(user.name, user.modified_by, "xhiveframework", idx, userid=user.xhiveframework_userid) + idx += 1 + + if user.fb_userid or user.fb_username: + insert_user_social_login( + user.name, user.modified_by, "facebook", idx, userid=user.fb_userid, username=user.fb_username + ) + idx += 1 + + if user.github_userid or user.github_username: + insert_user_social_login( + user.name, + user.modified_by, + "github", + idx, + userid=user.github_userid, + username=user.github_username, + ) + idx += 1 + + if user.google_userid: + insert_user_social_login(user.name, user.modified_by, "google", idx, userid=user.google_userid) + idx += 1 + + +def insert_user_social_login(user, modified_by, provider, idx, userid=None, username=None): + source_cols = get_standard_cols() + + creation_time = xhiveframework.utils.get_datetime_str(xhiveframework.utils.get_datetime()) + values = [ + xhiveframework.generate_hash(length=10), + creation_time, + creation_time, + user, + modified_by, + user, + "User", + "social_logins", + cstr(idx), + provider, + ] + + if userid: + source_cols.append("userid") + values.append(userid) + + if username: + source_cols.append("username") + values.append(username) + + query = """INSERT INTO `tabUser Social Login` (`{source_cols}`) + VALUES ({values}) + """.format(source_cols="`, `".join(source_cols), values=", ".join([xhiveframework.db.escape(d) for d in values])) + + xhiveframework.db.sql(query) + + +def get_provider_field_map(): + return xhiveframework._dict( + { + "xhiveframework": ["xhiveframework_userid"], + "facebook": ["fb_userid", "fb_username"], + "github": ["github_userid", "github_username"], + "google": ["google_userid"], + } + ) + + +def get_provider_fields(provider): + return get_provider_field_map().get(provider) + + +def get_standard_cols(): + return [ + "name", + "creation", + "modified", + "owner", + "modified_by", + "parent", + "parenttype", + "parentfield", + "idx", + "provider", + ] diff --git a/xhiveframework/patches/v10_0/reload_countries_and_currencies.py b/xhiveframework/patches/v10_0/reload_countries_and_currencies.py new file mode 100644 index 0000000..3c6034a --- /dev/null +++ b/xhiveframework/patches/v10_0/reload_countries_and_currencies.py @@ -0,0 +1,8 @@ +""" +Run this after updating country_info.json and or +""" +from xhiveframework.utils.install import import_country_and_currency + + +def execute(): + import_country_and_currency() diff --git a/xhiveframework/patches/v10_0/remove_custom_field_for_disabled_domain.py b/xhiveframework/patches/v10_0/remove_custom_field_for_disabled_domain.py new file mode 100644 index 0000000..f3f0caa --- /dev/null +++ b/xhiveframework/patches/v10_0/remove_custom_field_for_disabled_domain.py @@ -0,0 +1,14 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "domain") + xhiveframework.reload_doc("core", "doctype", "has_domain") + active_domains = xhiveframework.get_active_domains() + all_domains = xhiveframework.get_all("Domain") + + for d in all_domains: + if d.name not in active_domains: + inactive_domain = xhiveframework.get_doc("Domain", d.name) + inactive_domain.setup_data() + inactive_domain.remove_custom_field() diff --git a/xhiveframework/patches/v10_0/set_default_locking_time.py b/xhiveframework/patches/v10_0/set_default_locking_time.py new file mode 100644 index 0000000..69ec5d6 --- /dev/null +++ b/xhiveframework/patches/v10_0/set_default_locking_time.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "system_settings") + xhiveframework.db.set_single_value("System Settings", "allow_login_after_fail", 60) diff --git a/xhiveframework/patches/v10_0/set_no_copy_to_workflow_state.py b/xhiveframework/patches/v10_0/set_no_copy_to_workflow_state.py new file mode 100644 index 0000000..9bb9035 --- /dev/null +++ b/xhiveframework/patches/v10_0/set_no_copy_to_workflow_state.py @@ -0,0 +1,13 @@ +import xhiveframework + + +def execute(): + for dt in xhiveframework.get_all("Workflow", fields=["name", "document_type", "workflow_state_field"]): + fieldname = xhiveframework.db.get_value( + "Custom Field", filters={"dt": dt.document_type, "fieldname": dt.workflow_state_field} + ) + + if fieldname: + custom_field = xhiveframework.get_doc("Custom Field", fieldname) + custom_field.no_copy = 1 + custom_field.save() diff --git a/xhiveframework/patches/v11_0/__init__.py b/xhiveframework/patches/v11_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v11_0/apply_customization_to_custom_doctype.py b/xhiveframework/patches/v11_0/apply_customization_to_custom_doctype.py new file mode 100644 index 0000000..6dcf0a4 --- /dev/null +++ b/xhiveframework/patches/v11_0/apply_customization_to_custom_doctype.py @@ -0,0 +1,52 @@ +import xhiveframework +from xhiveframework.utils import cint + +# This patch aims to apply & delete all the customization +# on custom doctypes done through customize form + +# This is required because customize form in now blocked +# for custom doctypes and user may not be able to +# see previous customization + + +def execute(): + custom_doctypes = xhiveframework.get_all("DocType", filters={"custom": 1}) + + for doctype in custom_doctypes: + property_setters = xhiveframework.get_all( + "Property Setter", + filters={"doc_type": doctype.name, "doctype_or_field": "DocField"}, + fields=["name", "property", "value", "property_type", "field_name"], + ) + + custom_fields = xhiveframework.get_all("Custom Field", filters={"dt": doctype.name}, fields=["*"]) + + property_setter_map = {} + + for prop in property_setters: + property_setter_map[prop.field_name] = prop + xhiveframework.db.delete("Property Setter", {"name": prop.name}) + + meta = xhiveframework.get_meta(doctype.name) + + for df in meta.fields: + ps = property_setter_map.get(df.fieldname, None) + if ps: + value = cint(ps.value) if ps.property_type == "Int" else ps.value + df.set(ps.property, value) + + for cf in custom_fields: + cf.pop("parenttype") + cf.pop("parentfield") + cf.pop("parent") + cf.pop("name") + field = meta.get_field(cf.fieldname) + if field: + field.update(cf) + else: + df = xhiveframework.new_doc("DocField", parent_doc=meta, parentfield="fields") + df.update(cf) + meta.fields.append(df) + xhiveframework.db.delete("Custom Field", {"name": cf.name}) + + meta.save() diff --git a/xhiveframework/patches/v11_0/change_email_signature_fieldtype.py b/xhiveframework/patches/v11_0/change_email_signature_fieldtype.py new file mode 100644 index 0000000..8821058 --- /dev/null +++ b/xhiveframework/patches/v11_0/change_email_signature_fieldtype.py @@ -0,0 +1,14 @@ +# Copyright (c) 2018, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + signatures = xhiveframework.db.get_list("User", {"email_signature": ["!=", ""]}, ["name", "email_signature"]) + xhiveframework.reload_doc("core", "doctype", "user") + for d in signatures: + signature = d.get("email_signature") + signature = signature.replace("\n", "
      ") + signature = "
      " + signature + "
      " + xhiveframework.db.set_value("User", d.get("name"), "email_signature", signature) diff --git a/xhiveframework/patches/v11_0/copy_fetch_data_from_options.py b/xhiveframework/patches/v11_0/copy_fetch_data_from_options.py new file mode 100644 index 0000000..00a86e9 --- /dev/null +++ b/xhiveframework/patches/v11_0/copy_fetch_data_from_options.py @@ -0,0 +1,38 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "docfield", force=True) + xhiveframework.reload_doc("custom", "doctype", "custom_field", force=True) + xhiveframework.reload_doc("custom", "doctype", "customize_form_field", force=True) + xhiveframework.reload_doc("custom", "doctype", "property_setter", force=True) + + xhiveframework.db.sql( + """ + update `tabDocField` + set fetch_from = options, options='' + where options like '%.%' and (fetch_from is NULL OR fetch_from='') + and fieldtype in ('Data', 'Read Only', 'Text', 'Small Text', 'Text Editor', 'Code', 'Link', 'Check') + and fieldname!='naming_series' + """ + ) + + xhiveframework.db.sql( + """ + update `tabCustom Field` + set fetch_from = options, options='' + where options like '%.%' and (fetch_from is NULL OR fetch_from='') + and fieldtype in ('Data', 'Read Only', 'Text', 'Small Text', 'Text Editor', 'Code', 'Link', 'Check') + and fieldname!='naming_series' + """ + ) + + xhiveframework.db.sql( + """ + update `tabProperty Setter` + set property="fetch_from", name=concat(doc_type, '-', field_name, '-', property) + where property="options" and value like '%.%' + and property_type in ('Data', 'Read Only', 'Text', 'Small Text', 'Text Editor', 'Code', 'Link', 'Check') + and field_name!='naming_series' + """ + ) diff --git a/xhiveframework/patches/v11_0/create_contact_for_user.py b/xhiveframework/patches/v11_0/create_contact_for_user.py new file mode 100644 index 0000000..6606c06 --- /dev/null +++ b/xhiveframework/patches/v11_0/create_contact_for_user.py @@ -0,0 +1,28 @@ +import re + +import xhiveframework +from xhiveframework.core.doctype.user.user import create_contact + + +def execute(): + """Create Contact for each User if not present""" + xhiveframework.reload_doc("integrations", "doctype", "google_contacts") + xhiveframework.reload_doc("contacts", "doctype", "contact") + xhiveframework.reload_doc("core", "doctype", "dynamic_link") + + contact_meta = xhiveframework.get_meta("Contact") + if contact_meta.has_field("phone_nos") and contact_meta.has_field("email_ids"): + xhiveframework.reload_doc("contacts", "doctype", "contact_phone") + xhiveframework.reload_doc("contacts", "doctype", "contact_email") + + users = xhiveframework.get_all("User", filters={"name": ("not in", "Administrator, Guest")}, fields=["*"]) + for user in users: + if xhiveframework.db.exists("Contact", {"email_id": user.email}) or xhiveframework.db.exists( + "Contact Email", {"email_id": user.email} + ): + continue + if user.first_name: + user.first_name = re.sub("[<>]+", "", xhiveframework.safe_decode(user.first_name)) + if user.last_name: + user.last_name = re.sub("[<>]+", "", xhiveframework.safe_decode(user.last_name)) + create_contact(user, ignore_links=True, ignore_mandatory=True) diff --git a/xhiveframework/patches/v11_0/delete_all_prepared_reports.py b/xhiveframework/patches/v11_0/delete_all_prepared_reports.py new file mode 100644 index 0000000..365706d --- /dev/null +++ b/xhiveframework/patches/v11_0/delete_all_prepared_reports.py @@ -0,0 +1,9 @@ +import xhiveframework + + +def execute(): + if xhiveframework.db.table_exists("Prepared Report"): + xhiveframework.reload_doc("core", "doctype", "prepared_report") + prepared_reports = xhiveframework.get_all("Prepared Report") + for report in prepared_reports: + xhiveframework.delete_doc("Prepared Report", report.name) diff --git a/xhiveframework/patches/v11_0/delete_duplicate_user_permissions.py b/xhiveframework/patches/v11_0/delete_duplicate_user_permissions.py new file mode 100644 index 0000000..f5e9850 --- /dev/null +++ b/xhiveframework/patches/v11_0/delete_duplicate_user_permissions.py @@ -0,0 +1,18 @@ +import xhiveframework + + +def execute(): + duplicateRecords = xhiveframework.db.sql( + """select count(name) as `count`, allow, user, for_value + from `tabUser Permission` + group by allow, user, for_value + having count(*) > 1 """, + as_dict=1, + ) + + for record in duplicateRecords: + xhiveframework.db.sql( + f"""delete from `tabUser Permission` + where allow=%s and user=%s and for_value=%s limit {record.count - 1}""", + (record.allow, record.user, record.for_value), + ) diff --git a/xhiveframework/patches/v11_0/drop_column_apply_user_permissions.py b/xhiveframework/patches/v11_0/drop_column_apply_user_permissions.py new file mode 100644 index 0000000..a367e8b --- /dev/null +++ b/xhiveframework/patches/v11_0/drop_column_apply_user_permissions.py @@ -0,0 +1,14 @@ +import xhiveframework + + +def execute(): + column = "apply_user_permissions" + to_remove = ["DocPerm", "Custom DocPerm"] + + for doctype in to_remove: + if xhiveframework.db.table_exists(doctype): + if column in xhiveframework.db.get_table_columns(doctype): + xhiveframework.db.sql(f"alter table `tab{doctype}` drop column {column}") + + xhiveframework.reload_doc("core", "doctype", "docperm", force=True) + xhiveframework.reload_doc("core", "doctype", "custom_docperm", force=True) diff --git a/xhiveframework/patches/v11_0/fix_order_by_in_reports_json.py b/xhiveframework/patches/v11_0/fix_order_by_in_reports_json.py new file mode 100644 index 0000000..4040d5a --- /dev/null +++ b/xhiveframework/patches/v11_0/fix_order_by_in_reports_json.py @@ -0,0 +1,35 @@ +import json + +import xhiveframework + + +def execute(): + reports_data = xhiveframework.get_all( + "Report", + filters={ + "json": ["not like", '%%%"order_by": "`tab%%%'], + "report_type": "Report Builder", + "is_standard": "No", + }, + fields=["name"], + ) + + for d in reports_data: + doc = xhiveframework.get_doc("Report", d.get("name")) + + if not doc.get("json"): + continue + + json_data = json.loads(doc.get("json")) + + parts = [] + if ("order_by" in json_data) and ("." in json_data.get("order_by")): + parts = json_data.get("order_by").split(".") + + sort_by = parts[1].split(" ") + + json_data["order_by"] = f"`tab{doc.ref_doctype}`.`{sort_by[0]}`" + json_data["order_by"] += f" {sort_by[1]}" if len(sort_by) > 1 else "" + + doc.json = json.dumps(json_data) + doc.save() diff --git a/xhiveframework/patches/v11_0/make_all_prepared_report_attachments_private.py b/xhiveframework/patches/v11_0/make_all_prepared_report_attachments_private.py new file mode 100644 index 0000000..2c3efa3 --- /dev/null +++ b/xhiveframework/patches/v11_0/make_all_prepared_report_attachments_private.py @@ -0,0 +1,28 @@ +import xhiveframework + + +def execute(): + if xhiveframework.db.count("File", filters={"attached_to_doctype": "Prepared Report", "is_private": 0}) > 10000: + xhiveframework.db.auto_commit_on_many_writes = True + + files = xhiveframework.get_all( + "File", + fields=["name", "attached_to_name"], + filters={"attached_to_doctype": "Prepared Report", "is_private": 0}, + ) + for file_dict in files: + # For some reason Prepared Report doc might not exist, check if it exists first + if xhiveframework.db.exists("Prepared Report", file_dict.attached_to_name): + try: + file_doc = xhiveframework.get_doc("File", file_dict.name) + file_doc.is_private = 1 + file_doc.save() + except Exception: + # File might not exist on the file system in that case delete both Prepared Report and File doc + xhiveframework.delete_doc("Prepared Report", file_dict.attached_to_name) + else: + # If Prepared Report doc doesn't exist then the file doc is useless. Delete it. + xhiveframework.delete_doc("File", file_dict.name) + + if xhiveframework.db.auto_commit_on_many_writes: + xhiveframework.db.auto_commit_on_many_writes = False diff --git a/xhiveframework/patches/v11_0/migrate_report_settings_for_new_listview.py b/xhiveframework/patches/v11_0/migrate_report_settings_for_new_listview.py new file mode 100644 index 0000000..26886af --- /dev/null +++ b/xhiveframework/patches/v11_0/migrate_report_settings_for_new_listview.py @@ -0,0 +1,34 @@ +import json + +import xhiveframework + + +def execute(): + """ + Migrate JSON field of Report according to changes in New ListView + Rename key columns to fields + Rename key add_total_row to add_totals_row + Convert sort_by and sort_order to order_by + """ + + reports = xhiveframework.get_all("Report", {"report_type": "Report Builder"}) + + for report_name in reports: + settings = xhiveframework.db.get_value("Report", report_name, "json") + if not settings: + continue + + settings = xhiveframework._dict(json.loads(settings)) + + # columns -> fields + settings.fields = settings.columns or [] + settings.pop("columns", None) + + # sort_by + order_by -> order_by + settings.order_by = (settings.sort_by or "modified") + " " + (settings.order_by or "desc") + + # add_total_row -> add_totals_row + settings.add_totals_row = settings.add_total_row + settings.pop("add_total_row", None) + + xhiveframework.db.set_value("Report", report_name, "json", json.dumps(settings)) diff --git a/xhiveframework/patches/v11_0/multiple_references_in_events.py b/xhiveframework/patches/v11_0/multiple_references_in_events.py new file mode 100644 index 0000000..9ff990e --- /dev/null +++ b/xhiveframework/patches/v11_0/multiple_references_in_events.py @@ -0,0 +1,24 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("Event") + # Rename "Cancel" to "Cancelled" + xhiveframework.db.sql("""UPDATE tabEvent set event_type='Cancelled' where event_type='Cancel'""") + # Move references to Participants table + events = xhiveframework.db.sql( + """SELECT name, ref_type, ref_name FROM tabEvent WHERE ref_type!=''""", as_dict=True + ) + for event in events: + if event.ref_type and event.ref_name: + try: + e = xhiveframework.get_doc("Event", event.name) + e.append( + "event_participants", + {"reference_doctype": event.ref_type, "reference_docname": event.ref_name}, + ) + e.flags.ignore_mandatory = True + e.flags.ignore_permissions = True + e.save() + except Exception: + xhiveframework.log_error(xhiveframework.get_traceback()) diff --git a/xhiveframework/patches/v11_0/reload_and_rename_view_log.py b/xhiveframework/patches/v11_0/reload_and_rename_view_log.py new file mode 100644 index 0000000..cd90661 --- /dev/null +++ b/xhiveframework/patches/v11_0/reload_and_rename_view_log.py @@ -0,0 +1,28 @@ +import xhiveframework + + +def execute(): + if xhiveframework.db.table_exists("View log"): + # for mac users direct renaming would not work since mysql for mac saves table name in lower case + # so while renaming `tabView log` to `tabView Log` we get "Table 'tabView Log' already exists" error + # more info https://stackoverflow.com/a/44753093/5955589 , + # https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_lower_case_table_names + + # here we are creating a temp table to store view log data + xhiveframework.db.sql("CREATE TABLE `ViewLogTemp` AS SELECT * FROM `tabView log`") + + # deleting old View log table + xhiveframework.db.sql("DROP table `tabView log`") + xhiveframework.delete_doc("DocType", "View log") + + # reloading view log doctype to create `tabView Log` table + xhiveframework.reload_doc("core", "doctype", "view_log") + + # Move the data to newly created `tabView Log` table + xhiveframework.db.sql("INSERT INTO `tabView Log` SELECT * FROM `ViewLogTemp`") + xhiveframework.db.commit() + + # Delete temporary table + xhiveframework.db.sql("DROP table `ViewLogTemp`") + else: + xhiveframework.reload_doc("core", "doctype", "view_log") diff --git a/xhiveframework/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py b/xhiveframework/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py new file mode 100644 index 0000000..206936a --- /dev/null +++ b/xhiveframework/patches/v11_0/remove_doctype_user_permissions_for_page_and_report.py @@ -0,0 +1,8 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.delete_doc_if_exists("DocType", "User Permission for Page and Report") diff --git a/xhiveframework/patches/v11_0/remove_skip_for_doctype.py b/xhiveframework/patches/v11_0/remove_skip_for_doctype.py new file mode 100644 index 0000000..a700a11 --- /dev/null +++ b/xhiveframework/patches/v11_0/remove_skip_for_doctype.py @@ -0,0 +1,89 @@ +import xhiveframework +from xhiveframework.desk.form.linked_with import get_linked_doctypes +from xhiveframework.patches.v11_0.replicate_old_user_permissions import get_doctypes_to_skip +from xhiveframework.query_builder import Field + +# `skip_for_doctype` was a un-normalized way of storing for which +# doctypes the user permission was applicable. +# in this patch, we normalize this into `applicable_for` where +# a new record will be created for each doctype where the user permission +# is applicable +# +# if the user permission is applicable for all doctypes, then only +# one record is created + + +def execute(): + xhiveframework.reload_doctype("User Permission") + + # to check if we need to migrate from skip_for_doctype + has_skip_for_doctype = xhiveframework.db.has_column("User Permission", "skip_for_doctype") + skip_for_doctype_map = {} + + new_user_permissions_list = [] + + user_permissions_to_delete = [] + + for user_permission in xhiveframework.get_all("User Permission", fields=["*"]): + skip_for_doctype = [] + + # while migrating from v11 -> v11 + if has_skip_for_doctype: + if not user_permission.skip_for_doctype: + continue + skip_for_doctype = user_permission.skip_for_doctype.split("\n") + else: # while migrating from v10 -> v11 + if skip_for_doctype_map.get((user_permission.allow, user_permission.user)) is None: + skip_for_doctype = get_doctypes_to_skip(user_permission.allow, user_permission.user) + # cache skip for doctype for same user and doctype + skip_for_doctype_map[(user_permission.allow, user_permission.user)] = skip_for_doctype + else: + skip_for_doctype = skip_for_doctype_map[(user_permission.allow, user_permission.user)] + + if skip_for_doctype: + # only specific doctypes are selected + # split this into multiple records and delete + linked_doctypes = get_linked_doctypes(user_permission.allow, True).keys() + + linked_doctypes = list(linked_doctypes) + + # append the doctype for which we have build the user permission + linked_doctypes += [user_permission.allow] + + applicable_for_doctypes = list(set(linked_doctypes) - set(skip_for_doctype)) + + user_permissions_to_delete.append(user_permission.name) + user_permission.name = None + user_permission.skip_for_doctype = None + new_user_permissions_list.extend( + ( + xhiveframework.generate_hash(length=10), + user_permission.user, + user_permission.allow, + user_permission.for_value, + doctype, + 0, + user_permission.creation, + user_permission.modified, + ) + for doctype in applicable_for_doctypes + if doctype + ) + else: + # No skip_for_doctype found! Just update apply_to_all_doctypes. + xhiveframework.db.set_value("User Permission", user_permission.name, "apply_to_all_doctypes", 1) + + if new_user_permissions_list: + xhiveframework.qb.into("User Permission").columns( + "name", + "user", + "allow", + "for_value", + "applicable_for", + "apply_to_all_doctypes", + "creation", + "modified", + ).insert(*new_user_permissions_list).run() + + if user_permissions_to_delete: + xhiveframework.db.delete("User Permission", filters=(Field("name").isin(tuple(user_permissions_to_delete)))) diff --git a/xhiveframework/patches/v11_0/rename_email_alert_to_notification.py b/xhiveframework/patches/v11_0/rename_email_alert_to_notification.py new file mode 100644 index 0000000..5b2bc96 --- /dev/null +++ b/xhiveframework/patches/v11_0/rename_email_alert_to_notification.py @@ -0,0 +1,14 @@ +import xhiveframework +from xhiveframework.model.rename_doc import rename_doc + + +def execute(): + if xhiveframework.db.table_exists("Email Alert Recipient") and not xhiveframework.db.table_exists( + "Notification Recipient" + ): + rename_doc("DocType", "Email Alert Recipient", "Notification Recipient") + xhiveframework.reload_doc("email", "doctype", "notification_recipient") + + if xhiveframework.db.table_exists("Email Alert") and not xhiveframework.db.table_exists("Notification"): + rename_doc("DocType", "Email Alert", "Notification") + xhiveframework.reload_doc("email", "doctype", "notification") diff --git a/xhiveframework/patches/v11_0/rename_standard_reply_to_email_template.py b/xhiveframework/patches/v11_0/rename_standard_reply_to_email_template.py new file mode 100644 index 0000000..192da00 --- /dev/null +++ b/xhiveframework/patches/v11_0/rename_standard_reply_to_email_template.py @@ -0,0 +1,8 @@ +import xhiveframework +from xhiveframework.model.rename_doc import rename_doc + + +def execute(): + if xhiveframework.db.table_exists("Standard Reply") and not xhiveframework.db.table_exists("Email Template"): + rename_doc("DocType", "Standard Reply", "Email Template") + xhiveframework.reload_doc("email", "doctype", "email_template") diff --git a/xhiveframework/patches/v11_0/rename_workflow_action_to_workflow_action_master.py b/xhiveframework/patches/v11_0/rename_workflow_action_to_workflow_action_master.py new file mode 100644 index 0000000..577224d --- /dev/null +++ b/xhiveframework/patches/v11_0/rename_workflow_action_to_workflow_action_master.py @@ -0,0 +1,8 @@ +import xhiveframework +from xhiveframework.model.rename_doc import rename_doc + + +def execute(): + if xhiveframework.db.table_exists("Workflow Action") and not xhiveframework.db.table_exists("Workflow Action Master"): + rename_doc("DocType", "Workflow Action", "Workflow Action Master") + xhiveframework.reload_doc("workflow", "doctype", "workflow_action_master") diff --git a/xhiveframework/patches/v11_0/replicate_old_user_permissions.py b/xhiveframework/patches/v11_0/replicate_old_user_permissions.py new file mode 100644 index 0000000..500ee8b --- /dev/null +++ b/xhiveframework/patches/v11_0/replicate_old_user_permissions.py @@ -0,0 +1,97 @@ +import json + +import xhiveframework +from xhiveframework.permissions import get_valid_perms +from xhiveframework.utils import cint + + +def execute(): + xhiveframework.reload_doctype("User Permission") + user_permissions = xhiveframework.get_all("User Permission", fields=["allow", "name", "user"]) + + doctype_to_skip_map = {} + + for permission in user_permissions: + if (permission.allow, permission.user) not in doctype_to_skip_map: + doctype_to_skip_map[(permission.allow, permission.user)] = get_doctypes_to_skip( + permission.allow, permission.user + ) + + if not doctype_to_skip_map: + return + for key, doctype_to_skip in doctype_to_skip_map.items(): + if not doctype_to_skip: + continue + if not xhiveframework.db.has_column("User Permission", "applicable_for") and xhiveframework.db.has_column( + "User Permission", "skip_for_doctype" + ): + doctype_to_skip = "\n".join(doctype_to_skip) + xhiveframework.db.sql( + """ + update `tabUser Permission` + set skip_for_doctype = %s + where user=%s and allow=%s + """, + (doctype_to_skip, key[1], key[0]), + ) + + +def get_doctypes_to_skip(doctype, user): + """Returns doctypes to be skipped from user permission check""" + doctypes_to_skip = [] + valid_perms = get_user_valid_perms(user) or [] + for perm in valid_perms: + parent_doctype = perm.parent + try: + linked_doctypes = get_linked_doctypes(parent_doctype) + if doctype not in linked_doctypes: + continue + except xhiveframework.DoesNotExistError: + # if doctype not found (may be due to rename) it should not be considered for skip + continue + + if not cint(perm.apply_user_permissions): + # add doctype to skip list if any of the perm does not apply user permission + doctypes_to_skip.append(parent_doctype) + + elif parent_doctype not in doctypes_to_skip: + user_permission_doctypes = get_user_permission_doctypes(perm) + + # "No doctypes present" indicates that user permission will be applied to each link field + if not user_permission_doctypes: + continue + + elif doctype in user_permission_doctypes: + continue + + else: + doctypes_to_skip.append(parent_doctype) + # remove possible duplicates + return list(set(doctypes_to_skip)) + + +# store user's valid perms to avoid repeated query +user_valid_perm = {} + + +def get_user_valid_perms(user): + if not user_valid_perm.get(user): + user_valid_perm[user] = get_valid_perms(user=user) + return user_valid_perm.get(user) + + +def get_user_permission_doctypes(perm): + try: + return json.loads(perm.user_permission_doctypes or "[]") + except ValueError: + return [] + + +def get_linked_doctypes(doctype): + from xhiveframework.permissions import get_linked_doctypes + + linked_doctypes = get_linked_doctypes(doctype) + child_doctypes = [d.options for d in xhiveframework.get_meta(doctype).get_table_fields()] + for child_dt in child_doctypes: + linked_doctypes += get_linked_doctypes(child_dt) + return linked_doctypes diff --git a/xhiveframework/patches/v11_0/set_allow_self_approval_in_workflow.py b/xhiveframework/patches/v11_0/set_allow_self_approval_in_workflow.py new file mode 100644 index 0000000..b748fa7 --- /dev/null +++ b/xhiveframework/patches/v11_0/set_allow_self_approval_in_workflow.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("workflow", "doctype", "workflow_transition") + xhiveframework.db.sql("update `tabWorkflow Transition` set allow_self_approval=1") diff --git a/xhiveframework/patches/v11_0/set_default_letter_head_source.py b/xhiveframework/patches/v11_0/set_default_letter_head_source.py new file mode 100644 index 0000000..c77ac99 --- /dev/null +++ b/xhiveframework/patches/v11_0/set_default_letter_head_source.py @@ -0,0 +1,8 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("Letter Head") + + # source of all existing letter heads must be HTML + xhiveframework.db.sql("update `tabLetter Head` set source = 'HTML'") diff --git a/xhiveframework/patches/v11_0/set_dropbox_file_backup.py b/xhiveframework/patches/v11_0/set_dropbox_file_backup.py new file mode 100644 index 0000000..6cd4284 --- /dev/null +++ b/xhiveframework/patches/v11_0/set_dropbox_file_backup.py @@ -0,0 +1,9 @@ +import xhiveframework +from xhiveframework.utils import cint + + +def execute(): + xhiveframework.reload_doctype("Dropbox Settings") + check_dropbox_enabled = cint(xhiveframework.db.get_single_value("Dropbox Settings", "enabled")) + if check_dropbox_enabled == 1: + xhiveframework.db.set_single_value("Dropbox Settings", "file_backup", 1) diff --git a/xhiveframework/patches/v11_0/set_missing_creation_and_modified_value_for_user_permissions.py b/xhiveframework/patches/v11_0/set_missing_creation_and_modified_value_for_user_permissions.py new file mode 100644 index 0000000..55a0465 --- /dev/null +++ b/xhiveframework/patches/v11_0/set_missing_creation_and_modified_value_for_user_permissions.py @@ -0,0 +1,9 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.sql( + """UPDATE `tabUser Permission` + SET `modified`=NOW(), `creation`=NOW() + WHERE `creation` IS NULL""" + ) diff --git a/xhiveframework/patches/v11_0/update_list_user_settings.py b/xhiveframework/patches/v11_0/update_list_user_settings.py new file mode 100644 index 0000000..b92d25b --- /dev/null +++ b/xhiveframework/patches/v11_0/update_list_user_settings.py @@ -0,0 +1,33 @@ +import json + +import xhiveframework +from xhiveframework.model.utils.user_settings import sync_user_settings, update_user_settings + + +def execute(): + """Update list_view's order by property from __UserSettings""" + + users = xhiveframework.db.sql("select distinct(user) from `__UserSettings`", as_dict=True) + + for user in users: + # get user_settings for each user + settings = xhiveframework.db.sql( + f"select * from `__UserSettings` \ + where user={xhiveframework.db.escape(user.user)}", + as_dict=True, + ) + + # traverse through each doctype's settings for a user + for d in settings: + data = json.loads(d["data"]) + if data and ("List" in data) and ("order_by" in data["List"]) and data["List"]["order_by"]: + # convert order_by to sort_order & sort_by and delete order_by + order_by = data["List"]["order_by"] + if "`" in order_by and "." in order_by: + order_by = order_by.replace("`", "").split(".")[1] + + data["List"]["sort_by"], data["List"]["sort_order"] = order_by.split(" ") + data["List"].pop("order_by") + update_user_settings(d["doctype"], json.dumps(data), for_update=True) + + sync_user_settings() diff --git a/xhiveframework/patches/v12_0/__init__.py b/xhiveframework/patches/v12_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v12_0/change_existing_dashboard_chart_filters.py b/xhiveframework/patches/v12_0/change_existing_dashboard_chart_filters.py new file mode 100644 index 0000000..6a8af96 --- /dev/null +++ b/xhiveframework/patches/v12_0/change_existing_dashboard_chart_filters.py @@ -0,0 +1,31 @@ +import json + +import xhiveframework + + +def execute(): + if not xhiveframework.db.table_exists("Dashboard Chart"): + return + + charts_to_modify = xhiveframework.get_all( + "Dashboard Chart", + fields=["name", "filters_json", "document_type"], + filters={"chart_type": ["not in", ["Report", "Custom"]]}, + ) + + for chart in charts_to_modify: + old_filters = xhiveframework.parse_json(chart.filters_json) + + if chart.filters_json and isinstance(old_filters, dict): + new_filters = [] + doctype = chart.document_type + + for key in old_filters.keys(): + filter_value = old_filters[key] + if isinstance(filter_value, list): + new_filters.append([doctype, key, filter_value[0], filter_value[1], 0]) + else: + new_filters.append([doctype, key, "=", filter_value, 0]) + + new_filters_json = json.dumps(new_filters) + xhiveframework.db.set_value("Dashboard Chart", chart.name, "filters_json", new_filters_json) diff --git a/xhiveframework/patches/v12_0/create_notification_settings_for_user.py b/xhiveframework/patches/v12_0/create_notification_settings_for_user.py new file mode 100644 index 0000000..dd3edec --- /dev/null +++ b/xhiveframework/patches/v12_0/create_notification_settings_for_user.py @@ -0,0 +1,13 @@ +import xhiveframework +from xhiveframework.desk.doctype.notification_settings.notification_settings import ( + create_notification_settings, +) + + +def execute(): + xhiveframework.reload_doc("desk", "doctype", "notification_settings") + xhiveframework.reload_doc("desk", "doctype", "notification_subscribed_document") + + users = xhiveframework.get_all("User", fields=["name"]) + for user in users: + create_notification_settings(user.name) diff --git a/xhiveframework/patches/v12_0/delete_duplicate_indexes.py b/xhiveframework/patches/v12_0/delete_duplicate_indexes.py new file mode 100644 index 0000000..b259e35 --- /dev/null +++ b/xhiveframework/patches/v12_0/delete_duplicate_indexes.py @@ -0,0 +1,55 @@ +import xhiveframework + +# This patch deletes all the duplicate indexes created for same column +# The patch only checks for indexes with UNIQUE constraints + + +def execute(): + if xhiveframework.db.db_type != "mariadb": + return + + all_tables = xhiveframework.db.get_tables() + final_deletion_map = xhiveframework._dict() + + for table in all_tables: + indexes_to_keep_map = xhiveframework._dict() + indexes_to_delete = [] + index_info = xhiveframework.db.sql( + f"""SHOW INDEX FROM `{table}` + WHERE Seq_in_index = 1 + AND Non_unique=0""", + as_dict=1, + ) + + for index in index_info: + if not indexes_to_keep_map.get(index.Column_name): + indexes_to_keep_map[index.Column_name] = index + else: + indexes_to_delete.append(index.Key_name) + + if indexes_to_delete: + final_deletion_map[table] = indexes_to_delete + + for table_name, index_list in final_deletion_map.items(): + for index in index_list: + try: + if is_clustered_index(table_name, index): + continue + xhiveframework.db.sql_ddl(f"ALTER TABLE `{table_name}` DROP INDEX `{index}`") + except Exception as e: + xhiveframework.log_error("Failed to drop index") + print(f"x Failed to drop index {index} from {table_name}\n {e!s}") + else: + print(f"✓ dropped {index} index from {table}") + + +def is_clustered_index(table, index_name): + return bool( + xhiveframework.db.sql( + f"""SHOW INDEX FROM `{table}` + WHERE Key_name = "{index_name}" + AND Seq_in_index = 2 + """, + as_dict=True, + ) + ) diff --git a/xhiveframework/patches/v12_0/delete_feedback_request_if_exists.py b/xhiveframework/patches/v12_0/delete_feedback_request_if_exists.py new file mode 100644 index 0000000..df5e039 --- /dev/null +++ b/xhiveframework/patches/v12_0/delete_feedback_request_if_exists.py @@ -0,0 +1,5 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.delete("DocType", {"name": "Feedback Request"}) diff --git a/xhiveframework/patches/v12_0/fix_email_id_formatting.py b/xhiveframework/patches/v12_0/fix_email_id_formatting.py new file mode 100644 index 0000000..49baf0a --- /dev/null +++ b/xhiveframework/patches/v12_0/fix_email_id_formatting.py @@ -0,0 +1,56 @@ +import xhiveframework + + +def execute(): + fix_communications() + fix_show_as_cc_email_queue() + fix_email_queue_recipients() + + +def fix_communications(): + for communication in xhiveframework.db.sql( + """select name, recipients, cc, bcc from tabCommunication + where creation > '2020-06-01' + and communication_medium='Email' + and communication_type='Communication' + and (cc like '%<%' or bcc like '%<%' or recipients like '%<%') + """, + as_dict=1, + ): + communication["recipients"] = format_email_id(communication.recipients) + communication["cc"] = format_email_id(communication.cc) + communication["bcc"] = format_email_id(communication.bcc) + + xhiveframework.db.sql( + """update `tabCommunication` set recipients=%s,cc=%s,bcc=%s + where name =%s """, + (communication["recipients"], communication["cc"], communication["bcc"], communication["name"]), + ) + + +def fix_show_as_cc_email_queue(): + for queue in xhiveframework.get_all( + "Email Queue", + {"creation": [">", "2020-06-01"], "status": "Not Sent", "show_as_cc": ["like", "%<%"]}, + ["name", "show_as_cc"], + ): + xhiveframework.db.set_value("Email Queue", queue["name"], "show_as_cc", format_email_id(queue["show_as_cc"])) + + +def fix_email_queue_recipients(): + for recipient in xhiveframework.db.sql( + """select recipient, name from + `tabEmail Queue Recipient` where recipient like '%<%' + and status='Not Sent' and creation > '2020-06-01' """, + as_dict=1, + ): + xhiveframework.db.set_value( + "Email Queue Recipient", recipient["name"], "recipient", format_email_id(recipient["recipient"]) + ) + + +def format_email_id(email): + if email and ("<" in email and ">" in email): + return email.replace(">", ">").replace("<", "<") + + return email diff --git a/xhiveframework/patches/v12_0/fix_public_private_files.py b/xhiveframework/patches/v12_0/fix_public_private_files.py new file mode 100644 index 0000000..649ca6c --- /dev/null +++ b/xhiveframework/patches/v12_0/fix_public_private_files.py @@ -0,0 +1,34 @@ +import xhiveframework + + +def execute(): + files = xhiveframework.get_all("File", fields=["is_private", "file_url", "name"], filters={"is_folder": 0}) + + for file in files: + file_url = file.file_url or "" + if file.is_private: + if not file_url.startswith("/private/files/"): + generate_file(file.name) + else: + if file_url.startswith("/private/files/"): + generate_file(file.name) + + +def generate_file(file_name): + try: + file_doc = xhiveframework.get_doc("File", file_name) + # private + new_doc = xhiveframework.new_doc("File") + new_doc.is_private = file_doc.is_private + new_doc.file_name = file_doc.file_name + # to create copy of file in right location + # if the file doc is private then the file will be created in /private folder + # if the file doc is public then the file will be created in /files folder + new_doc.save_file(content=file_doc.get_content(), ignore_existing_file_check=True) + + file_doc.file_url = new_doc.file_url + file_doc.save() + except OSError: + pass + except Exception as e: + print(e) diff --git a/xhiveframework/patches/v12_0/move_email_and_phone_to_child_table.py b/xhiveframework/patches/v12_0/move_email_and_phone_to_child_table.py new file mode 100644 index 0000000..7a8a8fe --- /dev/null +++ b/xhiveframework/patches/v12_0/move_email_and_phone_to_child_table.py @@ -0,0 +1,105 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("contacts", "doctype", "contact_email") + xhiveframework.reload_doc("contacts", "doctype", "contact_phone") + xhiveframework.reload_doc("contacts", "doctype", "contact") + + contact_details = xhiveframework.db.sql( + """ + SELECT + `name`, `email_id`, `phone`, `mobile_no`, `modified_by`, `creation`, `modified` + FROM `tabContact` + where not exists (select * from `tabContact Email` + where `tabContact Email`.parent=`tabContact`.name + and `tabContact Email`.email_id=`tabContact`.email_id) + """, + as_dict=True, + ) + + email_values = [] + phone_values = [] + for count, contact_detail in enumerate(contact_details): + phone_counter = 1 + if contact_detail.email_id: + email_values.append( + ( + 1, + xhiveframework.generate_hash(length=10), + contact_detail.email_id, + "email_ids", + "Contact", + contact_detail.name, + 1, + contact_detail.creation, + contact_detail.modified, + contact_detail.modified_by, + ) + ) + + if contact_detail.phone: + is_primary_phone = 1 if phone_counter == 1 else 0 + phone_values.append( + ( + phone_counter, + xhiveframework.generate_hash(length=10), + contact_detail.phone, + "phone_nos", + "Contact", + contact_detail.name, + is_primary_phone, + 0, + contact_detail.creation, + contact_detail.modified, + contact_detail.modified_by, + ) + ) + phone_counter += 1 + + if contact_detail.mobile_no: + is_primary_mobile_no = 1 if phone_counter == 1 else 0 + phone_values.append( + ( + phone_counter, + xhiveframework.generate_hash(length=10), + contact_detail.mobile_no, + "phone_nos", + "Contact", + contact_detail.name, + 0, + is_primary_mobile_no, + contact_detail.creation, + contact_detail.modified, + contact_detail.modified_by, + ) + ) + + if email_values and (count % 10000 == 0 or count == len(contact_details) - 1): + xhiveframework.db.sql( + """ + INSERT INTO `tabContact Email` + (`idx`, `name`, `email_id`, `parentfield`, `parenttype`, `parent`, `is_primary`, `creation`, + `modified`, `modified_by`) + VALUES {} + """.format(", ".join(["%s"] * len(email_values))), + tuple(email_values), + ) + + email_values = [] + + if phone_values and (count % 10000 == 0 or count == len(contact_details) - 1): + xhiveframework.db.sql( + """ + INSERT INTO `tabContact Phone` + (`idx`, `name`, `phone`, `parentfield`, `parenttype`, `parent`, `is_primary_phone`, `is_primary_mobile_no`, `creation`, + `modified`, `modified_by`) + VALUES {} + """.format(", ".join(["%s"] * len(phone_values))), + tuple(phone_values), + ) + + phone_values = [] + + xhiveframework.db.add_index("Contact Phone", ["phone"]) + xhiveframework.db.add_index("Contact Email", ["email_id"]) diff --git a/xhiveframework/patches/v12_0/move_form_attachments_to_attachments_folder.py b/xhiveframework/patches/v12_0/move_form_attachments_to_attachments_folder.py new file mode 100644 index 0000000..ec0fa65 --- /dev/null +++ b/xhiveframework/patches/v12_0/move_form_attachments_to_attachments_folder.py @@ -0,0 +1,12 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.sql( + """ + UPDATE tabFile + SET folder = 'Home/Attachments' + WHERE ifnull(attached_to_doctype, '') != '' + AND folder = 'Home' + """ + ) diff --git a/xhiveframework/patches/v12_0/move_timeline_links_to_dynamic_links.py b/xhiveframework/patches/v12_0/move_timeline_links_to_dynamic_links.py new file mode 100644 index 0000000..bde8b64 --- /dev/null +++ b/xhiveframework/patches/v12_0/move_timeline_links_to_dynamic_links.py @@ -0,0 +1,64 @@ +import xhiveframework + + +def execute(): + communications = xhiveframework.db.sql( + """ + SELECT + `tabCommunication`.name, `tabCommunication`.creation, `tabCommunication`.modified, + `tabCommunication`.modified_by,`tabCommunication`.timeline_doctype, `tabCommunication`.timeline_name, + `tabCommunication`.link_doctype, `tabCommunication`.link_name + FROM `tabCommunication` + WHERE `tabCommunication`.communication_medium='Email' + """, + as_dict=True, + ) + + name = 1000000000 + values = [] + + for count, communication in enumerate(communications): + counter = 1 + if communication.timeline_doctype and communication.timeline_name: + name += 1 + values.append( + """({}, "{}", "timeline_links", "Communication", "{}", "{}", "{}", "{}", "{}", "{}")""".format( + counter, + str(name), + xhiveframework.db.escape(communication.name), + xhiveframework.db.escape(communication.timeline_doctype), + xhiveframework.db.escape(communication.timeline_name), + communication.creation, + communication.modified, + communication.modified_by, + ) + ) + counter += 1 + if communication.link_doctype and communication.link_name: + name += 1 + values.append( + """({}, "{}", "timeline_links", "Communication", "{}", "{}", "{}", "{}", "{}", "{}")""".format( + counter, + str(name), + xhiveframework.db.escape(communication.name), + xhiveframework.db.escape(communication.link_doctype), + xhiveframework.db.escape(communication.link_name), + communication.creation, + communication.modified, + communication.modified_by, + ) + ) + + if values and (count % 10000 == 0 or count == len(communications) - 1): + xhiveframework.db.sql( + """ + INSERT INTO `tabCommunication Link` + (`idx`, `name`, `parentfield`, `parenttype`, `parent`, `link_doctype`, `link_name`, `creation`, + `modified`, `modified_by`) + VALUES {} + """.format(", ".join([d for d in values])) + ) + + values = [] + + xhiveframework.db.add_index("Communication Link", ["link_doctype", "link_name"]) diff --git a/xhiveframework/patches/v12_0/remove_deprecated_fields_from_doctype.py b/xhiveframework/patches/v12_0/remove_deprecated_fields_from_doctype.py new file mode 100644 index 0000000..ad1dfb7 --- /dev/null +++ b/xhiveframework/patches/v12_0/remove_deprecated_fields_from_doctype.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "doctype_link") + xhiveframework.reload_doc("core", "doctype", "doctype_action") + xhiveframework.reload_doc("core", "doctype", "doctype") + xhiveframework.model.delete_fields({"DocType": ["hide_heading", "image_view", "read_only_onload"]}, delete=1) + + xhiveframework.db.delete("Property Setter", {"property": "read_only_onload"}) diff --git a/xhiveframework/patches/v12_0/remove_example_email_thread_notify.py b/xhiveframework/patches/v12_0/remove_example_email_thread_notify.py new file mode 100644 index 0000000..4ba7db0 --- /dev/null +++ b/xhiveframework/patches/v12_0/remove_example_email_thread_notify.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + # remove all example.com email user accounts from notifications + xhiveframework.db.sql( + """UPDATE `tabUser` + SET thread_notify=0, send_me_a_copy=0 + WHERE email like '%@example.com'""" + ) diff --git a/xhiveframework/patches/v12_0/remove_feedback_rating.py b/xhiveframework/patches/v12_0/remove_feedback_rating.py new file mode 100644 index 0000000..73b1c54 --- /dev/null +++ b/xhiveframework/patches/v12_0/remove_feedback_rating.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + """ + Deprecate Feedback Trigger and Rating. This feature was not customizable. + Now can be achieved via custom Web Forms + """ + xhiveframework.delete_doc("DocType", "Feedback Trigger") + xhiveframework.delete_doc("DocType", "Feedback Rating") diff --git a/xhiveframework/patches/v12_0/rename_events_repeat_on.py b/xhiveframework/patches/v12_0/rename_events_repeat_on.py new file mode 100644 index 0000000..9ddfbbd --- /dev/null +++ b/xhiveframework/patches/v12_0/rename_events_repeat_on.py @@ -0,0 +1,37 @@ +import xhiveframework +from xhiveframework.utils import get_datetime + + +def execute(): + weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] + + weekly_events = xhiveframework.get_list( + "Event", + filters={"repeat_this_event": 1, "repeat_on": "Every Week"}, + fields=["name", "starts_on"], + ) + xhiveframework.reload_doc("desk", "doctype", "event") + + # Initially Daily Events had option to choose days, but now Weekly does, so just changing from Daily -> Weekly does the job + xhiveframework.db.sql( + """UPDATE `tabEvent` SET `tabEvent`.repeat_on='Weekly' WHERE `tabEvent`.repeat_on='Every Day'""" + ) + xhiveframework.db.sql( + """UPDATE `tabEvent` SET `tabEvent`.repeat_on='Weekly' WHERE `tabEvent`.repeat_on='Every Week'""" + ) + xhiveframework.db.sql( + """UPDATE `tabEvent` SET `tabEvent`.repeat_on='Monthly' WHERE `tabEvent`.repeat_on='Every Month'""" + ) + xhiveframework.db.sql( + """UPDATE `tabEvent` SET `tabEvent`.repeat_on='Yearly' WHERE `tabEvent`.repeat_on='Every Year'""" + ) + + for weekly_event in weekly_events: + # Set WeekDay based on the starts_on so that event can repeat Weekly + xhiveframework.db.set_value( + "Event", + weekly_event.name, + weekdays[get_datetime(weekly_event.starts_on).weekday()], + 1, + update_modified=False, + ) diff --git a/xhiveframework/patches/v12_0/rename_uploaded_files_with_proper_name.py b/xhiveframework/patches/v12_0/rename_uploaded_files_with_proper_name.py new file mode 100644 index 0000000..ef8bad0 --- /dev/null +++ b/xhiveframework/patches/v12_0/rename_uploaded_files_with_proper_name.py @@ -0,0 +1,33 @@ +import os + +import xhiveframework + + +def execute(): + file_names_with_url = xhiveframework.get_all( + "File", + filters={"is_folder": 0, "file_name": ["like", "%/%"]}, + fields=["name", "file_name", "file_url"], + ) + + for f in file_names_with_url: + filename = f.file_name.rsplit("/", 1)[-1] + + if not f.file_url: + f.file_url = f.file_name + + try: + if not file_exists(f.file_url): + continue + xhiveframework.db.set_value( + "File", f.name, {"file_name": filename, "file_url": f.file_url}, update_modified=False + ) + except Exception: + continue + + +def file_exists(file_path): + file_path = xhiveframework.utils.get_files_path( + file_path.rsplit("/", 1)[-1], is_private=file_path.startswith("/private") + ) + return os.path.exists(file_path) diff --git a/xhiveframework/patches/v12_0/replace_null_values_in_tables.py b/xhiveframework/patches/v12_0/replace_null_values_in_tables.py new file mode 100644 index 0000000..d0a84ca --- /dev/null +++ b/xhiveframework/patches/v12_0/replace_null_values_in_tables.py @@ -0,0 +1,30 @@ +import re + +import xhiveframework + + +def execute(): + fields = xhiveframework.db.sql( + """ + SELECT COLUMN_NAME , TABLE_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS + WHERE DATA_TYPE IN ('INT', 'FLOAT', 'DECIMAL') AND IS_NULLABLE = 'YES' + """, + as_dict=1, + ) + + update_column_table_map = {} + + for field in fields: + update_column_table_map.setdefault(field.TABLE_NAME, []) + + update_column_table_map[field.TABLE_NAME].append( + f"`{field.COLUMN_NAME}`=COALESCE(`{field.COLUMN_NAME}`, 0)" + ) + + for table in xhiveframework.db.get_tables(): + if update_column_table_map.get(table) and xhiveframework.db.exists("DocType", re.sub("^tab", "", table)): + xhiveframework.db.sql( + """UPDATE `{table}` SET {columns}""".format( + table=table, columns=", ".join(update_column_table_map.get(table)) + ) + ) diff --git a/xhiveframework/patches/v12_0/reset_home_settings.py b/xhiveframework/patches/v12_0/reset_home_settings.py new file mode 100644 index 0000000..87bf85a --- /dev/null +++ b/xhiveframework/patches/v12_0/reset_home_settings.py @@ -0,0 +1,12 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "user") + xhiveframework.db.sql( + """ + UPDATE `tabUser` + SET `home_settings` = '' + WHERE `user_type` = 'System User' + """ + ) diff --git a/xhiveframework/patches/v12_0/set_correct_assign_value_in_docs.py b/xhiveframework/patches/v12_0/set_correct_assign_value_in_docs.py new file mode 100644 index 0000000..e786733 --- /dev/null +++ b/xhiveframework/patches/v12_0/set_correct_assign_value_in_docs.py @@ -0,0 +1,28 @@ +import xhiveframework +from xhiveframework.query_builder.functions import Coalesce, GroupConcat + + +def execute(): + xhiveframework.reload_doc("desk", "doctype", "todo") + + ToDo = xhiveframework.qb.DocType("ToDo") + assignees = GroupConcat("owner").distinct().as_("assignees") + + assignments = ( + xhiveframework.qb.from_(ToDo) + .select(ToDo.name, ToDo.reference_type, assignees) + .where(Coalesce(ToDo.reference_type, "") != "") + .where(Coalesce(ToDo.reference_name, "") != "") + .where(ToDo.status != "Cancelled") + .groupby(ToDo.reference_type, ToDo.reference_name) + ).run(as_dict=True) + + for doc in assignments: + assignments = doc.assignees.split(",") + xhiveframework.db.set_value( + doc.reference_type, + doc.reference_name, + "_assign", + xhiveframework.as_json(assignments), + update_modified=False, + ) diff --git a/xhiveframework/patches/v12_0/set_correct_url_in_files.py b/xhiveframework/patches/v12_0/set_correct_url_in_files.py new file mode 100644 index 0000000..0e35b1b --- /dev/null +++ b/xhiveframework/patches/v12_0/set_correct_url_in_files.py @@ -0,0 +1,41 @@ +import os + +import xhiveframework + + +def execute(): + files = xhiveframework.get_all( + "File", + fields=["name", "file_name", "file_url"], + filters={ + "is_folder": 0, + "file_url": ["!=", ""], + }, + ) + + private_file_path = xhiveframework.get_site_path("private", "files") + public_file_path = xhiveframework.get_site_path("public", "files") + + for file in files: + file_path = file.file_url + file_name = file_path.split("/")[-1] + + if not file_path.startswith(("/private/", "/files/")): + continue + + file_is_private = file_path.startswith("/private/files/") + full_path = xhiveframework.utils.get_files_path(file_name, is_private=file_is_private) + + if not os.path.exists(full_path): + if file_is_private: + public_file_url = os.path.join(public_file_path, file_name) + if os.path.exists(public_file_url): + xhiveframework.db.set_value( + "File", file.name, {"file_url": f"/files/{file_name}", "is_private": 0} + ) + else: + private_file_url = os.path.join(private_file_path, file_name) + if os.path.exists(private_file_url): + xhiveframework.db.set_value( + "File", file.name, {"file_url": f"/private/files/{file_name}", "is_private": 1} + ) diff --git a/xhiveframework/patches/v12_0/set_default_incoming_email_port.py b/xhiveframework/patches/v12_0/set_default_incoming_email_port.py new file mode 100644 index 0000000..373ea5b --- /dev/null +++ b/xhiveframework/patches/v12_0/set_default_incoming_email_port.py @@ -0,0 +1,43 @@ +import xhiveframework +from xhiveframework.email.utils import get_port + + +def execute(): + """ + 1. Set default incoming email port in email domain + 2. Set default incoming email port in all email account (for those account where domain is missing) + """ + xhiveframework.reload_doc("email", "doctype", "email_domain", force=True) + xhiveframework.reload_doc("email", "doctype", "email_account", force=True) + + setup_incoming_email_port_in_email_domains() + setup_incoming_email_port_in_email_accounts() + + +def setup_incoming_email_port_in_email_domains(): + email_domains = xhiveframework.get_all("Email Domain", ["incoming_port", "use_imap", "use_ssl", "name"]) + for domain in email_domains: + if not domain.incoming_port: + incoming_port = get_port(domain) + xhiveframework.db.set_value( + "Email Domain", domain.name, "incoming_port", incoming_port, update_modified=False + ) + + # update incoming email port in all + xhiveframework.db.sql( + """update `tabEmail Account` set incoming_port=%s where domain = %s""", + (domain.incoming_port, domain.name), + ) + + +def setup_incoming_email_port_in_email_accounts(): + email_accounts = xhiveframework.get_all( + "Email Account", ["incoming_port", "use_imap", "use_ssl", "name", "enable_incoming"] + ) + + for account in email_accounts: + if account.enable_incoming and not account.incoming_port: + incoming_port = get_port(account) + xhiveframework.db.set_value( + "Email Account", account.name, "incoming_port", incoming_port, update_modified=False + ) diff --git a/xhiveframework/patches/v12_0/set_default_password_reset_limit.py b/xhiveframework/patches/v12_0/set_default_password_reset_limit.py new file mode 100644 index 0000000..c43e497 --- /dev/null +++ b/xhiveframework/patches/v12_0/set_default_password_reset_limit.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "system_settings", force=1) + xhiveframework.db.set_single_value("System Settings", "password_reset_limit", 3) diff --git a/xhiveframework/patches/v12_0/set_primary_key_in_series.py b/xhiveframework/patches/v12_0/set_primary_key_in_series.py new file mode 100644 index 0000000..e8ecc2b --- /dev/null +++ b/xhiveframework/patches/v12_0/set_primary_key_in_series.py @@ -0,0 +1,26 @@ +import xhiveframework + + +def execute(): + # if current = 0, simply delete the key as it'll be recreated on first entry + xhiveframework.db.delete("Series", {"current": 0}) + + duplicate_keys = xhiveframework.db.sql( + """ + SELECT name, max(current) as current + from + `tabSeries` + group by + name + having count(name) > 1 + """, + as_dict=True, + ) + + for row in duplicate_keys: + xhiveframework.db.delete("Series", {"name": row.name}) + if row.current: + xhiveframework.db.sql("insert into `tabSeries`(`name`, `current`) values (%(name)s, %(current)s)", row) + xhiveframework.db.commit() + + xhiveframework.db.sql("ALTER table `tabSeries` ADD PRIMARY KEY IF NOT EXISTS (name)") diff --git a/xhiveframework/patches/v12_0/setup_comments_from_communications.py b/xhiveframework/patches/v12_0/setup_comments_from_communications.py new file mode 100644 index 0000000..a04d8f8 --- /dev/null +++ b/xhiveframework/patches/v12_0/setup_comments_from_communications.py @@ -0,0 +1,31 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("Comment") + + if xhiveframework.db.count("Communication", filters=dict(communication_type="Comment")) > 20000: + xhiveframework.db.auto_commit_on_many_writes = True + + for comment in xhiveframework.get_all("Communication", fields=["*"], filters=dict(communication_type="Comment")): + new_comment = xhiveframework.new_doc("Comment") + new_comment.comment_type = comment.comment_type + new_comment.comment_email = comment.sender + new_comment.comment_by = comment.sender_full_name + new_comment.subject = comment.subject + new_comment.content = comment.content or comment.subject + new_comment.reference_doctype = comment.reference_doctype + new_comment.reference_name = comment.reference_name + new_comment.link_doctype = comment.link_doctype + new_comment.link_name = comment.link_name + new_comment.creation = comment.creation + new_comment.modified = comment.modified + new_comment.owner = comment.owner + new_comment.modified_by = comment.modified_by + new_comment.db_insert() + + if xhiveframework.db.auto_commit_on_many_writes: + xhiveframework.db.auto_commit_on_many_writes = False + + # clean up + xhiveframework.db.delete("Communication", {"communication_type": "Comment"}) diff --git a/xhiveframework/patches/v12_0/setup_email_linking.py b/xhiveframework/patches/v12_0/setup_email_linking.py new file mode 100644 index 0000000..fd47442 --- /dev/null +++ b/xhiveframework/patches/v12_0/setup_email_linking.py @@ -0,0 +1,5 @@ +from xhiveframework.desk.page.setup_wizard.install_fixtures import setup_email_linking + + +def execute(): + setup_email_linking() diff --git a/xhiveframework/patches/v12_0/setup_tags.py b/xhiveframework/patches/v12_0/setup_tags.py new file mode 100644 index 0000000..3efce11 --- /dev/null +++ b/xhiveframework/patches/v12_0/setup_tags.py @@ -0,0 +1,47 @@ +import xhiveframework + + +def execute(): + xhiveframework.delete_doc_if_exists("DocType", "Tag Category") + xhiveframework.delete_doc_if_exists("DocType", "Tag Doc Category") + + xhiveframework.reload_doc("desk", "doctype", "tag") + xhiveframework.reload_doc("desk", "doctype", "tag_link") + + tag_list = [] + tag_links = [] + time = xhiveframework.utils.get_datetime() + + for doctype in xhiveframework.get_list("DocType", filters={"istable": 0, "issingle": 0, "is_virtual": 0}): + if not xhiveframework.db.count(doctype.name) or not xhiveframework.db.has_column(doctype.name, "_user_tags"): + continue + + for _user_tags in xhiveframework.db.sql( + f"select `name`, `_user_tags` from `tab{doctype.name}`", as_dict=True + ): + if not _user_tags.get("_user_tags"): + continue + + for tag in _user_tags.get("_user_tags").split(",") if _user_tags.get("_user_tags") else []: + if not tag: + continue + + tag_list.append((tag.strip(), time, time, "Administrator")) + + tag_link_name = xhiveframework.generate_hash(length=10) + tag_links.append( + (tag_link_name, doctype.name, _user_tags.name, tag.strip(), time, time, "Administrator") + ) + + xhiveframework.db.bulk_insert( + "Tag", + fields=["name", "creation", "modified", "modified_by"], + values=set(tag_list), + ignore_duplicates=True, + ) + xhiveframework.db.bulk_insert( + "Tag Link", + fields=["name", "document_type", "document_name", "tag", "creation", "modified", "modified_by"], + values=set(tag_links), + ignore_duplicates=True, + ) diff --git a/xhiveframework/patches/v12_0/update_auto_repeat_status_and_not_submittable.py b/xhiveframework/patches/v12_0/update_auto_repeat_status_and_not_submittable.py new file mode 100644 index 0000000..f812a89 --- /dev/null +++ b/xhiveframework/patches/v12_0/update_auto_repeat_status_and_not_submittable.py @@ -0,0 +1,34 @@ +import xhiveframework +from xhiveframework.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + # auto repeat is not submittable in v12 + xhiveframework.reload_doc("automation", "doctype", "Auto Repeat") + xhiveframework.db.sql("update `tabDocPerm` set submit=0, cancel=0, amend=0 where parent='Auto Repeat'") + xhiveframework.db.sql("update `tabAuto Repeat` set docstatus=0 where docstatus=1 or docstatus=2") + + for entry in xhiveframework.get_all("Auto Repeat"): + doc = xhiveframework.get_doc("Auto Repeat", entry.name) + + # create custom field for allow auto repeat + fields = xhiveframework.get_meta(doc.reference_doctype).fields + insert_after = fields[len(fields) - 1].fieldname + df = dict( + fieldname="auto_repeat", + label="Auto Repeat", + fieldtype="Link", + insert_after=insert_after, + options="Auto Repeat", + hidden=1, + print_hide=1, + read_only=1, + ) + create_custom_field(doc.reference_doctype, df) + + if doc.status in ["Draft", "Stopped", "Cancelled"]: + doc.disabled = 1 + + doc.flags.ignore_links = 1 + # updates current status as Active, Disabled or Completed on validate + doc.save() diff --git a/xhiveframework/patches/v12_0/update_global_search.py b/xhiveframework/patches/v12_0/update_global_search.py new file mode 100644 index 0000000..0e51825 --- /dev/null +++ b/xhiveframework/patches/v12_0/update_global_search.py @@ -0,0 +1,8 @@ +import xhiveframework +from xhiveframework.desk.page.setup_wizard.install_fixtures import update_global_search_doctypes + + +def execute(): + xhiveframework.reload_doc("desk", "doctype", "global_search_doctype") + xhiveframework.reload_doc("desk", "doctype", "global_search_settings") + update_global_search_doctypes() diff --git a/xhiveframework/patches/v12_0/update_print_format_type.py b/xhiveframework/patches/v12_0/update_print_format_type.py new file mode 100644 index 0000000..5e3e55f --- /dev/null +++ b/xhiveframework/patches/v12_0/update_print_format_type.py @@ -0,0 +1,18 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.sql( + """ + UPDATE `tabPrint Format` + SET `print_format_type` = 'Jinja' + WHERE `print_format_type` in ('Server', 'Client') + """ + ) + xhiveframework.db.sql( + """ + UPDATE `tabPrint Format` + SET `print_format_type` = 'JS' + WHERE `print_format_type` = 'Js' + """ + ) diff --git a/xhiveframework/patches/v13_0/__init__.py b/xhiveframework/patches/v13_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v13_0/add_standard_navbar_items.py b/xhiveframework/patches/v13_0/add_standard_navbar_items.py new file mode 100644 index 0000000..fe9b4c6 --- /dev/null +++ b/xhiveframework/patches/v13_0/add_standard_navbar_items.py @@ -0,0 +1,9 @@ +import xhiveframework +from xhiveframework.utils.install import add_standard_navbar_items + + +def execute(): + # Add standard navbar items for XhiveERP in Navbar Settings + xhiveframework.reload_doc("core", "doctype", "navbar_settings") + xhiveframework.reload_doc("core", "doctype", "navbar_item") + add_standard_navbar_items() diff --git a/xhiveframework/patches/v13_0/add_switch_theme_to_navbar_settings.py b/xhiveframework/patches/v13_0/add_switch_theme_to_navbar_settings.py new file mode 100644 index 0000000..3ee4171 --- /dev/null +++ b/xhiveframework/patches/v13_0/add_switch_theme_to_navbar_settings.py @@ -0,0 +1,24 @@ +import xhiveframework + + +def execute(): + navbar_settings = xhiveframework.get_single("Navbar Settings") + + if xhiveframework.db.exists("Navbar Item", {"item_label": "Toggle Theme"}): + return + + for navbar_item in navbar_settings.settings_dropdown[6:]: + navbar_item.idx = navbar_item.idx + 1 + + navbar_settings.append( + "settings_dropdown", + { + "item_label": "Toggle Theme", + "item_type": "Action", + "action": "new xhiveframework.ui.ThemeSwitcher().show()", + "is_standard": 1, + "idx": 7, + }, + ) + + navbar_settings.save() diff --git a/xhiveframework/patches/v13_0/add_toggle_width_in_navbar_settings.py b/xhiveframework/patches/v13_0/add_toggle_width_in_navbar_settings.py new file mode 100644 index 0000000..4e57bfa --- /dev/null +++ b/xhiveframework/patches/v13_0/add_toggle_width_in_navbar_settings.py @@ -0,0 +1,24 @@ +import xhiveframework + + +def execute(): + navbar_settings = xhiveframework.get_single("Navbar Settings") + + if xhiveframework.db.exists("Navbar Item", {"item_label": "Toggle Full Width"}): + return + + for navbar_item in navbar_settings.settings_dropdown[5:]: + navbar_item.idx = navbar_item.idx + 1 + + navbar_settings.append( + "settings_dropdown", + { + "item_label": "Toggle Full Width", + "item_type": "Action", + "action": "xhiveframework.ui.toolbar.toggle_full_width()", + "is_standard": 1, + "idx": 6, + }, + ) + + navbar_settings.save() diff --git a/xhiveframework/patches/v13_0/create_custom_dashboards_cards_and_charts.py b/xhiveframework/patches/v13_0/create_custom_dashboards_cards_and_charts.py new file mode 100644 index 0000000..841531b --- /dev/null +++ b/xhiveframework/patches/v13_0/create_custom_dashboards_cards_and_charts.py @@ -0,0 +1,46 @@ +import xhiveframework +from xhiveframework.model.naming import append_number_if_name_exists +from xhiveframework.utils.dashboard import get_dashboards_with_link + + +def execute(): + if ( + not xhiveframework.db.table_exists("Dashboard Chart") + or not xhiveframework.db.table_exists("Number Card") + or not xhiveframework.db.table_exists("Dashboard") + ): + return + + xhiveframework.reload_doc("desk", "doctype", "dashboard_chart") + xhiveframework.reload_doc("desk", "doctype", "number_card") + xhiveframework.reload_doc("desk", "doctype", "dashboard") + + modified_charts = get_modified_docs("Dashboard Chart") + modified_cards = get_modified_docs("Number Card") + modified_dashboards = [doc.name for doc in get_modified_docs("Dashboard")] + + for chart in modified_charts: + modified_dashboards += get_dashboards_with_link(chart.name, "Dashboard Chart") + rename_modified_doc(chart.name, "Dashboard Chart") + + for card in modified_cards: + modified_dashboards += get_dashboards_with_link(card.name, "Number Card") + rename_modified_doc(card.name, "Number Card") + + modified_dashboards = list(set(modified_dashboards)) + + for dashboard in modified_dashboards: + rename_modified_doc(dashboard, "Dashboard") + + +def get_modified_docs(doctype): + return xhiveframework.get_all(doctype, filters={"owner": "Administrator", "modified_by": ["!=", "Administrator"]}) + + +def rename_modified_doc(docname, doctype): + new_name = docname + " Custom" + try: + xhiveframework.rename_doc(doctype, docname, new_name) + except xhiveframework.ValidationError: + new_name = append_number_if_name_exists(doctype, new_name) + xhiveframework.rename_doc(doctype, docname, new_name) diff --git a/xhiveframework/patches/v13_0/delete_package_publish_tool.py b/xhiveframework/patches/v13_0/delete_package_publish_tool.py new file mode 100644 index 0000000..72b5d1b --- /dev/null +++ b/xhiveframework/patches/v13_0/delete_package_publish_tool.py @@ -0,0 +1,10 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.delete_doc("DocType", "Package Publish Tool", ignore_missing=True) + xhiveframework.delete_doc("DocType", "Package Document Type", ignore_missing=True) + xhiveframework.delete_doc("DocType", "Package Publish Target", ignore_missing=True) diff --git a/xhiveframework/patches/v13_0/email_unsubscribe.py b/xhiveframework/patches/v13_0/email_unsubscribe.py new file mode 100644 index 0000000..b709d72 --- /dev/null +++ b/xhiveframework/patches/v13_0/email_unsubscribe.py @@ -0,0 +1,14 @@ +import xhiveframework + + +def execute(): + email_unsubscribe = [ + {"email": "admin@example.com", "global_unsubscribe": 1}, + {"email": "guest@example.com", "global_unsubscribe": 1}, + ] + + for unsubscribe in email_unsubscribe: + if not xhiveframework.get_all("Email Unsubscribe", filters=unsubscribe): + doc = xhiveframework.new_doc("Email Unsubscribe") + doc.update(unsubscribe) + doc.insert(ignore_permissions=True) diff --git a/xhiveframework/patches/v13_0/enable_custom_script.py b/xhiveframework/patches/v13_0/enable_custom_script.py new file mode 100644 index 0000000..5ab2aa5 --- /dev/null +++ b/xhiveframework/patches/v13_0/enable_custom_script.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + """Enable all the existing Client script""" + + xhiveframework.db.sql( + """ + UPDATE `tabClient Script` SET enabled=1 + """ + ) diff --git a/xhiveframework/patches/v13_0/encrypt_2fa_secrets.py b/xhiveframework/patches/v13_0/encrypt_2fa_secrets.py new file mode 100644 index 0000000..d281025 --- /dev/null +++ b/xhiveframework/patches/v13_0/encrypt_2fa_secrets.py @@ -0,0 +1,45 @@ +import xhiveframework +import xhiveframework.defaults +from xhiveframework.cache_manager import clear_defaults_cache +from xhiveframework.twofactor import PARENT_FOR_DEFAULTS +from xhiveframework.utils.password import encrypt + +DOCTYPE = "DefaultValue" +OLD_PARENT = "__default" + + +def execute(): + table = xhiveframework.qb.DocType(DOCTYPE) + + # set parent for `*_otplogin` + ( + xhiveframework.qb.update(table) + .set(table.parent, PARENT_FOR_DEFAULTS) + .where(table.parent == OLD_PARENT) + .where(table.defkey.like("%_otplogin")) + ).run() + + # update records for `*_otpsecret` + secrets = { + key: value + for key, value in xhiveframework.defaults.get_defaults_for(parent=OLD_PARENT).items() + if key.endswith("_otpsecret") + } + + if not secrets: + return + + defvalue_cases = xhiveframework.qb.terms.Case() + + for key, value in secrets.items(): + defvalue_cases.when(table.defkey == key, encrypt(value)) + + ( + xhiveframework.qb.update(table) + .set(table.parent, PARENT_FOR_DEFAULTS) + .set(table.defvalue, defvalue_cases) + .where(table.parent == OLD_PARENT) + .where(table.defkey.like("%_otpsecret")) + ).run() + + clear_defaults_cache() diff --git a/xhiveframework/patches/v13_0/generate_theme_files_in_public_folder.py b/xhiveframework/patches/v13_0/generate_theme_files_in_public_folder.py new file mode 100644 index 0000000..a0838f3 --- /dev/null +++ b/xhiveframework/patches/v13_0/generate_theme_files_in_public_folder.py @@ -0,0 +1,16 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "website_theme_ignore_app") + themes = xhiveframework.get_all("Website Theme", filters={"theme_url": ("not like", "/files/website_theme/%")}) + for theme in themes: + doc = xhiveframework.get_doc("Website Theme", theme.name) + try: + doc.save() + except Exception: + print("Ignoring....") + print(xhiveframework.get_traceback()) diff --git a/xhiveframework/patches/v13_0/increase_password_length.py b/xhiveframework/patches/v13_0/increase_password_length.py new file mode 100644 index 0000000..28776ef --- /dev/null +++ b/xhiveframework/patches/v13_0/increase_password_length.py @@ -0,0 +1,5 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.change_column_type("__Auth", column="password", type="TEXT") diff --git a/xhiveframework/patches/v13_0/jinja_hook.py b/xhiveframework/patches/v13_0/jinja_hook.py new file mode 100644 index 0000000..8db8361 --- /dev/null +++ b/xhiveframework/patches/v13_0/jinja_hook.py @@ -0,0 +1,17 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +from click import secho + +import xhiveframework + + +def execute(): + if xhiveframework.get_hooks("jenv"): + print() + secho( + 'WARNING: The hook "jenv" is deprecated. Follow the migration guide to use the new "jinja" hook.', + fg="yellow", + ) + secho("https://lab.membtech.com/xhiveframework/xhiveframework15/wiki/Migrating-to-Version-13", fg="yellow") + print() diff --git a/xhiveframework/patches/v13_0/make_user_type.py b/xhiveframework/patches/v13_0/make_user_type.py new file mode 100644 index 0000000..855efbd --- /dev/null +++ b/xhiveframework/patches/v13_0/make_user_type.py @@ -0,0 +1,12 @@ +import xhiveframework +from xhiveframework.utils.install import create_user_type + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "role") + xhiveframework.reload_doc("core", "doctype", "user_document_type") + xhiveframework.reload_doc("core", "doctype", "user_type_module") + xhiveframework.reload_doc("core", "doctype", "user_select_document_type") + xhiveframework.reload_doc("core", "doctype", "user_type") + + create_user_type() diff --git a/xhiveframework/patches/v13_0/migrate_translation_column_data.py b/xhiveframework/patches/v13_0/migrate_translation_column_data.py new file mode 100644 index 0000000..c877458 --- /dev/null +++ b/xhiveframework/patches/v13_0/migrate_translation_column_data.py @@ -0,0 +1,8 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("Translation") + xhiveframework.db.sql( + "UPDATE `tabTranslation` SET `translated_text`=`target_name`, `source_text`=`source_name`, `contributed`=0" + ) diff --git a/xhiveframework/patches/v13_0/queryreport_columns.py b/xhiveframework/patches/v13_0/queryreport_columns.py new file mode 100644 index 0000000..5502172 --- /dev/null +++ b/xhiveframework/patches/v13_0/queryreport_columns.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework + + +def execute(): + """Convert Query Report json to support other content""" + records = xhiveframework.get_all("Report", filters={"json": ["!=", ""]}, fields=["name", "json"]) + for record in records: + jstr = record["json"] + data = json.loads(jstr) + if isinstance(data, list): + # double escape braces + jstr = f'{{"columns":{jstr}}}' + xhiveframework.db.set_value("Report", record["name"], "json", jstr) diff --git a/xhiveframework/patches/v13_0/remove_chat.py b/xhiveframework/patches/v13_0/remove_chat.py new file mode 100644 index 0000000..817808d --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_chat.py @@ -0,0 +1,19 @@ +import click + +import xhiveframework + + +def execute(): + xhiveframework.delete_doc_if_exists("DocType", "Chat Message") + xhiveframework.delete_doc_if_exists("DocType", "Chat Message Attachment") + xhiveframework.delete_doc_if_exists("DocType", "Chat Profile") + xhiveframework.delete_doc_if_exists("DocType", "Chat Token") + xhiveframework.delete_doc_if_exists("DocType", "Chat Room User") + xhiveframework.delete_doc_if_exists("DocType", "Chat Room") + xhiveframework.delete_doc_if_exists("Module Def", "Chat") + + click.secho( + "Chat Module is moved to a separate app and is removed from XhiveFramework in version-13.\n" + "Please install the app to continue using the chat feature: https://lab.membtech.com/xhiveframework/chat", + fg="yellow", + ) diff --git a/xhiveframework/patches/v13_0/remove_custom_link.py b/xhiveframework/patches/v13_0/remove_custom_link.py new file mode 100644 index 0000000..ef22d07 --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_custom_link.py @@ -0,0 +1,18 @@ +import xhiveframework + + +def execute(): + """ + Remove the doctype "Custom Link" that was used to add Custom Links to the + Dashboard since this is now managed by Customize Form. + Update `parent` property to the DocType and delte the doctype + """ + xhiveframework.reload_doctype("DocType Link") + if xhiveframework.db.has_table("Custom Link"): + for custom_link in xhiveframework.get_all("Custom Link", ["name", "document_type"]): + xhiveframework.db.sql( + "update `tabDocType Link` set custom=1, parent=%s where parent=%s", + (custom_link.document_type, custom_link.name), + ) + + xhiveframework.delete_doc("DocType", "Custom Link") diff --git a/xhiveframework/patches/v13_0/remove_duplicate_navbar_items.py b/xhiveframework/patches/v13_0/remove_duplicate_navbar_items.py new file mode 100644 index 0000000..9323d84 --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_duplicate_navbar_items.py @@ -0,0 +1,14 @@ +import xhiveframework + + +def execute(): + navbar_settings = xhiveframework.get_single("Navbar Settings") + duplicate_items = [ + navbar_item + for navbar_item in navbar_settings.settings_dropdown + if navbar_item.item_label == "Toggle Full Width" + ] + + if len(duplicate_items) > 1: + navbar_settings.remove(duplicate_items[0]) + navbar_settings.save() diff --git a/xhiveframework/patches/v13_0/remove_invalid_options_for_data_fields.py b/xhiveframework/patches/v13_0/remove_invalid_options_for_data_fields.py new file mode 100644 index 0000000..1b4f96c --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_invalid_options_for_data_fields.py @@ -0,0 +1,15 @@ +# Copyright (c) 2022, XhiveFramework and Contributors +# License: MIT. See LICENSE + + +import xhiveframework +from xhiveframework.model import data_field_options + + +def execute(): + custom_field = xhiveframework.qb.DocType("Custom Field") + ( + xhiveframework.qb.update(custom_field) + .set(custom_field.options, None) + .where((custom_field.fieldtype == "Data") & (custom_field.options.notin(data_field_options))) + ).run() diff --git a/xhiveframework/patches/v13_0/remove_share_for_std_users.py b/xhiveframework/patches/v13_0/remove_share_for_std_users.py new file mode 100644 index 0000000..dd5ce59 --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_share_for_std_users.py @@ -0,0 +1,7 @@ +import xhiveframework +import xhiveframework.share + + +def execute(): + for user in xhiveframework.STANDARD_USERS: + xhiveframework.share.remove("User", user, user) diff --git a/xhiveframework/patches/v13_0/remove_tailwind_from_page_builder.py b/xhiveframework/patches/v13_0/remove_tailwind_from_page_builder.py new file mode 100644 index 0000000..9c5b24a --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_tailwind_from_page_builder.py @@ -0,0 +1,11 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "web_page_block") + # remove unused templates + xhiveframework.delete_doc("Web Template", "Navbar with Links on Right", force=1) + xhiveframework.delete_doc("Web Template", "Footer Horizontal", force=1) diff --git a/xhiveframework/patches/v13_0/remove_twilio_settings.py b/xhiveframework/patches/v13_0/remove_twilio_settings.py new file mode 100644 index 0000000..cd57a3a --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_twilio_settings.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + """Add missing Twilio patch. + + While making Twilio as a standaone app, we missed to delete Twilio records from DB through migration. Adding the missing patch. + """ + xhiveframework.delete_doc_if_exists("DocType", "Twilio Number Group") + if twilio_settings_doctype_in_integrations(): + xhiveframework.delete_doc_if_exists("DocType", "Twilio Settings") + xhiveframework.db.delete("Singles", {"doctype": "Twilio Settings"}) + + +def twilio_settings_doctype_in_integrations() -> bool: + """Check Twilio Settings doctype exists in integrations module or not.""" + return xhiveframework.db.exists("DocType", {"name": "Twilio Settings", "module": "Integrations"}) diff --git a/xhiveframework/patches/v13_0/remove_web_view.py b/xhiveframework/patches/v13_0/remove_web_view.py new file mode 100644 index 0000000..9d7814f --- /dev/null +++ b/xhiveframework/patches/v13_0/remove_web_view.py @@ -0,0 +1,7 @@ +import xhiveframework + + +def execute(): + xhiveframework.delete_doc_if_exists("DocType", "Web View") + xhiveframework.delete_doc_if_exists("DocType", "Web View Component") + xhiveframework.delete_doc_if_exists("DocType", "CSS Class") diff --git a/xhiveframework/patches/v13_0/rename_custom_client_script.py b/xhiveframework/patches/v13_0/rename_custom_client_script.py new file mode 100644 index 0000000..f4bcc0b --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_custom_client_script.py @@ -0,0 +1,13 @@ +import xhiveframework +from xhiveframework.model.rename_doc import rename_doc + + +def execute(): + if xhiveframework.db.exists("DocType", "Client Script"): + return + + xhiveframework.flags.ignore_route_conflict_validation = True + rename_doc("DocType", "Custom Script", "Client Script") + xhiveframework.flags.ignore_route_conflict_validation = False + + xhiveframework.reload_doctype("Client Script", force=True) diff --git a/xhiveframework/patches/v13_0/rename_desk_page_to_workspace.py b/xhiveframework/patches/v13_0/rename_desk_page_to_workspace.py new file mode 100644 index 0000000..889cf23 --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_desk_page_to_workspace.py @@ -0,0 +1,22 @@ +import xhiveframework +from xhiveframework.model.rename_doc import rename_doc + + +def execute(): + if xhiveframework.db.exists("DocType", "Desk Page"): + if xhiveframework.db.exists("DocType", "Workspace"): + # this patch was not added initially, so this page might still exist + xhiveframework.delete_doc("DocType", "Desk Page") + else: + xhiveframework.flags.ignore_route_conflict_validation = True + rename_doc("DocType", "Desk Page", "Workspace") + xhiveframework.flags.ignore_route_conflict_validation = False + + rename_doc("DocType", "Desk Chart", "Workspace Chart", ignore_if_exists=True) + rename_doc("DocType", "Desk Shortcut", "Workspace Shortcut", ignore_if_exists=True) + rename_doc("DocType", "Desk Link", "Workspace Link", ignore_if_exists=True) + + xhiveframework.reload_doc("desk", "doctype", "workspace", force=True) + xhiveframework.reload_doc("desk", "doctype", "workspace_link", force=True) + xhiveframework.reload_doc("desk", "doctype", "workspace_chart", force=True) + xhiveframework.reload_doc("desk", "doctype", "workspace_shortcut", force=True) diff --git a/xhiveframework/patches/v13_0/rename_is_custom_field_in_dashboard_chart.py b/xhiveframework/patches/v13_0/rename_is_custom_field_in_dashboard_chart.py new file mode 100644 index 0000000..1faf120 --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_is_custom_field_in_dashboard_chart.py @@ -0,0 +1,12 @@ +import xhiveframework +from xhiveframework.model.utils.rename_field import rename_field + + +def execute(): + if not xhiveframework.db.table_exists("Dashboard Chart"): + return + + xhiveframework.reload_doc("desk", "doctype", "dashboard_chart") + + if xhiveframework.db.has_column("Dashboard Chart", "is_custom"): + rename_field("Dashboard Chart", "is_custom", "use_report_chart") diff --git a/xhiveframework/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/xhiveframework/patches/v13_0/rename_list_view_setting_to_list_view_settings.py new file mode 100644 index 0000000..253d8fb --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -0,0 +1,29 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + if not xhiveframework.db.table_exists("List View Setting"): + return + if not xhiveframework.db.exists("DocType", "List View Setting"): + return + + xhiveframework.reload_doc("desk", "doctype", "List View Settings") + + existing_list_view_settings = xhiveframework.get_all("List View Settings", as_list=True, order_by="modified") + for list_view_setting in xhiveframework.get_all( + "List View Setting", + fields=["disable_count", "disable_sidebar_stats", "disable_auto_refresh", "name"], + order_by="modified", + ): + name = list_view_setting.pop("name") + if name not in [x[0] for x in existing_list_view_settings]: + list_view_setting["doctype"] = "List View Settings" + list_view_settings = xhiveframework.get_doc(list_view_setting) + # setting name here is necessary because autoname is set as prompt + list_view_settings.name = name + list_view_settings.insert() + + xhiveframework.delete_doc("DocType", "List View Setting", force=True) diff --git a/xhiveframework/patches/v13_0/rename_notification_fields.py b/xhiveframework/patches/v13_0/rename_notification_fields.py new file mode 100644 index 0000000..1c52efb --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_notification_fields.py @@ -0,0 +1,16 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.utils.rename_field import rename_field + + +def execute(): + """ + Change notification recipient fields from email to receiver fields + """ + xhiveframework.reload_doc("Email", "doctype", "Notification Recipient") + xhiveframework.reload_doc("Email", "doctype", "Notification") + + rename_field("Notification Recipient", "email_by_document_field", "receiver_by_document_field") + rename_field("Notification Recipient", "email_by_role", "receiver_by_role") diff --git a/xhiveframework/patches/v13_0/rename_onboarding.py b/xhiveframework/patches/v13_0/rename_onboarding.py new file mode 100644 index 0000000..15c0fbe --- /dev/null +++ b/xhiveframework/patches/v13_0/rename_onboarding.py @@ -0,0 +1,9 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + if xhiveframework.db.exists("DocType", "Onboarding"): + xhiveframework.rename_doc("DocType", "Onboarding", "Module Onboarding", ignore_if_exists=True) diff --git a/xhiveframework/patches/v13_0/replace_field_target_with_open_in_new_tab.py b/xhiveframework/patches/v13_0/replace_field_target_with_open_in_new_tab.py new file mode 100644 index 0000000..78615d4 --- /dev/null +++ b/xhiveframework/patches/v13_0/replace_field_target_with_open_in_new_tab.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + doctype = "Top Bar Item" + if not xhiveframework.db.table_exists(doctype) or not xhiveframework.db.has_column(doctype, "target"): + return + + xhiveframework.reload_doc("website", "doctype", "top_bar_item") + xhiveframework.db.set_value(doctype, {"target": 'target = "_blank"'}, "open_in_new_tab", 1) diff --git a/xhiveframework/patches/v13_0/replace_old_data_import.py b/xhiveframework/patches/v13_0/replace_old_data_import.py new file mode 100644 index 0000000..0257131 --- /dev/null +++ b/xhiveframework/patches/v13_0/replace_old_data_import.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + if not xhiveframework.db.table_exists("Data Import"): + return + + meta = xhiveframework.get_meta("Data Import") + # if Data Import is the new one, return early + if meta.fields[1].fieldname == "import_type": + return + + xhiveframework.db.sql("DROP TABLE IF EXISTS `tabData Import Legacy`") + xhiveframework.rename_doc("DocType", "Data Import", "Data Import Legacy") + xhiveframework.db.commit() + xhiveframework.db.sql("DROP TABLE IF EXISTS `tabData Import`") + xhiveframework.rename_doc("DocType", "Data Import Beta", "Data Import") diff --git a/xhiveframework/patches/v13_0/reset_corrupt_defaults.py b/xhiveframework/patches/v13_0/reset_corrupt_defaults.py new file mode 100644 index 0000000..0232a81 --- /dev/null +++ b/xhiveframework/patches/v13_0/reset_corrupt_defaults.py @@ -0,0 +1,33 @@ +import xhiveframework +from xhiveframework.patches.v13_0.encrypt_2fa_secrets import DOCTYPE +from xhiveframework.patches.v13_0.encrypt_2fa_secrets import PARENT_FOR_DEFAULTS as TWOFACTOR_PARENT +from xhiveframework.utils import cint + + +def execute(): + """ + This patch is needed to fix parent incorrectly set as `__2fa` because of + https://lab.membtech.com/xhiveframework/xhiveframework15/commit/a822092211533ff17ff9b92dd86f6f868ed63e2e + """ + + if not xhiveframework.db.get_value( + DOCTYPE, {"parent": TWOFACTOR_PARENT, "defkey": ("not like", "%_otp%")}, "defkey" + ): + return + + # system settings + system_settings = xhiveframework.get_single("System Settings") + system_settings.set_defaults() + + # home page + xhiveframework.db.set_default( + "desktop:home_page", "workspace" if cint(system_settings.setup_complete) else "setup-wizard" + ) + + # letter head + try: + letter_head = xhiveframework.get_doc("Letter Head", {"is_default": 1}) + letter_head.set_as_default() + + except xhiveframework.DoesNotExistError: + pass diff --git a/xhiveframework/patches/v13_0/set_existing_dashboard_charts_as_public.py b/xhiveframework/patches/v13_0/set_existing_dashboard_charts_as_public.py new file mode 100644 index 0000000..c56b341 --- /dev/null +++ b/xhiveframework/patches/v13_0/set_existing_dashboard_charts_as_public.py @@ -0,0 +1,21 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("desk", "doctype", "dashboard_chart") + + if not xhiveframework.db.table_exists("Dashboard Chart"): + return + + users_with_permission = xhiveframework.get_all( + "Has Role", + fields=["parent"], + filters={"role": ["in", ["System Manager", "Dashboard Manager"]], "parenttype": "User"}, + distinct=True, + ) + + users = [item.parent for item in users_with_permission] + charts = xhiveframework.get_all("Dashboard Chart", filters={"owner": ["in", users]}) + + for chart in charts: + xhiveframework.db.set_value("Dashboard Chart", chart.name, "is_public", 1) diff --git a/xhiveframework/patches/v13_0/set_first_day_of_the_week.py b/xhiveframework/patches/v13_0/set_first_day_of_the_week.py new file mode 100644 index 0000000..97b93b2 --- /dev/null +++ b/xhiveframework/patches/v13_0/set_first_day_of_the_week.py @@ -0,0 +1,8 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("System Settings") + # setting first_day_of_the_week value as "Monday" to avoid breaking change + # because before the configuration was introduced, system used to consider "Monday" as start of the week + xhiveframework.db.set_single_value("System Settings", "first_day_of_the_week", "Monday") diff --git a/xhiveframework/patches/v13_0/set_path_for_homepage_in_web_page_view.py b/xhiveframework/patches/v13_0/set_path_for_homepage_in_web_page_view.py new file mode 100644 index 0000000..6fe21ad --- /dev/null +++ b/xhiveframework/patches/v13_0/set_path_for_homepage_in_web_page_view.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "web_page_view", force=True) + xhiveframework.db.sql("""UPDATE `tabWeb Page View` set path='/' where path=''""") diff --git a/xhiveframework/patches/v13_0/set_read_times.py b/xhiveframework/patches/v13_0/set_read_times.py new file mode 100644 index 0000000..ca4a3df --- /dev/null +++ b/xhiveframework/patches/v13_0/set_read_times.py @@ -0,0 +1,21 @@ +from math import ceil + +import xhiveframework +from xhiveframework.utils import markdown, strip_html_tags + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "blog_post") + + for blog in xhiveframework.get_all("Blog Post"): + blog = xhiveframework.get_doc("Blog Post", blog.name) + xhiveframework.db.set_value("Blog Post", blog.name, "read_time", get_read_time(blog), update_modified=False) + + +def get_read_time(blog): + content = blog.content or blog.content_html + if blog.content_type == "Markdown": + content = markdown(blog.content_md) + + total_words = len(strip_html_tags(content or "").split()) + return ceil(total_words / 250) diff --git a/xhiveframework/patches/v13_0/set_route_for_blog_category.py b/xhiveframework/patches/v13_0/set_route_for_blog_category.py new file mode 100644 index 0000000..d72ca2c --- /dev/null +++ b/xhiveframework/patches/v13_0/set_route_for_blog_category.py @@ -0,0 +1,9 @@ +import xhiveframework + + +def execute(): + categories = xhiveframework.get_list("Blog Category") + for category in categories: + doc = xhiveframework.get_doc("Blog Category", category["name"]) + doc.set_route() + doc.save() diff --git a/xhiveframework/patches/v13_0/set_social_icons.py b/xhiveframework/patches/v13_0/set_social_icons.py new file mode 100644 index 0000000..0532c45 --- /dev/null +++ b/xhiveframework/patches/v13_0/set_social_icons.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + providers = xhiveframework.get_all("Social Login Key") + + for provider in providers: + doc = xhiveframework.get_doc("Social Login Key", provider) + doc.set_icon() + doc.save() diff --git a/xhiveframework/patches/v13_0/set_unique_for_page_view.py b/xhiveframework/patches/v13_0/set_unique_for_page_view.py new file mode 100644 index 0000000..6e741de --- /dev/null +++ b/xhiveframework/patches/v13_0/set_unique_for_page_view.py @@ -0,0 +1,7 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "web_page_view", force=True) + site_url = xhiveframework.utils.get_site_url(xhiveframework.local.site) + xhiveframework.db.sql(f"""UPDATE `tabWeb Page View` set is_unique=1 where referrer LIKE '%{site_url}%'""") diff --git a/xhiveframework/patches/v13_0/site_wise_logging.py b/xhiveframework/patches/v13_0/site_wise_logging.py new file mode 100644 index 0000000..d4d7ef2 --- /dev/null +++ b/xhiveframework/patches/v13_0/site_wise_logging.py @@ -0,0 +1,11 @@ +import os + +import xhiveframework + + +def execute(): + site = xhiveframework.local.site + + log_folder = os.path.join(site, "logs") + if not os.path.exists(log_folder): + os.mkdir(log_folder) diff --git a/xhiveframework/patches/v13_0/update_date_filters_in_user_settings.py b/xhiveframework/patches/v13_0/update_date_filters_in_user_settings.py new file mode 100644 index 0000000..db476eb --- /dev/null +++ b/xhiveframework/patches/v13_0/update_date_filters_in_user_settings.py @@ -0,0 +1,54 @@ +import json + +import xhiveframework +from xhiveframework.model.utils.user_settings import sync_user_settings, update_user_settings + + +def execute(): + users = xhiveframework.db.sql("select distinct(user) from `__UserSettings`", as_dict=True) + + for user in users: + user_settings = xhiveframework.db.sql( + f""" + select + * from `__UserSettings` + where + user='{user.user}' + """, + as_dict=True, + ) + + for setting in user_settings: + data = xhiveframework.parse_json(setting.get("data")) + if data: + for key in data: + update_user_setting_filters(data, key, setting) + + sync_user_settings() + + +def update_user_setting_filters(data, key, user_setting): + timespan_map = { + "1 week": "week", + "1 month": "month", + "3 months": "quarter", + "6 months": "6 months", + "1 year": "year", + } + + period_map = {"Previous": "last", "Next": "next"} + + if data.get(key): + update = False + if isinstance(data.get(key), dict): + filters = data.get(key).get("filters") + if filters and isinstance(filters, list): + for f in filters: + if f[2] == "Next" or f[2] == "Previous": + update = True + f[3] = period_map[f[2]] + " " + timespan_map[f[3]] + f[2] = "Timespan" + + if update: + data[key]["filters"] = filters + update_user_settings(user_setting["doctype"], json.dumps(data), for_update=True) diff --git a/xhiveframework/patches/v13_0/update_duration_options.py b/xhiveframework/patches/v13_0/update_duration_options.py new file mode 100644 index 0000000..fc39a09 --- /dev/null +++ b/xhiveframework/patches/v13_0/update_duration_options.py @@ -0,0 +1,32 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("core", "doctype", "DocField") + + if xhiveframework.db.has_column("DocField", "show_days"): + xhiveframework.db.sql( + """ + UPDATE + tabDocField + SET + hide_days = 1 WHERE show_days = 0 + """ + ) + xhiveframework.db.sql_ddl("alter table tabDocField drop column show_days") + + if xhiveframework.db.has_column("DocField", "show_seconds"): + xhiveframework.db.sql( + """ + UPDATE + tabDocField + SET + hide_seconds = 1 WHERE show_seconds = 0 + """ + ) + xhiveframework.db.sql_ddl("alter table tabDocField drop column show_seconds") + + xhiveframework.clear_cache(doctype="DocField") diff --git a/xhiveframework/patches/v13_0/update_icons_in_customized_desk_pages.py b/xhiveframework/patches/v13_0/update_icons_in_customized_desk_pages.py new file mode 100644 index 0000000..5522189 --- /dev/null +++ b/xhiveframework/patches/v13_0/update_icons_in_customized_desk_pages.py @@ -0,0 +1,18 @@ +import xhiveframework + + +def execute(): + if not xhiveframework.db.exists("Desk Page"): + return + + pages = xhiveframework.get_all( + "Desk Page", filters={"is_standard": False}, fields=["name", "extends", "for_user"] + ) + default_icon = {} + for page in pages: + if page.extends and page.for_user: + if not default_icon.get(page.extends): + default_icon[page.extends] = xhiveframework.db.get_value("Desk Page", page.extends, "icon") + + icon = default_icon.get(page.extends) + xhiveframework.db.set_value("Desk Page", page.name, "icon", icon) diff --git a/xhiveframework/patches/v13_0/update_newsletter_content_type.py b/xhiveframework/patches/v13_0/update_newsletter_content_type.py new file mode 100644 index 0000000..64784e6 --- /dev/null +++ b/xhiveframework/patches/v13_0/update_newsletter_content_type.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("email", "doctype", "Newsletter") + xhiveframework.db.sql( + """ + UPDATE tabNewsletter + SET content_type = 'Rich Text' + """ + ) diff --git a/xhiveframework/patches/v13_0/update_notification_channel_if_empty.py b/xhiveframework/patches/v13_0/update_notification_channel_if_empty.py new file mode 100644 index 0000000..9c778ac --- /dev/null +++ b/xhiveframework/patches/v13_0/update_notification_channel_if_empty.py @@ -0,0 +1,14 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("Email", "doctype", "Notification") + + notifications = xhiveframework.get_all("Notification", {"is_standard": 1}, {"name", "channel"}) + for notification in notifications: + if not notification.channel: + xhiveframework.db.set_value("Notification", notification.name, "channel", "Email", update_modified=False) + xhiveframework.db.commit() diff --git a/xhiveframework/patches/v13_0/web_template_set_module.py b/xhiveframework/patches/v13_0/web_template_set_module.py new file mode 100644 index 0000000..d77c585 --- /dev/null +++ b/xhiveframework/patches/v13_0/web_template_set_module.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework + + +def execute(): + """Set default module for standard Web Template, if none.""" + xhiveframework.reload_doc("website", "doctype", "Web Template Field") + xhiveframework.reload_doc("website", "doctype", "web_template") + + standard_templates = xhiveframework.get_list("Web Template", {"standard": 1}) + for template in standard_templates: + doc = xhiveframework.get_doc("Web Template", template.name) + if not doc.module: + doc.module = "Website" + doc.save() diff --git a/xhiveframework/patches/v13_0/website_theme_custom_scss.py b/xhiveframework/patches/v13_0/website_theme_custom_scss.py new file mode 100644 index 0000000..a837843 --- /dev/null +++ b/xhiveframework/patches/v13_0/website_theme_custom_scss.py @@ -0,0 +1,38 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "website_theme_ignore_app") + xhiveframework.reload_doc("website", "doctype", "color") + xhiveframework.reload_doc("website", "doctype", "website_theme", force=True) + + for theme in xhiveframework.get_all("Website Theme"): + doc = xhiveframework.get_doc("Website Theme", theme.name) + setup_color_record(doc) + if not doc.get("custom_scss") and doc.theme_scss: + # move old theme to new theme + doc.custom_scss = doc.theme_scss + doc.save() + + +def setup_color_record(doc): + color_fields = [ + "primary_color", + "text_color", + "light_color", + "dark_color", + "background_color", + ] + + for color_field in color_fields: + color_code = doc.get(color_field) + if not color_code or xhiveframework.db.exists("Color", color_code): + continue + + xhiveframework.get_doc( + { + "doctype": "Color", + "__newname": color_code, + "color": color_code, + } + ).insert() diff --git a/xhiveframework/patches/v14_0/__init__.py b/xhiveframework/patches/v14_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/patches/v14_0/clear_long_pending_stale_logs.py b/xhiveframework/patches/v14_0/clear_long_pending_stale_logs.py new file mode 100644 index 0000000..6147a17 --- /dev/null +++ b/xhiveframework/patches/v14_0/clear_long_pending_stale_logs.py @@ -0,0 +1,40 @@ +import xhiveframework +from xhiveframework.core.doctype.log_settings.log_settings import clear_log_table +from xhiveframework.utils import add_to_date, today + + +def execute(): + """Due to large size of log tables on old sites some table cleanups never finished during daily log clean up. This patch discards such data by using "big delete" code. + + ref: https://lab.membtech.com/xhiveframework/xhiveframework15/issues/16971 + """ + + DOCTYPE_RETENTION_MAP = { + "Error Log": get_current_setting("clear_error_log_after") or 90, + "Activity Log": get_current_setting("clear_activity_log_after") or 90, + "Email Queue": get_current_setting("clear_email_queue_after") or 30, + # child table on email queue + "Email Queue Recipient": get_current_setting("clear_email_queue_after") or 30, + # newly added + "Scheduled Job Log": 90, + } + + for doctype, retention in DOCTYPE_RETENTION_MAP.items(): + if is_log_cleanup_stuck(doctype, retention): + print(f"Clearing old {doctype} records") + clear_log_table(doctype, retention) + + +def is_log_cleanup_stuck(doctype: str, retention: int) -> bool: + """Check if doctype has data significantly older than configured cleanup period""" + threshold = add_to_date(today(), days=retention * -2) + + return bool(xhiveframework.db.exists(doctype, {"modified": ("<", threshold)})) + + +def get_current_setting(fieldname): + try: + return xhiveframework.db.get_single_value("Log Settings", fieldname) + except Exception: + # Field might be gone if patch is reattempted + pass diff --git a/xhiveframework/patches/v14_0/copy_mail_data.py b/xhiveframework/patches/v14_0/copy_mail_data.py new file mode 100644 index 0000000..0d706c8 --- /dev/null +++ b/xhiveframework/patches/v14_0/copy_mail_data.py @@ -0,0 +1,23 @@ +import xhiveframework + + +def execute(): + # patch for all Email Account with the flag use_imap + for email_account in xhiveframework.get_list("Email Account", filters={"enable_incoming": 1, "use_imap": 1}): + # get all data from Email Account + doc = xhiveframework.get_doc("Email Account", email_account.name) + + imap_list = [folder.folder_name for folder in doc.imap_folder] + # and append the old data to the child table + if doc.uidvalidity or doc.uidnext and "INBOX" not in imap_list: + doc.append( + "imap_folder", + { + "folder_name": "INBOX", + "append_to": doc.append_to, + "uid_validity": doc.uidvalidity, + "uidnext": doc.uidnext, + }, + ) + + doc.save() diff --git a/xhiveframework/patches/v14_0/delete_data_migration_tool.py b/xhiveframework/patches/v14_0/delete_data_migration_tool.py new file mode 100644 index 0000000..0ec6560 --- /dev/null +++ b/xhiveframework/patches/v14_0/delete_data_migration_tool.py @@ -0,0 +1,12 @@ +# Copyright (c) 2022, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import xhiveframework + + +def execute(): + doctypes = xhiveframework.get_all("DocType", {"module": "Data Migration", "custom": 0}, pluck="name") + for doctype in doctypes: + xhiveframework.delete_doc("DocType", doctype, ignore_missing=True) + + xhiveframework.delete_doc("Module Def", "Data Migration", ignore_missing=True, force=True) diff --git a/xhiveframework/patches/v14_0/delete_payment_gateways.py b/xhiveframework/patches/v14_0/delete_payment_gateways.py new file mode 100644 index 0000000..ddb5d1d --- /dev/null +++ b/xhiveframework/patches/v14_0/delete_payment_gateways.py @@ -0,0 +1,16 @@ +import xhiveframework + + +def execute(): + if "payments" in xhiveframework.get_installed_apps(): + return + + for doctype in ( + "Payment Gateway", + "Razorpay Settings", + "Braintree Settings", + "PayPal Settings", + "Paytm Settings", + "Stripe Settings", + ): + xhiveframework.delete_doc_if_exists("DocType", doctype, force=True) diff --git a/xhiveframework/patches/v14_0/different_encryption_key.py b/xhiveframework/patches/v14_0/different_encryption_key.py new file mode 100644 index 0000000..4739767 --- /dev/null +++ b/xhiveframework/patches/v14_0/different_encryption_key.py @@ -0,0 +1,16 @@ +import pathlib + +import xhiveframework +from xhiveframework.installer import update_site_config +from xhiveframework.utils.backups import BACKUP_ENCRYPTION_CONFIG_KEY, get_backup_path + + +def execute(): + if xhiveframework.conf.get(BACKUP_ENCRYPTION_CONFIG_KEY): + return + + backup_path = pathlib.Path(get_backup_path()) + encrypted_backups_present = bool(list(backup_path.glob("*-enc*"))) + + if encrypted_backups_present: + update_site_config(BACKUP_ENCRYPTION_CONFIG_KEY, xhiveframework.local.conf.encryption_key) diff --git a/xhiveframework/patches/v14_0/disable_email_accounts_with_oauth.py b/xhiveframework/patches/v14_0/disable_email_accounts_with_oauth.py new file mode 100644 index 0000000..d9cda89 --- /dev/null +++ b/xhiveframework/patches/v14_0/disable_email_accounts_with_oauth.py @@ -0,0 +1,36 @@ +import xhiveframework +from xhiveframework.desk.doctype.notification_log.notification_log import make_notification_logs + + +def execute(): + if xhiveframework.get_all("Email Account", {"auth_method": "OAuth", "connected_user": ["is", "set"]}, limit=1): + return + + # Setting awaiting password to 1 for email accounts where Oauth is enabled. + # This is done so that people can resetup their email accounts with connected app mechanism. + xhiveframework.db.set_value("Email Account", {"auth_method": "OAuth"}, "awaiting_password", 1) + + message = "Email Accounts with auth method as OAuth have been disabled.\ + Please re-setup your OAuth based email accounts with the connected app mechanism to re-enable them." + + if sysmanagers := get_system_managers(): + make_notification_logs( + { + "type": "Alert", + "subject": xhiveframework._(message), + }, + sysmanagers, + ) + + +def get_system_managers(): + user_doctype = xhiveframework.qb.DocType("User").as_("user") + user_role_doctype = xhiveframework.qb.DocType("Has Role").as_("user_role") + return ( + xhiveframework.qb.from_(user_doctype) + .from_(user_role_doctype) + .select(user_doctype.email) + .where(user_role_doctype.role == "System Manager") + .where(user_doctype.enabled == 1) + .where(user_role_doctype.parent == user_doctype.name) + ).run(pluck=True) diff --git a/xhiveframework/patches/v14_0/drop_data_import_legacy.py b/xhiveframework/patches/v14_0/drop_data_import_legacy.py new file mode 100644 index 0000000..99e1aa2 --- /dev/null +++ b/xhiveframework/patches/v14_0/drop_data_import_legacy.py @@ -0,0 +1,23 @@ +import click + +import xhiveframework + + +def execute(): + doctype = "Data Import Legacy" + table = xhiveframework.utils.get_table_name(doctype) + + # delete the doctype record to avoid broken links + xhiveframework.delete_doc("DocType", doctype, force=True) + + # leaving table in database for manual cleanup + click.secho( + f"`{doctype}` has been deprecated. The DocType is deleted, but the data still" + " exists on the database. If this data is worth recovering, you may export it" + f" using\n\n\tbench --site {xhiveframework.local.site} backup -i '{doctype}'\n\nAfter" + " this, the table will continue to persist in the database, until you choose" + " to remove it yourself. If you want to drop the table, you may run\n\n\tbench" + f" --site {xhiveframework.local.site} execute xhiveframework.db.sql --args \"('DROP TABLE IF" + f" EXISTS `{table}`', )\"\n", + fg="yellow", + ) diff --git a/xhiveframework/patches/v14_0/drop_unused_indexes.py b/xhiveframework/patches/v14_0/drop_unused_indexes.py new file mode 100644 index 0000000..5c3d8f6 --- /dev/null +++ b/xhiveframework/patches/v14_0/drop_unused_indexes.py @@ -0,0 +1,56 @@ +""" +This patch just drops some known indexes which aren't being used anymore or never were used. +""" + +import click + +import xhiveframework + +UNUSED_INDEXES = [ + ("Comment", ["link_doctype", "link_name"]), + ("Activity Log", ["link_doctype", "link_name"]), +] + + +def execute(): + if xhiveframework.db.db_type == "postgres": + return + + db_tables = xhiveframework.db.get_tables(cached=False) + + # All parent indexes + parent_doctypes = xhiveframework.get_all( + "DocType", + {"istable": 0, "is_virtual": 0, "issingle": 0}, + pluck="name", + ) + db_tables = xhiveframework.db.get_tables(cached=False) + + for doctype in parent_doctypes: + table = f"tab{doctype}" + if table not in db_tables: + continue + drop_index_if_exists(table, "parent") + + # Unused composite indexes + for doctype, index_fields in UNUSED_INDEXES: + table = f"tab{doctype}" + index_name = xhiveframework.db.get_index_name(index_fields) + if table not in db_tables: + continue + drop_index_if_exists(table, index_name) + + +def drop_index_if_exists(table: str, index: str): + if not xhiveframework.db.has_index(table, index): + click.echo(f"- Skipped {index} index for {table} because it doesn't exist") + return + + try: + xhiveframework.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`") + except Exception as e: + xhiveframework.log_error("Failed to drop index") + click.secho(f"x Failed to drop index {index} from {table}\n {e!s}", fg="red") + return + + click.echo(f"✓ dropped {index} index from {table}") diff --git a/xhiveframework/patches/v14_0/log_settings_migration.py b/xhiveframework/patches/v14_0/log_settings_migration.py new file mode 100644 index 0000000..280eb7e --- /dev/null +++ b/xhiveframework/patches/v14_0/log_settings_migration.py @@ -0,0 +1,29 @@ +import xhiveframework + + +def execute(): + old_settings = { + "Error Log": get_current_setting("clear_error_log_after"), + "Activity Log": get_current_setting("clear_activity_log_after"), + "Email Queue": get_current_setting("clear_email_queue_after"), + } + + xhiveframework.reload_doc("core", "doctype", "Logs To Clear") + xhiveframework.reload_doc("core", "doctype", "Log Settings") + + log_settings = xhiveframework.get_doc("Log Settings") + log_settings.add_default_logtypes() + + for doctype, retention in old_settings.items(): + if retention: + log_settings.register_doctype(doctype, retention) + + log_settings.save() + + +def get_current_setting(fieldname): + try: + return xhiveframework.db.get_single_value("Log Settings", fieldname) + except Exception: + # Field might be gone if patch is reattempted + pass diff --git a/xhiveframework/patches/v14_0/modify_value_column_size_for_singles.py b/xhiveframework/patches/v14_0/modify_value_column_size_for_singles.py new file mode 100644 index 0000000..f018d86 --- /dev/null +++ b/xhiveframework/patches/v14_0/modify_value_column_size_for_singles.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + if xhiveframework.db.db_type == "mariadb": + xhiveframework.db.sql_ddl("alter table `tabSingles` modify column `value` longtext") diff --git a/xhiveframework/patches/v14_0/remove_db_aggregation.py b/xhiveframework/patches/v14_0/remove_db_aggregation.py new file mode 100644 index 0000000..bb0c6b8 --- /dev/null +++ b/xhiveframework/patches/v14_0/remove_db_aggregation.py @@ -0,0 +1,35 @@ +import re + +import xhiveframework +from xhiveframework.query_builder import DocType + + +def execute(): + """Replace temporarily available Database Aggregate APIs on xhiveframework (develop) + + APIs changed: + * xhiveframework.db.max => xhiveframework.qb.max + * xhiveframework.db.min => xhiveframework.qb.min + * xhiveframework.db.sum => xhiveframework.qb.sum + * xhiveframework.db.avg => xhiveframework.qb.avg + """ + ServerScript = DocType("Server Script") + server_scripts = ( + xhiveframework.qb.from_(ServerScript) + .where( + ServerScript.script.like("%xhiveframework.db.max(%") + | ServerScript.script.like("%xhiveframework.db.min(%") + | ServerScript.script.like("%xhiveframework.db.sum(%") + | ServerScript.script.like("%xhiveframework.db.avg(%") + ) + .select("name", "script") + .run(as_dict=True) + ) + + for server_script in server_scripts: + name, script = server_script["name"], server_script["script"] + + for agg in ["avg", "max", "min", "sum"]: + script = re.sub(f"xhiveframework.db.{agg}\\(", f"xhiveframework.qb.{agg}(", script) + + xhiveframework.db.set_value("Server Script", name, "script", script) diff --git a/xhiveframework/patches/v14_0/remove_is_first_startup.py b/xhiveframework/patches/v14_0/remove_is_first_startup.py new file mode 100644 index 0000000..1e7b656 --- /dev/null +++ b/xhiveframework/patches/v14_0/remove_is_first_startup.py @@ -0,0 +1,8 @@ +import xhiveframework + + +def execute(): + singles = xhiveframework.qb.Table("tabSingles") + xhiveframework.qb.from_(singles).delete().where( + (singles.doctype == "System Settings") & (singles.field == "is_first_startup") + ).run() diff --git a/xhiveframework/patches/v14_0/remove_manage_subscriptions_from_navbar.py b/xhiveframework/patches/v14_0/remove_manage_subscriptions_from_navbar.py new file mode 100644 index 0000000..94b5504 --- /dev/null +++ b/xhiveframework/patches/v14_0/remove_manage_subscriptions_from_navbar.py @@ -0,0 +1,10 @@ +import xhiveframework + + +def execute(): + navbar_settings = xhiveframework.get_single("Navbar Settings") + for i, l in enumerate(navbar_settings.settings_dropdown): + if l.item_label == "Manage Subscriptions": + navbar_settings.settings_dropdown.pop(i) + navbar_settings.save() + break diff --git a/xhiveframework/patches/v14_0/remove_post_and_post_comment.py b/xhiveframework/patches/v14_0/remove_post_and_post_comment.py new file mode 100644 index 0000000..3ab1f1e --- /dev/null +++ b/xhiveframework/patches/v14_0/remove_post_and_post_comment.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + xhiveframework.delete_doc_if_exists("DocType", "Post") + xhiveframework.delete_doc_if_exists("DocType", "Post Comment") diff --git a/xhiveframework/patches/v14_0/reset_creation_datetime.py b/xhiveframework/patches/v14_0/reset_creation_datetime.py new file mode 100644 index 0000000..dc8220b --- /dev/null +++ b/xhiveframework/patches/v14_0/reset_creation_datetime.py @@ -0,0 +1,38 @@ +import glob +import json +import os + +import xhiveframework +from xhiveframework.query_builder import DocType as _DocType + + +def execute(): + """Resetting creation datetimes for DocTypes""" + DocType = _DocType("DocType") + doctype_jsons = glob.glob(os.path.join("..", "apps", "xhiveframework", "xhiveframework", "**", "doctype", "**", "*.json")) + + xhiveframework_modules = xhiveframework.get_all("Module Def", filters={"app_name": "xhiveframework"}, pluck="name") + site_doctypes = xhiveframework.get_all( + "DocType", + filters={"module": ("in", xhiveframework_modules), "custom": False}, + fields=["name", "creation"], + ) + + for dt_path in doctype_jsons: + with open(dt_path) as f: + try: + file_schema = xhiveframework._dict(json.load(f)) + except Exception: + continue + + if not file_schema.name: + continue + + _site_schema = [x for x in site_doctypes if x.name == file_schema.name] + if not _site_schema: + continue + + if file_schema.creation != _site_schema[0].creation: + xhiveframework.qb.update(DocType).set(DocType.creation, file_schema.creation).where( + DocType.name == file_schema.name + ).run() diff --git a/xhiveframework/patches/v14_0/save_ratings_in_fraction.py b/xhiveframework/patches/v14_0/save_ratings_in_fraction.py new file mode 100644 index 0000000..59b45f4 --- /dev/null +++ b/xhiveframework/patches/v14_0/save_ratings_in_fraction.py @@ -0,0 +1,37 @@ +import xhiveframework +from xhiveframework.query_builder import DocType + + +def execute(): + RATING_FIELD_TYPE = "decimal(3,2)" + rating_fields = xhiveframework.get_all( + "DocField", fields=["parent", "fieldname"], filters={"fieldtype": "Rating"} + ) + + custom_rating_fields = xhiveframework.get_all( + "Custom Field", fields=["dt", "fieldname"], filters={"fieldtype": "Rating"} + ) + + for _field in rating_fields + custom_rating_fields: + doctype_name = _field.get("parent") or _field.get("dt") + doctype = DocType(doctype_name) + field = _field.fieldname + + # TODO: Add postgres support (for the check) + if ( + xhiveframework.conf.db_type == "mariadb" + and xhiveframework.db.get_column_type(doctype_name, field) == RATING_FIELD_TYPE + ): + continue + + # commit any changes so far for upcoming DDL + xhiveframework.db.commit() + + # alter column types for rating fieldtype + xhiveframework.db.change_column_type(doctype_name, column=field, type=RATING_FIELD_TYPE, nullable=True) + + # update data: int => decimal + xhiveframework.qb.update(doctype).set(doctype[field], doctype[field] / 5).run() + + # commit to flush updated rows + xhiveframework.db.commit() diff --git a/xhiveframework/patches/v14_0/set_document_expiry_default.py b/xhiveframework/patches/v14_0/set_document_expiry_default.py new file mode 100644 index 0000000..55eff85 --- /dev/null +++ b/xhiveframework/patches/v14_0/set_document_expiry_default.py @@ -0,0 +1,8 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.set_single_value( + "System Settings", + {"document_share_key_expiry": 30, "allow_older_web_view_links": 1}, + ) diff --git a/xhiveframework/patches/v14_0/set_suspend_email_queue_default.py b/xhiveframework/patches/v14_0/set_suspend_email_queue_default.py new file mode 100644 index 0000000..e6bfc58 --- /dev/null +++ b/xhiveframework/patches/v14_0/set_suspend_email_queue_default.py @@ -0,0 +1,13 @@ +import xhiveframework +from xhiveframework.cache_manager import clear_defaults_cache + + +def execute(): + xhiveframework.db.set_default( + "suspend_email_queue", + xhiveframework.db.get_default("hold_queue", "Administrator") or 0, + parent="__default", + ) + + xhiveframework.db.delete("DefaultValue", {"defkey": "hold_queue"}) + clear_defaults_cache() diff --git a/xhiveframework/patches/v14_0/setup_likes_from_feedback.py b/xhiveframework/patches/v14_0/setup_likes_from_feedback.py new file mode 100644 index 0000000..28ab602 --- /dev/null +++ b/xhiveframework/patches/v14_0/setup_likes_from_feedback.py @@ -0,0 +1,32 @@ +import xhiveframework + + +def execute(): + xhiveframework.reload_doctype("Comment") + + if xhiveframework.db.count("Feedback") > 20000: + xhiveframework.db.auto_commit_on_many_writes = True + + for feedback in xhiveframework.get_all("Feedback", fields=["*"]): + if feedback.like: + new_comment = xhiveframework.new_doc("Comment") + new_comment.comment_type = "Like" + new_comment.comment_email = feedback.owner + new_comment.content = "Liked by: " + feedback.owner + new_comment.reference_doctype = feedback.reference_doctype + new_comment.reference_name = feedback.reference_name + new_comment.creation = feedback.creation + new_comment.modified = feedback.modified + new_comment.owner = feedback.owner + new_comment.modified_by = feedback.modified_by + new_comment.ip_address = feedback.ip_address + new_comment.db_insert() + + if xhiveframework.db.auto_commit_on_many_writes: + xhiveframework.db.auto_commit_on_many_writes = False + + # clean up + xhiveframework.db.delete("Feedback") + xhiveframework.db.commit() + + xhiveframework.delete_doc("DocType", "Feedback") diff --git a/xhiveframework/patches/v14_0/transform_todo_schema.py b/xhiveframework/patches/v14_0/transform_todo_schema.py new file mode 100644 index 0000000..f06ac7a --- /dev/null +++ b/xhiveframework/patches/v14_0/transform_todo_schema.py @@ -0,0 +1,12 @@ +import xhiveframework +from xhiveframework.query_builder.utils import DocType + + +def execute(): + # Email Template & Help Article have owner field that doesn't have any additional functionality + # Only ToDo has to be updated. + + ToDo = DocType("ToDo") + xhiveframework.reload_doctype("ToDo", force=True) + + xhiveframework.qb.update(ToDo).set(ToDo.allocated_to, ToDo.owner).run() diff --git a/xhiveframework/patches/v14_0/update_attachment_comment.py b/xhiveframework/patches/v14_0/update_attachment_comment.py new file mode 100644 index 0000000..f76a695 --- /dev/null +++ b/xhiveframework/patches/v14_0/update_attachment_comment.py @@ -0,0 +1,33 @@ +import xhiveframework + + +def execute(): + xhiveframework.db.auto_commit_on_many_writes = 1 + + # Strip everything except link to attachment and icon from comments of type "Attached" + for name, content in xhiveframework.get_all( + "Comment", filters={"comment_type": "Attachment"}, fields=["name", "content"], as_list=True + ): + if not content: + continue + + start = content.find("") + end = content.find("") if end == -1 else end + if end != -1: + content = content[: end + 4] + + xhiveframework.db.set_value("Comment", name, "content", content, update_modified=False) + + # Strip "Removed " from comments of type "Attachment Removed" + for name, content in xhiveframework.get_all( + "Comment", + filters={"comment_type": "Attachment Removed"}, + fields=["name", "content"], + as_list=True, + ): + if content and content.startswith("Removed "): + xhiveframework.db.set_value("Comment", name, "content", content[8:], update_modified=False) diff --git a/xhiveframework/patches/v14_0/update_auto_account_deletion_duration.py b/xhiveframework/patches/v14_0/update_auto_account_deletion_duration.py new file mode 100644 index 0000000..b5049ed --- /dev/null +++ b/xhiveframework/patches/v14_0/update_auto_account_deletion_duration.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + days = xhiveframework.db.get_single_value("Website Settings", "auto_account_deletion") + xhiveframework.db.set_single_value("Website Settings", "auto_account_deletion", days * 24) diff --git a/xhiveframework/patches/v14_0/update_color_names_in_kanban_board_column.py b/xhiveframework/patches/v14_0/update_color_names_in_kanban_board_column.py new file mode 100644 index 0000000..706fefd --- /dev/null +++ b/xhiveframework/patches/v14_0/update_color_names_in_kanban_board_column.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + + +import xhiveframework + + +def execute(): + indicator_map = { + "blue": "Blue", + "orange": "Orange", + "red": "Red", + "green": "Green", + "darkgrey": "Gray", + "gray": "Gray", + "purple": "Purple", + "yellow": "Yellow", + "lightblue": "Light Blue", + } + for d in xhiveframework.get_all("Kanban Board Column", fields=["name", "indicator"]): + color_name = indicator_map.get(d.indicator, "Gray") + xhiveframework.db.set_value("Kanban Board Column", d.name, "indicator", color_name) diff --git a/xhiveframework/patches/v14_0/update_github_endpoints.py b/xhiveframework/patches/v14_0/update_github_endpoints.py new file mode 100644 index 0000000..20fa9d9 --- /dev/null +++ b/xhiveframework/patches/v14_0/update_github_endpoints.py @@ -0,0 +1,10 @@ +import json + +import xhiveframework + + +def execute(): + if xhiveframework.db.exists("Social Login Key", "github"): + xhiveframework.db.set_value( + "Social Login Key", "github", "auth_url_data", json.dumps({"scope": "user:email"}) + ) diff --git a/xhiveframework/patches/v14_0/update_integration_request.py b/xhiveframework/patches/v14_0/update_integration_request.py new file mode 100644 index 0000000..cdafc4c --- /dev/null +++ b/xhiveframework/patches/v14_0/update_integration_request.py @@ -0,0 +1,21 @@ +import xhiveframework + + +def execute(): + doctype = "Integration Request" + + if not xhiveframework.db.has_column(doctype, "integration_type"): + return + + xhiveframework.db.set_value( + doctype, + {"integration_type": "Remote", "integration_request_service": ("!=", "PayPal")}, + "is_remote_request", + 1, + ) + xhiveframework.db.set_value( + doctype, + {"integration_type": "Subscription Notification"}, + "request_description", + "Subscription Notification", + ) diff --git a/xhiveframework/patches/v14_0/update_is_system_generated_flag.py b/xhiveframework/patches/v14_0/update_is_system_generated_flag.py new file mode 100644 index 0000000..05b0a54 --- /dev/null +++ b/xhiveframework/patches/v14_0/update_is_system_generated_flag.py @@ -0,0 +1,20 @@ +import xhiveframework + + +def execute(): + # assuming all customization generated by Admin is system generated customization + custom_field = xhiveframework.qb.DocType("Custom Field") + ( + xhiveframework.qb.update(custom_field) + .set(custom_field.is_system_generated, True) + .where(custom_field.owner == "Administrator") + .run() + ) + + property_setter = xhiveframework.qb.DocType("Property Setter") + ( + xhiveframework.qb.update(property_setter) + .set(property_setter.is_system_generated, True) + .where(property_setter.owner == "Administrator") + .run() + ) diff --git a/xhiveframework/patches/v14_0/update_multistep_webforms.py b/xhiveframework/patches/v14_0/update_multistep_webforms.py new file mode 100644 index 0000000..65fe748 --- /dev/null +++ b/xhiveframework/patches/v14_0/update_multistep_webforms.py @@ -0,0 +1,12 @@ +import xhiveframework + + +def execute(): + if not xhiveframework.db.has_column("Web Form", "is_multi_step_form"): + return + + for web_form in xhiveframework.get_all("Web Form", filters={"is_multi_step_form": 1}): + web_form_fields = xhiveframework.get_doc("Web Form", web_form.name).web_form_fields + for web_form_field in web_form_fields: + if web_form_field.fieldtype == "Section Break" and web_form_field.idx != 1: + xhiveframework.db.set_value("Web Form Field", web_form_field.name, "fieldtype", "Page Break") diff --git a/xhiveframework/patches/v14_0/update_webforms.py b/xhiveframework/patches/v14_0/update_webforms.py new file mode 100644 index 0000000..0a15c32 --- /dev/null +++ b/xhiveframework/patches/v14_0/update_webforms.py @@ -0,0 +1,14 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + + +import xhiveframework + + +def execute(): + xhiveframework.reload_doc("website", "doctype", "web_form_list_column") + xhiveframework.reload_doctype("Web Form") + + for web_form in xhiveframework.get_all("Web Form", fields=["*"]): + if web_form.allow_multiple and not web_form.show_list: + xhiveframework.db.set_value("Web Form", web_form.name, "show_list", True) diff --git a/xhiveframework/patches/v14_0/update_workspace2.py b/xhiveframework/patches/v14_0/update_workspace2.py new file mode 100644 index 0000000..cc30afb --- /dev/null +++ b/xhiveframework/patches/v14_0/update_workspace2.py @@ -0,0 +1,85 @@ +import json + +import xhiveframework +from xhiveframework import _ + + +def execute(): + for seq, workspace in enumerate(xhiveframework.get_all("Workspace")): + doc = xhiveframework.get_doc("Workspace", workspace.name) + content = create_content(doc) + update_workspace(doc, seq, content) + + +def create_content(doc): + content = [] + if doc.get("onboarding"): + content.append({"type": "onboarding", "data": {"onboarding_name": doc.onboarding, "col": 12}}) + if doc.charts: + invalid_links = [] + for c in doc.charts: + if c.get_invalid_links()[0]: + invalid_links.append(c) + else: + content.append({"type": "chart", "data": {"chart_name": c.label, "col": 12}}) + for l in invalid_links: + del doc.charts[doc.charts.index(l)] + if doc.shortcuts: + invalid_links = [] + if doc.charts: + content.append({"type": "spacer", "data": {"col": 12}}) + content.append( + { + "type": "header", + "data": {"text": doc.get("shortcuts_label") or _("Your Shortcuts"), "level": 4, "col": 12}, + } + ) + for s in doc.shortcuts: + if s.get_invalid_links()[0]: + invalid_links.append(s) + else: + content.append({"type": "shortcut", "data": {"shortcut_name": s.label, "col": 4}}) + for l in invalid_links: + del doc.shortcuts[doc.shortcuts.index(l)] + if doc.links: + invalid_links = [] + content.append({"type": "spacer", "data": {"col": 12}}) + content.append( + { + "type": "header", + "data": {"text": doc.get("cards_label") or _("Reports & Masters"), "level": 4, "col": 12}, + } + ) + for l in doc.links: + if l.type == "Card Break": + content.append({"type": "card", "data": {"card_name": l.label, "col": 4}}) + if l.get_invalid_links()[0]: + invalid_links.append(l) + for l in invalid_links: + del doc.links[doc.links.index(l)] + return content + + +def update_workspace(doc, seq, content): + if ( + not doc.title + and (not doc.content or doc.content == "[]") + and not doc.get("is_standard") + and not doc.public + ): + doc.sequence_id = seq + 1 + doc.content = json.dumps(content) + doc.public = 0 if doc.for_user else 1 + doc.title = doc.get("extends") or doc.get("label") + doc.extends = "" + doc.category = "" + doc.onboarding = "" + doc.extends_another_page = 0 + doc.is_default = 0 + doc.is_standard = 0 + doc.developer_mode_only = 0 + doc.disable_user_customization = 0 + doc.pin_to_top = 0 + doc.pin_to_bottom = 0 + doc.hide_custom = 0 + doc.save(ignore_permissions=True) diff --git a/xhiveframework/patches/v15_0/copy_disable_prepared_report_to_prepared_report.py b/xhiveframework/patches/v15_0/copy_disable_prepared_report_to_prepared_report.py new file mode 100644 index 0000000..536f861 --- /dev/null +++ b/xhiveframework/patches/v15_0/copy_disable_prepared_report_to_prepared_report.py @@ -0,0 +1,6 @@ +import xhiveframework + + +def execute(): + table = xhiveframework.qb.DocType("Report") + xhiveframework.qb.update(table).set(table.prepared_report, 0).where(table.disable_prepared_report == 1) diff --git a/xhiveframework/patches/v15_0/drop_modified_index.py b/xhiveframework/patches/v15_0/drop_modified_index.py new file mode 100644 index 0000000..a5d85ef --- /dev/null +++ b/xhiveframework/patches/v15_0/drop_modified_index.py @@ -0,0 +1,21 @@ +import xhiveframework +from xhiveframework.patches.v14_0.drop_unused_indexes import drop_index_if_exists + + +def execute(): + if xhiveframework.db.db_type == "postgres": + return + + db_tables = xhiveframework.db.get_tables(cached=False) + + child_tables = xhiveframework.get_all( + "DocType", + {"istable": 1, "is_virtual": 0}, + pluck="name", + ) + + for doctype in child_tables: + table = f"tab{doctype}" + if table not in db_tables: + continue + drop_index_if_exists(table, "modified") diff --git a/xhiveframework/patches/v15_0/move_event_cancelled_to_status.py b/xhiveframework/patches/v15_0/move_event_cancelled_to_status.py new file mode 100644 index 0000000..d1f3474 --- /dev/null +++ b/xhiveframework/patches/v15_0/move_event_cancelled_to_status.py @@ -0,0 +1,12 @@ +import xhiveframework + + +def execute(): + Event = xhiveframework.qb.DocType("Event") + query = ( + xhiveframework.qb.update(Event) + .set(Event.event_type, "Private") + .set(Event.status, "Cancelled") + .where(Event.event_type == "Cancelled") + ) + query.run() diff --git a/xhiveframework/patches/v15_0/remove_background_jobs_from_dropdown.py b/xhiveframework/patches/v15_0/remove_background_jobs_from_dropdown.py new file mode 100644 index 0000000..3794a09 --- /dev/null +++ b/xhiveframework/patches/v15_0/remove_background_jobs_from_dropdown.py @@ -0,0 +1,9 @@ +import xhiveframework + + +def execute(): + item = xhiveframework.db.exists("Navbar Item", {"item_label": "Background Jobs"}) + if not item: + return + + xhiveframework.delete_doc("Navbar Item", item) diff --git a/xhiveframework/patches/v15_0/remove_event_streaming.py b/xhiveframework/patches/v15_0/remove_event_streaming.py new file mode 100644 index 0000000..0f88875 --- /dev/null +++ b/xhiveframework/patches/v15_0/remove_event_streaming.py @@ -0,0 +1,22 @@ +import xhiveframework + + +def execute(): + if "event_streaming" in xhiveframework.get_installed_apps(): + return + + xhiveframework.delete_doc_if_exists("Module Def", "Event Streaming", force=True) + + for doc in [ + "Event Consumer Document Type", + "Document Type Mapping", + "Event Producer", + "Event Producer Last Update", + "Event Producer Document Type", + "Event Consumer", + "Document Type Field Mapping", + "Event Update Log", + "Event Update Log Consumer", + "Event Sync Log", + ]: + xhiveframework.delete_doc_if_exists("DocType", doc, force=True) diff --git a/xhiveframework/patches/v15_0/remove_implicit_primary_key.py b/xhiveframework/patches/v15_0/remove_implicit_primary_key.py new file mode 100644 index 0000000..a15d41b --- /dev/null +++ b/xhiveframework/patches/v15_0/remove_implicit_primary_key.py @@ -0,0 +1,50 @@ +import xhiveframework +from xhiveframework.model.naming import is_autoincremented + +possible_log_types = ( + "Version", + "Error Log", + "Scheduled Job Log", + "Event Sync Log", + "Event Update Log", + "Access Log", + "View Log", + "Activity Log", + "Energy Point Log", + "Notification Log", + "Email Queue", + "DocShare", + "Document Follow", + "Console Log", +) + + +def execute(): + """Few doctypes had int PKs even though schema didn't mention them, this requires detecting it + at runtime which is prone to bugs and adds unnecessary overhead. + + This patch converts them back to varchar. + """ + for doctype in possible_log_types: + if ( + xhiveframework.db.exists("DocType", doctype) + and _is_implicit_int_pk(doctype) + and not is_autoincremented(doctype) + ): + xhiveframework.db.change_column_type( + doctype, + "name", + type=f"varchar({xhiveframework.db.VARCHAR_LEN})", + nullable=True, + ) + + +def _is_implicit_int_pk(doctype: str) -> bool: + query = f"""select data_type FROM information_schema.columns where column_name = 'name' and table_name = 'tab{doctype}'""" + values = () + if xhiveframework.db.db_type == "mariadb": + query += " and table_schema = %s" + values = (xhiveframework.db.db_name,) + + col_type = xhiveframework.db.sql(query, values) + return bool(col_type and col_type[0][0] == "bigint") diff --git a/xhiveframework/patches/v15_0/remove_prepared_report_settings_from_system_settings.py b/xhiveframework/patches/v15_0/remove_prepared_report_settings_from_system_settings.py new file mode 100644 index 0000000..71b62e0 --- /dev/null +++ b/xhiveframework/patches/v15_0/remove_prepared_report_settings_from_system_settings.py @@ -0,0 +1,9 @@ +import xhiveframework +from xhiveframework.utils import cint + + +def execute(): + expiry_period = ( + cint(xhiveframework.db.get_singles_dict("System Settings").get("prepared_report_expiry_period")) or 30 + ) + xhiveframework.get_single("Log Settings").register_doctype("Prepared Report", expiry_period) diff --git a/xhiveframework/patches/v15_0/sanitize_workspace_titles.py b/xhiveframework/patches/v15_0/sanitize_workspace_titles.py new file mode 100644 index 0000000..c1e9491 --- /dev/null +++ b/xhiveframework/patches/v15_0/sanitize_workspace_titles.py @@ -0,0 +1,25 @@ +import xhiveframework +from xhiveframework.desk.doctype.workspace.workspace import update_page +from xhiveframework.utils import strip_html +from xhiveframework.utils.html_utils import unescape_html + + +def execute(): + workspaces_to_update = xhiveframework.get_all( + "Workspace", + filters={"module": ("is", "not set")}, + fields=["name", "title", "icon", "indicator_color", "parent_page as parent", "public"], + ) + for workspace in workspaces_to_update: + new_title = strip_html(unescape_html(workspace.title)) + + if new_title == workspace.title: + continue + + workspace.title = new_title + try: + update_page(**workspace) + xhiveframework.db.commit() + + except Exception: + xhiveframework.db.rollback() diff --git a/xhiveframework/patches/v15_0/set_contact_full_name.py b/xhiveframework/patches/v15_0/set_contact_full_name.py new file mode 100644 index 0000000..d0b1cef --- /dev/null +++ b/xhiveframework/patches/v15_0/set_contact_full_name.py @@ -0,0 +1,31 @@ +import xhiveframework +from xhiveframework.contacts.doctype.contact.contact import get_full_name +from xhiveframework.utils import update_progress_bar + + +def execute(): + """Set full name for all contacts""" + xhiveframework.db.auto_commit_on_many_writes = 1 + + contacts = xhiveframework.get_all( + "Contact", + fields=["name", "first_name", "middle_name", "last_name", "company_name"], + filters={"full_name": ("is", "not set")}, + as_list=True, + ) + total = len(contacts) + for idx, (name, first, middle, last, company) in enumerate(contacts): + update_progress_bar("Setting full name for contacts", idx, total) + try: + xhiveframework.db.set_value( + "Contact", + name, + "full_name", + get_full_name(first, middle, last, company), + update_modified=False, + ) + except xhiveframework.db.DataError as e: + if xhiveframework.db.is_data_too_long(e): + print("Full name is too long for DB column, skipping") + continue + raise e diff --git a/xhiveframework/patches/v15_0/set_file_type.py b/xhiveframework/patches/v15_0/set_file_type.py new file mode 100644 index 0000000..40fb8bb --- /dev/null +++ b/xhiveframework/patches/v15_0/set_file_type.py @@ -0,0 +1,32 @@ +import mimetypes + +import xhiveframework + + +def execute(): + """Set 'File Type' for all files based on file extension.""" + files = xhiveframework.db.get_all( + "File", + fields=["name", "file_name", "file_url"], + filters={"is_folder": 0, "file_type": ("is", "not set")}, + ) + + xhiveframework.db.auto_commit_on_many_writes = 1 + + for file in files: + file_extension = get_file_extension(file.file_name or file.file_url) + if file_extension: + xhiveframework.db.set_value("File", file.name, "file_type", file_extension, update_modified=False) + + xhiveframework.db.auto_commit_on_many_writes = 0 + + +def get_file_extension(file_name): + if not file_name: + return None + file_type = mimetypes.guess_type(file_name)[0] + if not file_type: + return None + + file_extension = mimetypes.guess_extension(file_type) + return file_extension.lstrip(".").upper() if file_extension else None diff --git a/xhiveframework/patches/v15_0/validate_newsletter_recipients.py b/xhiveframework/patches/v15_0/validate_newsletter_recipients.py new file mode 100644 index 0000000..debe9f0 --- /dev/null +++ b/xhiveframework/patches/v15_0/validate_newsletter_recipients.py @@ -0,0 +1,9 @@ +import xhiveframework +from xhiveframework.utils import validate_email_address + + +def execute(): + for name, email in xhiveframework.get_all("Email Group Member", fields=["name", "email"], as_list=True): + if not validate_email_address(email, throw=False): + xhiveframework.db.set_value("Email Group Member", name, "unsubscribed", 1) + xhiveframework.db.commit() diff --git a/xhiveframework/permissions.py b/xhiveframework/permissions.py new file mode 100644 index 0000000..e9bc5b8 --- /dev/null +++ b/xhiveframework/permissions.py @@ -0,0 +1,842 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import copy +import functools + +import xhiveframework +import xhiveframework.share +from xhiveframework import _, msgprint +from xhiveframework.query_builder import DocType +from xhiveframework.utils import cint, cstr + +rights = ( + "select", + "read", + "write", + "create", + "delete", + "submit", + "cancel", + "amend", + "print", + "email", + "report", + "import", + "export", + "share", +) + + +GUEST_ROLE = "Guest" +ALL_USER_ROLE = "All" # This includes website users too. +SYSTEM_USER_ROLE = "Desk User" +ADMIN_ROLE = "Administrator" + + +# These roles are automatically assigned based on user type +AUTOMATIC_ROLES = (GUEST_ROLE, ALL_USER_ROLE, SYSTEM_USER_ROLE, ADMIN_ROLE) + + +def print_has_permission_check_logs(func): + @functools.wraps(func) + def inner(*args, **kwargs): + raise_exception = kwargs.get("raise_exception", True) + self_perm_check = True if not kwargs.get("user") else kwargs.get("user") == xhiveframework.session.user + + if raise_exception: + xhiveframework.flags["has_permission_check_logs"] = [] + + result = func(*args, **kwargs) + + # print only if access denied + # and if user is checking his own permission + if not result and self_perm_check and raise_exception: + msgprint(("
      ").join(xhiveframework.flags.get("has_permission_check_logs", []))) + + if raise_exception: + xhiveframework.flags.pop("has_permission_check_logs", None) + return result + + return inner + + +def _debug_log(log: str): + if not hasattr(xhiveframework.local, "permission_debug_log"): + xhiveframework.local.permission_debug_log = [] + xhiveframework.local.permission_debug_log.append(log) + + +def _pop_debug_log() -> list[str]: + if log := getattr(xhiveframework.local, "permission_debug_log", None): + del xhiveframework.local.permission_debug_log + return log + return [] + + +@print_has_permission_check_logs +def has_permission( + doctype, + ptype="read", + doc=None, + user=None, + raise_exception=True, + *, + parent_doctype=None, + debug=False, +) -> bool: + """Return True if user has permission `ptype` for given `doctype`. + If `doc` is passed, also check user, share and owner permissions. + + :param doctype: DocType to check permission for + :param ptype: Permission Type to check + :param doc: Check User Permissions for specified document. + :param user: User to check permission for. Defaults to current user. + :param raise_exception: + DOES NOT raise an exception. + If not False, will display a message using xhiveframework.msgprint + which explains why the permission check failed. + + :param parent_doctype: + Required when checking permission for a child DocType (unless doc is specified) + """ + + if not user: + user = xhiveframework.session.user + + if user == "Administrator": + debug and _debug_log("Allowed everything because user is Administrator") + return True + + if ptype == "share" and xhiveframework.get_system_settings("disable_document_sharing"): + debug and _debug_log("User can't share because sharing is disabled globally from system settings") + return False + + if not doc and hasattr(doctype, "doctype"): + # first argument can be doc or doctype + doc = doctype + doctype = doc.doctype + + if xhiveframework.is_table(doctype): + return has_child_permission(doctype, ptype, doc, user, raise_exception, parent_doctype, debug=debug) + + meta = xhiveframework.get_meta(doctype) + + if doc: + if isinstance(doc, str | int): + doc = xhiveframework.get_doc(meta.name, doc) + perm = get_doc_permissions(doc, user=user, ptype=ptype, debug=debug).get(ptype) + if not perm: + debug and _debug_log( + "Permission check failed from role permission system. Check if user's role grant them permission to the document." + ) + msg = _("User {0} does not have access to this document").format(xhiveframework.bold(user)) + if xhiveframework.has_permission(doc.doctype): + msg += f": {_(doc.doctype)} - {doc.name}" + push_perm_check_log(msg, debug=debug) + else: + if ptype == "submit" and not cint(meta.is_submittable): + push_perm_check_log(_("Document Type is not submittable"), debug=debug) + return False + + if ptype == "import" and not cint(meta.allow_import): + push_perm_check_log(_("Document Type is not importable"), debug=debug) + return False + + role_permissions = get_role_permissions(meta, user=user, debug=debug) + debug and _debug_log( + "User has following permissions using role permission system: " + + xhiveframework.as_json(role_permissions, indent=8) + ) + + perm = role_permissions.get(ptype) + + if not perm: + push_perm_check_log( + _("User {0} does not have doctype access via role permission for document {1}").format( + xhiveframework.bold(user), xhiveframework.bold(_(doctype)) + ), + debug=debug, + ) + + def false_if_not_shared(): + if ptype not in ("read", "write", "share", "submit", "email", "print"): + debug and _debug_log(f"Permission type {ptype} can not be shared") + return False + + rights = ["read" if ptype in ("email", "print") else ptype] + + if doc: + doc_name = get_doc_name(doc) + shared = xhiveframework.share.get_shared( + doctype, + user, + rights=rights, + filters=[["share_name", "=", doc_name]], + limit=1, + ) + debug and _debug_log(f"Document is shared with user for {ptype}? {bool(shared)}") + return bool(shared) + + elif xhiveframework.share.get_shared(doctype, user, rights=rights, limit=1): + # if atleast one shared doc of that type, then return True + # this is used in db_query to check if permission on DocType + debug and _debug_log(f"At least one document is shared with user with perm: {rights}") + return True + + return False + + if not perm: + debug and _debug_log("Checking if document/doctype is explicitly shared with user") + perm = false_if_not_shared() + + return bool(perm) + + +def get_doc_permissions(doc, user=None, ptype=None, debug=False): + """Return a dict of evaluated permissions for given `doc` like `{"read":1, "write":1}`""" + if not user: + user = xhiveframework.session.user + + meta = xhiveframework.get_meta(doc.doctype) + + def is_user_owner(): + return (doc.get("owner") or "").lower() == user.lower() + + if not has_controller_permissions(doc, ptype, user=user, debug=debug): + push_perm_check_log(_("Not allowed via controller permission check"), debug=debug) + return {ptype: 0} + + permissions = copy.deepcopy(get_role_permissions(meta, user=user, is_owner=is_user_owner(), debug=debug)) + + debug and _debug_log( + "User has following permissions using role permission system: " + + xhiveframework.as_json(permissions, indent=8) + ) + + if not cint(meta.is_submittable): + permissions["submit"] = 0 + + if not cint(meta.allow_import): + permissions["import"] = 0 + + # Override with `if_owner` perms irrespective of user + if permissions.get("has_if_owner_enabled"): + # apply owner permissions on top of existing permissions + # some access might be only for the owner + # eg. everyone might have read access but only owner can delete + permissions.update(permissions.get("if_owner", {})) + debug and _debug_log( + "User is owner of document, so permissions are updated to: " + xhiveframework.as_json(permissions) + ) + + if not has_user_permission(doc, user, debug=debug): + if is_user_owner(): + # replace with owner permissions + permissions = permissions.get("if_owner", {}) + # if_owner does not come with create rights... + permissions["create"] = 0 + debug and _debug_log("User has only 'If owner' permissions because of User Permissions") + else: + debug and _debug_log("User has no permissions because of User Permissions") + permissions = {} + + debug and _debug_log( + "Final applicable permissions after evaluating user permissions: " + + xhiveframework.as_json(permissions, indent=8) + ) + return permissions + + +def get_role_permissions(doctype_meta, user=None, is_owner=None, debug=False): + """ + Returns dict of evaluated role permissions like + { + "read": 1, + "write": 0, + // if "if_owner" is enabled + "if_owner": + { + "read": 1, + "write": 0 + } + } + """ + if isinstance(doctype_meta, str): + doctype_meta = xhiveframework.get_meta(doctype_meta) # assuming doctype name was passed + + if not user: + user = xhiveframework.session.user + + cache_key = (doctype_meta.name, user, bool(is_owner)) + + if user == "Administrator": + debug and _debug_log("all permissions granted because user is Administrator") + return allow_everything() + + if not xhiveframework.local.role_permissions.get(cache_key) or debug: + perms = xhiveframework._dict(if_owner={}) + + roles = xhiveframework.get_roles(user) + debug and _debug_log("User has following roles: " + str(roles)) + + def is_perm_applicable(perm): + return perm.role in roles and cint(perm.permlevel) == 0 + + def has_permission_without_if_owner_enabled(ptype): + return any(p.get(ptype, 0) and not p.get("if_owner", 0) for p in applicable_permissions) + + applicable_permissions = list(filter(is_perm_applicable, getattr(doctype_meta, "permissions", []))) + has_if_owner_enabled = any(p.get("if_owner", 0) for p in applicable_permissions) + perms["has_if_owner_enabled"] = has_if_owner_enabled + + for ptype in rights: + pvalue = any(p.get(ptype, 0) for p in applicable_permissions) + # check if any perm object allows perm type + perms[ptype] = cint(pvalue) + if ( + pvalue + and has_if_owner_enabled + and not has_permission_without_if_owner_enabled(ptype) + and ptype != "create" + ): + perms["if_owner"][ptype] = cint(pvalue and is_owner) + # has no access if not owner + # only provide select or read access so that user is able to at-least access list + # (and the documents will be filtered based on owner sin further checks) + perms[ptype] = 1 if ptype in ("select", "read") else 0 + + xhiveframework.local.role_permissions[cache_key] = perms + + return xhiveframework.local.role_permissions[cache_key] + + +def get_user_permissions(user): + from xhiveframework.core.doctype.user_permission.user_permission import get_user_permissions + + return get_user_permissions(user) + + +def has_user_permission(doc, user=None, debug=False): + """Return True if User is allowed to view considering User Permissions.""" + from xhiveframework.core.doctype.user_permission.user_permission import get_user_permissions + + user_permissions = get_user_permissions(user) + + if not user_permissions: + # no user permission rules specified for this doctype + debug and _debug_log("User is not affected by any user permissions") + return True + + # user can create own role permissions, so nothing applies + if get_role_permissions("User Permission", user=user).get("write"): + debug and _debug_log("User permission bypassed because user can modify user permissions.") + return True + + # don't apply strict user permissions for single doctypes since they contain empty link fields + apply_strict_user_permissions = ( + False if doc.meta.issingle else xhiveframework.get_system_settings("apply_strict_user_permissions") + ) + if apply_strict_user_permissions: + debug and _debug_log("Strict user permissions will be applied") + + doctype = doc.get("doctype") + docname = doc.get("name") + + # STEP 1: --------------------- + # check user permissions on self + if doctype in user_permissions: + allowed_docs = get_allowed_docs_for_doctype(user_permissions.get(doctype, []), doctype) + + # if allowed_docs is empty it states that there is no applicable permission under the current doctype + + # only check if allowed_docs is not empty + if allowed_docs and str(docname) not in allowed_docs: + # no user permissions for this doc specified + debug and _debug_log( + "User doesn't have access to this document because of User Permissions, allowed documents: " + + str(allowed_docs) + ) + push_perm_check_log(_("Not allowed for {0}: {1}").format(_(doctype), docname), debug=debug) + return False + else: + debug and _debug_log(f"User Has access to {docname} via User Permissions.") + + # STEP 2: --------------------------------- + # check user permissions in all link fields + + def check_user_permission_on_link_fields(d): + # check user permissions for all the link fields of the given + # document object d + # + # called for both parent and child records + + meta = xhiveframework.get_meta(d.get("doctype")) + + # check all link fields for user permissions + for field in meta.get_link_fields(): + if field.ignore_user_permissions: + continue + + # empty value, do you still want to apply user permissions? + if not d.get(field.fieldname) and not apply_strict_user_permissions: + # nah, not strict + continue + + if field.options not in user_permissions: + continue + + # get the list of all allowed values for this link + allowed_docs = get_allowed_docs_for_doctype(user_permissions.get(field.options, []), doctype) + + if allowed_docs and d.get(field.fieldname) not in allowed_docs: + # restricted for this link field, and no matching values found + # make the right message and exit + if d.get("parentfield"): + # "You are not allowed to access this Employee record because it is linked + # to Company 'Restricted Company' in row 3, field Reference Type" + msg = _( + "You are not allowed to access this {0} record because it is linked to {1} '{2}' in row {3}, field {4}" + ).format( + _(meta.doctype), + _(field.options), + d.get(field.fieldname) or _("empty"), + d.idx, + _(field.label, context=field.parent) if field.label else field.fieldname, + ) + else: + # "You are not allowed to access Company 'Restricted Company' in field Reference Type" + msg = _( + "You are not allowed to access this {0} record because it is linked to {1} '{2}' in field {3}" + ).format( + _(meta.doctype), + _(field.options), + d.get(field.fieldname) or _("empty"), + _(field.label, context=field.parent) if field.label else field.fieldname, + ) + + push_perm_check_log(msg, debug=debug) + + return False + + return True + + if not check_user_permission_on_link_fields(doc): + return False + + for d in doc.get_all_children(): + if not check_user_permission_on_link_fields(d): + return False + + return True + + +def has_controller_permissions(doc, ptype, user=None, debug=False) -> bool: + """Return controller permissions if denied, True if not defined. + + Controllers can only deny permission, they can not explicitly grant any permission that wasn't + already present.""" + if not user: + user = xhiveframework.session.user + + methods = xhiveframework.get_hooks("has_permission").get(doc.doctype, []) + + if not methods: + return True + + for method in reversed(methods): + controller_permission = xhiveframework.call(method, doc=doc, ptype=ptype, user=user, debug=debug) + debug and _debug_log(f"Controller permission check from {method}: {controller_permission}") + if controller_permission is not None: + return bool(controller_permission) + + # None of the controller hooks returned anything conclusive + return True + + +def get_doctypes_with_read(): + return list({cstr(p.parent) for p in get_valid_perms() if p.parent}) + + +def get_valid_perms(doctype=None, user=None): + """Get valid permissions for the current user from DocPerm and Custom DocPerm""" + roles = get_roles(user) + + perms = get_perms_for(roles) + custom_perms = get_perms_for(roles, "Custom DocPerm") + + doctypes_with_custom_perms = get_doctypes_with_custom_docperms() + for p in perms: + if p.parent not in doctypes_with_custom_perms: + custom_perms.append(p) + + if doctype: + return [p for p in custom_perms if p.parent == doctype] + else: + return custom_perms + + +def get_all_perms(role): + """Returns valid permissions for a given role""" + perms = xhiveframework.get_all("DocPerm", fields="*", filters=dict(role=role)) + custom_perms = xhiveframework.get_all("Custom DocPerm", fields="*", filters=dict(role=role)) + doctypes_with_custom_perms = xhiveframework.get_all("Custom DocPerm", pluck="parent", distinct=True) + + for p in perms: + if p.parent not in doctypes_with_custom_perms: + custom_perms.append(p) + return custom_perms + + +def get_roles(user=None, with_standard=True): + """get roles of current user""" + if not user: + user = xhiveframework.session.user + + if user == "Guest" or not user: + return [GUEST_ROLE] + + def get(): + if user == "Administrator": + return xhiveframework.get_all("Role", pluck="name") # return all available roles + else: + table = DocType("Has Role") + roles = ( + xhiveframework.qb.from_(table) + .where( + (table.parenttype == "User") + & (table.parent == user) + & (table.role.notin(AUTOMATIC_ROLES)) + ) + .select(table.role) + .run(pluck=True) + ) + roles += [ALL_USER_ROLE, GUEST_ROLE] + if is_system_user(user): + roles.append(SYSTEM_USER_ROLE) + return roles + + roles = xhiveframework.cache.hget("roles", user, get) + + # filter standard if required + if not with_standard: + roles = [r for r in roles if r not in AUTOMATIC_ROLES] + + return roles + + +def get_doctype_roles(doctype, access_type="read"): + """Returns a list of roles that are allowed to access passed doctype.""" + meta = xhiveframework.get_meta(doctype) + return [d.role for d in meta.get("permissions") if d.get(access_type)] + + +def get_perms_for(roles, perm_doctype="DocPerm"): + """Get perms for given roles""" + filters = {"permlevel": 0, "docstatus": 0, "role": ["in", roles]} + return xhiveframework.get_all(perm_doctype, fields=["*"], filters=filters) + + +def get_doctypes_with_custom_docperms(): + """Returns all the doctypes with Custom Docperms""" + + doctypes = xhiveframework.get_all("Custom DocPerm", fields=["parent"], distinct=1) + return [d.parent for d in doctypes] + + +def add_user_permission( + doctype, + name, + user, + ignore_permissions=False, + applicable_for=None, + is_default=0, + hide_descendants=0, +): + """Add user permission""" + from xhiveframework.core.doctype.user_permission.user_permission import user_permission_exists + + if not user_permission_exists(user, doctype, name, applicable_for): + if not xhiveframework.db.exists(doctype, name): + xhiveframework.throw(_("{0} {1} not found").format(_(doctype), name), xhiveframework.DoesNotExistError) + + xhiveframework.get_doc( + dict( + doctype="User Permission", + user=user, + allow=doctype, + for_value=name, + is_default=is_default, + applicable_for=applicable_for, + apply_to_all_doctypes=0 if applicable_for else 1, + hide_descendants=hide_descendants, + ) + ).insert(ignore_permissions=ignore_permissions) + + +def remove_user_permission(doctype, name, user): + user_permission_name = xhiveframework.db.get_value( + "User Permission", dict(user=user, allow=doctype, for_value=name) + ) + xhiveframework.delete_doc("User Permission", user_permission_name) + + +def clear_user_permissions_for_doctype(doctype, user=None): + filters = {"allow": doctype} + if user: + filters["user"] = user + user_permissions_for_doctype = xhiveframework.get_all("User Permission", filters=filters) + for d in user_permissions_for_doctype: + xhiveframework.delete_doc("User Permission", d.name) + + +def can_import(doctype, raise_exception=False): + if not ("System Manager" in xhiveframework.get_roles() or has_permission(doctype, "import")): + if raise_exception: + raise xhiveframework.PermissionError(f"You are not allowed to import: {doctype}") + else: + return False + return True + + +def can_export(doctype, raise_exception=False): + if "System Manager" in xhiveframework.get_roles(): + return True + else: + role_permissions = xhiveframework.permissions.get_role_permissions(doctype) + has_access = role_permissions.get("export") or role_permissions.get("if_owner").get("export") + if not has_access and raise_exception: + raise xhiveframework.PermissionError(_("You are not allowed to export {} doctype").format(doctype)) + return has_access + + +def update_permission_property( + doctype, + role, + permlevel, + ptype, + value=None, + validate=True, + if_owner=0, +): + """Update a property in Custom Perm""" + from xhiveframework.core.doctype.doctype.doctype import validate_permissions_for_doctype + + out = setup_custom_perms(doctype) + + name = xhiveframework.db.get_value( + "Custom DocPerm", dict(parent=doctype, role=role, permlevel=permlevel, if_owner=if_owner) + ) + table = DocType("Custom DocPerm") + xhiveframework.qb.update(table).set(ptype, value).where(table.name == name).run() + + if validate: + validate_permissions_for_doctype(doctype) + + return out + + +def setup_custom_perms(parent): + """if custom permssions are not setup for the current doctype, set them up""" + if not xhiveframework.db.exists("Custom DocPerm", dict(parent=parent)): + copy_perms(parent) + return True + + +def add_permission(doctype, role, permlevel=0, ptype=None): + """Add a new permission rule to the given doctype + for the given Role and Permission Level""" + from xhiveframework.core.doctype.doctype.doctype import validate_permissions_for_doctype + + setup_custom_perms(doctype) + + if xhiveframework.db.get_value( + "Custom DocPerm", dict(parent=doctype, role=role, permlevel=permlevel, if_owner=0) + ): + xhiveframework.msgprint( + _("Rule for this doctype, role, permlevel and if-owner combination already exists.").format( + doctype, + ), + alert=True, + ) + return + + if not ptype: + ptype = "read" + + custom_docperm = xhiveframework.get_doc( + { + "doctype": "Custom DocPerm", + "__islocal": 1, + "parent": doctype, + "parenttype": "DocType", + "parentfield": "permissions", + "role": role, + "permlevel": permlevel, + ptype: 1, + } + ) + + custom_docperm.save() + + validate_permissions_for_doctype(doctype) + return custom_docperm.name + + +def copy_perms(parent): + """Copy all DocPerm in to Custom DocPerm for the given document""" + for d in xhiveframework.get_all("DocPerm", fields="*", filters=dict(parent=parent)): + custom_perm = xhiveframework.new_doc("Custom DocPerm") + custom_perm.update(d) + custom_perm.insert(ignore_permissions=True) + + +def reset_perms(doctype): + """Reset permissions for given doctype.""" + from xhiveframework.desk.notifications import delete_notification_count_for + + delete_notification_count_for(doctype) + xhiveframework.db.delete("Custom DocPerm", {"parent": doctype}) + + +def get_linked_doctypes(dt: str) -> list: + meta = xhiveframework.get_meta(dt) + linked_doctypes = [dt] + [ + d.options + for d in meta.get( + "fields", + {"fieldtype": "Link", "ignore_user_permissions": ("!=", 1), "options": ("!=", "[Select]")}, + ) + ] + + return list(set(linked_doctypes)) + + +def get_doc_name(doc): + if not doc: + return None + return doc if isinstance(doc, str) else str(doc.name) + + +def allow_everything(): + """ + returns a dict with access to everything + eg. {"read": 1, "write": 1, ...} + """ + return {ptype: 1 for ptype in rights} + + +def get_allowed_docs_for_doctype(user_permissions, doctype): + """Returns all the docs from the passed user_permissions that are + allowed under provided doctype""" + return filter_allowed_docs_for_doctype(user_permissions, doctype, with_default_doc=False) + + +def filter_allowed_docs_for_doctype(user_permissions, doctype, with_default_doc=True): + """Returns all the docs from the passed user_permissions that are + allowed under provided doctype along with default doc value if with_default_doc is set""" + allowed_doc = [] + default_doc = None + for doc in user_permissions: + if not doc.get("applicable_for") or doc.get("applicable_for") == doctype: + allowed_doc.append(doc.get("doc")) + if doc.get("is_default") or len(user_permissions) == 1 and with_default_doc: + default_doc = doc.get("doc") + + return (allowed_doc, default_doc) if with_default_doc else allowed_doc + + +def push_perm_check_log(log, debug=False): + debug and _debug_log(log) + if xhiveframework.flags.get("has_permission_check_logs") is None: + return + + xhiveframework.flags.get("has_permission_check_logs").append(log) + + +def has_child_permission( + child_doctype, + ptype="read", + child_doc=None, + user=None, + raise_exception=True, + parent_doctype=None, + *, + debug=False, +) -> bool: + debug and _debug_log("This doctype is a child table, permissions will be checked on parent.") + if isinstance(child_doc, str): + child_doc = xhiveframework.db.get_value( + child_doctype, + child_doc, + ("parent", "parenttype", "parentfield"), + as_dict=True, + ) + + if child_doc: + parent_doctype = child_doc.parenttype + + if not parent_doctype: + push_perm_check_log( + _("Please specify a valid parent DocType for {0}").format(xhiveframework.bold(child_doctype)), + debug=debug, + ) + return False + + parent_meta = xhiveframework.get_meta(parent_doctype) + + if parent_meta.istable or not ( + valid_parentfields := [ + df.fieldname for df in parent_meta.get_table_fields() if df.options == child_doctype + ] + ): + push_perm_check_log( + _("{0} is not a valid parent DocType for {1}").format( + xhiveframework.bold(parent_doctype), xhiveframework.bold(child_doctype) + ), + debug=debug, + ) + return False + + if child_doc: + parentfield = child_doc.parentfield + if not parentfield: + push_perm_check_log( + _("Parentfield not specified in {0}: {1}").format( + xhiveframework.bold(child_doctype), xhiveframework.bold(child_doc.name) + ), + debug=debug, + ) + return False + + if parentfield not in valid_parentfields: + push_perm_check_log( + _("{0} is not a valid parentfield for {1}").format( + xhiveframework.bold(parentfield), xhiveframework.bold(child_doctype) + ), + debug=debug, + ) + return False + + permlevel = parent_meta.get_field(parentfield).permlevel + accessible_permlevels = parent_meta.get_permlevel_access(ptype, user=user) + if permlevel > 0 and permlevel not in accessible_permlevels: + push_perm_check_log( + _("Insufficient Permission Level for {0}").format(xhiveframework.bold(parent_doctype)), debug=debug + ) + debug and _debug_log( + f"This table is perm level {permlevel} but user only has access to {accessible_permlevels}" + ) + return False + + return has_permission( + parent_doctype, + ptype=ptype, + doc=child_doc and getattr(child_doc, "parent_doc", child_doc.parent), + user=user, + raise_exception=raise_exception, + debug=debug, + ) + + +def is_system_user(user: str | None = None) -> bool: + return xhiveframework.get_cached_value("User", user or xhiveframework.session.user, "user_type") == "System User" diff --git a/xhiveframework/printing/__init__.py b/xhiveframework/printing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/__init__.py b/xhiveframework/printing/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/letter_head/__init__.py b/xhiveframework/printing/doctype/letter_head/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/letter_head/letter_head.js b/xhiveframework/printing/doctype/letter_head/letter_head.js new file mode 100644 index 0000000..9c0d7e1 --- /dev/null +++ b/xhiveframework/printing/doctype/letter_head/letter_head.js @@ -0,0 +1,61 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Letter Head", { + setup(frm) { + frm.get_field("instructions").html(INSTRUCTIONS); + }, + + refresh: function (frm) { + frm.flag_public_attachments = true; + }, + + validate: (frm) => { + ["header_script", "footer_script"].forEach((field) => { + if (!frm.doc[field]) return; + + try { + eval(frm.doc[field]); + } catch (e) { + xhiveframework.throw({ + title: __("Error in Header/Footer Script"), + indicator: "orange", + message: '
      ' + e.stack + "
      ", + }); + } + }); + }, +}); + +const INSTRUCTIONS = `

      ${__("Letter Head Scripts")}

      +

      ${__("Header/Footer scripts can be used to add dynamic behaviours.")}

      +
      +
      +// ${__(
      +	"The following Header Script will add the current date to an element in 'Header HTML' with class 'header-content'"
      +)}
      +var el = document.getElementsByClassName("header-content");
      +if (el.length > 0) {
      +	el[0].textContent += " " + new Date().toGMTString();
      +}
      +
      +
      +

      ${__("You can also access wkhtmltopdf variables (valid only in PDF print):")}

      +
      +
      +// ${__("Get Header and Footer wkhtmltopdf variables")}
      +// ${__("Snippet and more variables:  {0}", ["https://wkhtmltopdf.org/usage/wkhtmltopdf.txt"])}
      +var vars = {};
      +var query_strings_from_url = document.location.search.substring(1).split('&');
      +for (var query_string in query_strings_from_url) {
      +	if (query_strings_from_url.hasOwnProperty(query_string)) {
      +		var temp_var = query_strings_from_url[query_string].split('=', 2);
      +		vars[temp_var[0]] = decodeURI(temp_var[1]);
      +	}
      +}
      +var el = document.getElementsByClassName("header-content");
      +if (el.length > 0 && vars["page"] == 1) {
      +	el[0].textContent += " : " + vars["date"];
      +}
      +
      +
      `; diff --git a/xhiveframework/printing/doctype/letter_head/letter_head.json b/xhiveframework/printing/doctype/letter_head/letter_head.json new file mode 100644 index 0000000..4ffca13 --- /dev/null +++ b/xhiveframework/printing/doctype/letter_head/letter_head.json @@ -0,0 +1,229 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:letter_head_name", + "creation": "2012-11-22 17:45:46", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "letter_head_name", + "source", + "footer_source", + "column_break_3", + "disabled", + "is_default", + "letter_head_image_section", + "image", + "image_height", + "image_width", + "align", + "header_section", + "content", + "footer_section", + "footer", + "footer_image_section", + "footer_image", + "footer_image_height", + "footer_image_width", + "footer_align", + "scripts_section", + "header_script", + "footer_script", + "instructions" + ], + "fields": [ + { + "fieldname": "letter_head_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Letter Head Name", + "oldfieldname": "letter_head_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "depends_on": "letter_head_name", + "fieldname": "source", + "fieldtype": "Select", + "label": "Letter Head Based On", + "options": "Image\nHTML" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "letter_head_name", + "fieldname": "disabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disabled", + "oldfieldname": "disabled", + "oldfieldtype": "Check" + }, + { + "default": "0", + "depends_on": "letter_head_name", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default Letter Head", + "oldfieldname": "is_default", + "oldfieldtype": "Check", + "search_index": 1 + }, + { + "depends_on": "eval:doc.letter_head_name && doc.source === 'Image'", + "fieldname": "letter_head_image_section", + "fieldtype": "Section Break", + "label": "Letter Head Image" + }, + { + "depends_on": "eval:doc.letter_head_name && doc.source === 'Image'", + "fieldname": "image", + "fieldtype": "Attach Image", + "label": "Image" + }, + { + "depends_on": "eval:doc.source==='HTML' && doc.letter_head_name", + "fieldname": "header_section", + "fieldtype": "Section Break", + "label": "Header" + }, + { + "depends_on": "eval:!doc.__islocal && doc.source==='HTML'", + "description": "Letter Head in HTML", + "fieldname": "content", + "fieldtype": "HTML Editor", + "label": "Header HTML", + "oldfieldname": "content", + "oldfieldtype": "Text Editor" + }, + { + "depends_on": "eval:doc.footer_source==='HTML' && doc.letter_head_name", + "fieldname": "footer_section", + "fieldtype": "Section Break", + "label": "Footer" + }, + { + "depends_on": "eval:!doc.__islocal", + "description": "Footer will display correctly only in PDF", + "fieldname": "footer", + "fieldtype": "HTML Editor", + "label": "Footer HTML" + }, + { + "default": "Left", + "fieldname": "align", + "fieldtype": "Select", + "label": "Align", + "options": "Left\nRight\nCenter" + }, + { + "fieldname": "image_height", + "fieldtype": "Float", + "label": "Image Height" + }, + { + "fieldname": "image_width", + "fieldtype": "Float", + "label": "Image Width" + }, + { + "depends_on": "eval:doc.footer_source==='Image' && doc.letter_head_name", + "fieldname": "footer_image_section", + "fieldtype": "Section Break", + "label": "Footer Image" + }, + { + "fieldname": "footer_image", + "fieldtype": "Attach Image", + "label": "Image" + }, + { + "fieldname": "footer_image_height", + "fieldtype": "Float", + "label": "Image Height" + }, + { + "fieldname": "footer_image_width", + "fieldtype": "Float", + "label": "Image Width" + }, + { + "fieldname": "footer_align", + "fieldtype": "Select", + "label": "Align", + "options": "Left\nRight\nCenter" + }, + { + "default": "HTML", + "depends_on": "letter_head_name", + "fieldname": "footer_source", + "fieldtype": "Select", + "label": "Footer Based On", + "options": "Image\nHTML" + }, + { + "depends_on": "eval:!doc.__islocal && doc.source==='HTML'", + "fieldname": "header_script", + "fieldtype": "Code", + "label": "Header Script", + "options": "Javascript" + }, + { + "depends_on": "eval:!doc.__islocal && doc.footer_source==='HTML'", + "fieldname": "footer_script", + "fieldtype": "Code", + "label": "Footer Script", + "options": "Javascript" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.header_script || doc.footer_script", + "fieldname": "scripts_section", + "fieldtype": "Section Break", + "label": "Scripts" + }, + { + "fieldname": "instructions", + "fieldtype": "HTML", + "label": "Instructions", + "read_only": 1 + } + ], + "icon": "fa fa-font", + "idx": 1, + "links": [], + "max_attachments": 3, + "modified": "2023-12-21 16:19:37.525003", + "modified_by": "Administrator", + "module": "Printing", + "name": "Letter Head", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Desk User" + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/printing/doctype/letter_head/letter_head.py b/xhiveframework/printing/doctype/letter_head/letter_head.py new file mode 100644 index 0000000..71cecde --- /dev/null +++ b/xhiveframework/printing/doctype/letter_head/letter_head.py @@ -0,0 +1,123 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import flt, is_image + + +class LetterHead(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + align: DF.Literal["Left", "Right", "Center"] + content: DF.HTMLEditor | None + disabled: DF.Check + footer: DF.HTMLEditor | None + footer_align: DF.Literal["Left", "Right", "Center"] + footer_image: DF.AttachImage | None + footer_image_height: DF.Float + footer_image_width: DF.Float + footer_source: DF.Literal["Image", "HTML"] + image: DF.AttachImage | None + image_height: DF.Float + image_width: DF.Float + is_default: DF.Check + letter_head_name: DF.Data + source: DF.Literal["Image", "HTML"] + + # end: auto-generated types + def before_insert(self): + # for better UX, let user set from attachment + self.source = "Image" + + def validate(self): + self.set_image() + self.validate_disabled_and_default() + + def validate_disabled_and_default(self): + if self.disabled and self.is_default: + xhiveframework.throw(_("Letter Head cannot be both disabled and default")) + + if not self.is_default and not self.disabled: + if not xhiveframework.db.exists("Letter Head", dict(is_default=1)): + self.is_default = 1 + + def set_image(self): + if self.source == "Image": + self.set_image_as_html( + field="image", + width="image_width", + height="image_height", + align="align", + html_field="content", + dimension_prefix="image_", + success_msg=_("Header HTML set from attachment {0}").format(self.image), + failure_msg=_("Please attach an image file to set HTML for Letter Head."), + ) + + if self.footer_source == "Image": + self.set_image_as_html( + field="footer_image", + width="footer_image_width", + height="footer_image_height", + align="footer_align", + html_field="footer", + dimension_prefix="footer_image_", + success_msg=_("Footer HTML set from attachment {0}").format(self.footer_image), + failure_msg=_("Please attach an image file to set HTML for Footer."), + ) + + def set_image_as_html( + self, field, width, height, dimension_prefix, align, html_field, success_msg, failure_msg + ): + if not self.get(field) or not is_image(self.get(field)): + xhiveframework.msgprint(failure_msg, alert=True, indicator="orange") + return + + self.set(width, flt(self.get(width))) + self.set(height, flt(self.get(height))) + + # To preserve the aspect ratio of the image, apply constraints only on + # the greater dimension and allow the other to scale accordingly + dimension = "width" if self.get(width) > self.get(height) else "height" + dimension_value = self.get(f"{dimension_prefix}{dimension}") + + if not dimension_value: + dimension_value = "" + + self.set( + html_field, + f"""
      +{self.get( +
      """, + ) + + xhiveframework.msgprint(success_msg, alert=True) + + def on_update(self): + self.set_as_default() + + # clear the cache so that the new letter head is uploaded + xhiveframework.clear_cache() + + def set_as_default(self): + from xhiveframework.utils import set_default + + if self.is_default: + xhiveframework.db.sql("update `tabLetter Head` set is_default=0 where name != %s", self.name) + + set_default("letter_head", self.name) + + # update control panel - so it loads new letter directly + xhiveframework.db.set_default("default_letter_head_content", self.content) + else: + xhiveframework.defaults.clear_default("letter_head", self.name) + xhiveframework.defaults.clear_default("default_letter_head_content", self.content) diff --git a/xhiveframework/printing/doctype/letter_head/test_letter_head.py b/xhiveframework/printing/doctype/letter_head/test_letter_head.py new file mode 100644 index 0000000..2c01d77 --- /dev/null +++ b/xhiveframework/printing/doctype/letter_head/test_letter_head.py @@ -0,0 +1,14 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestLetterHead(XhiveFrameworkTestCase): + def test_auto_image(self): + letter_head = xhiveframework.get_doc( + dict(doctype="Letter Head", letter_head_name="Test", source="Image", image="/public/test.png") + ).insert() + + # test if image is automatically set + self.assertTrue(letter_head.image in letter_head.content) diff --git a/xhiveframework/printing/doctype/network_printer_settings/__init__.py b/xhiveframework/printing/doctype/network_printer_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.js b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.js new file mode 100644 index 0000000..7af5682 --- /dev/null +++ b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.js @@ -0,0 +1,29 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Network Printer Settings", { + onload(frm) { + frm.trigger("connect_print_server"); + }, + server_ip(frm) { + frm.trigger("connect_print_server"); + }, + port(frm) { + frm.trigger("connect_print_server"); + }, + connect_print_server(frm) { + if (frm.doc.server_ip && frm.doc.port) { + xhiveframework.call({ + doc: frm.doc, + method: "get_printers_list", + args: { + ip: frm.doc.server_ip, + port: frm.doc.port, + }, + callback: function (data) { + frm.set_df_property("printer_name", "options", [""].concat(data.message)); + }, + }); + } + }, +}); diff --git a/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.json b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.json new file mode 100644 index 0000000..019d4cc --- /dev/null +++ b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.json @@ -0,0 +1,77 @@ +{ + "actions": [], + "autoname": "Prompt", + "creation": "2021-09-17 11:26:06.943999", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "server_ip", + "port", + "column_break_4", + "printer_name" + ], + "fields": [ + { + "default": "localhost", + "fieldname": "server_ip", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Server IP", + "reqd": 1 + }, + { + "default": "631", + "fieldname": "port", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Port", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "printer_name", + "fieldtype": "Select", + "label": "Printer Name", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-08-28 22:25:22.236295", + "modified_by": "Administrator", + "module": "Printing", + "name": "Network Printer Settings", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Desk User", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.py b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.py new file mode 100644 index 0000000..88d5db1 --- /dev/null +++ b/xhiveframework/printing/doctype/network_printer_settings/network_printer_settings.py @@ -0,0 +1,54 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class NetworkPrinterSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + port: DF.Int + printer_name: DF.Literal[None] + server_ip: DF.Data + + # end: auto-generated types + @xhiveframework.whitelist() + def get_printers_list(self, ip="127.0.0.1", port=631): + printer_list = [] + try: + import cups + except ImportError: + xhiveframework.throw( + _( + """This feature can not be used as dependencies are missing. + Please contact your system manager to enable this by installing pycups!""" + ) + ) + return + try: + cups.setServer(self.server_ip) + cups.setPort(self.port) + conn = cups.Connection() + printers = conn.getPrinters() + printer_list.extend( + {"value": printer_id, "label": printer["printer-make-and-model"]} + for printer_id, printer in printers.items() + ) + except RuntimeError: + xhiveframework.throw(_("Failed to connect to server")) + except xhiveframework.ValidationError: + xhiveframework.throw(_("Failed to connect to server")) + return printer_list + + +@xhiveframework.whitelist() +def get_network_printer_settings(): + return xhiveframework.db.get_list("Network Printer Settings", pluck="name") diff --git a/xhiveframework/printing/doctype/network_printer_settings/test_network_printer_settings.py b/xhiveframework/printing/doctype/network_printer_settings/test_network_printer_settings.py new file mode 100644 index 0000000..8209680 --- /dev/null +++ b/xhiveframework/printing/doctype/network_printer_settings/test_network_printer_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestNetworkPrinterSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/printing/doctype/print_format/__init__.py b/xhiveframework/printing/doctype/print_format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/print_format/print_format.js b/xhiveframework/printing/doctype/print_format/print_format.js new file mode 100644 index 0000000..31d2cca --- /dev/null +++ b/xhiveframework/printing/doctype/print_format/print_format.js @@ -0,0 +1,88 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Print Format", "onload", function (frm) { + frm.add_fetch("doc_type", "module", "module"); +}); + +xhiveframework.ui.form.on("Print Format", { + refresh: function (frm) { + frm.set_intro(""); + frm.toggle_enable(["html", "doc_type", "module"], false); + if (xhiveframework.session.user === "Administrator" || frm.doc.standard === "No") { + frm.toggle_enable(["html", "doc_type", "module"], true); + frm.enable_save(); + } + + if (frm.doc.standard === "Yes" && xhiveframework.session.user !== "Administrator") { + frm.set_intro(__("Please duplicate this to make changes")); + } + frm.trigger("render_buttons"); + frm.toggle_display("standard", xhiveframework.boot.developer_mode); + frm.trigger("hide_absolute_value_field"); + }, + render_buttons: function (frm) { + frm.page.clear_inner_toolbar(); + if (!frm.is_new()) { + if (!frm.doc.custom_format) { + frm.add_custom_button(__("Edit Format"), function () { + if (!frm.doc.doc_type) { + xhiveframework.msgprint(__("Please select DocType first")); + return; + } + if (frm.doc.print_format_builder_beta) { + xhiveframework.set_route("print-format-builder-beta", frm.doc.name); + } else { + xhiveframework.set_route("print-format-builder", frm.doc.name); + } + }); + } else if (frm.doc.custom_format && !frm.doc.raw_printing) { + frm.set_df_property("html", "reqd", 1); + } + if (xhiveframework.model.can_write("Customize Form")) { + xhiveframework.model.with_doctype(frm.doc.doc_type, function () { + let current_format = xhiveframework.get_meta(frm.doc.doc_type).default_print_format; + if (current_format == frm.doc.name) { + return; + } + + frm.add_custom_button(__("Set as Default"), function () { + xhiveframework.call({ + method: "xhiveframework.printing.doctype.print_format.print_format.make_default", + args: { + name: frm.doc.name, + }, + callback: function () { + frm.refresh(); + }, + }); + }); + }); + } + } + }, + custom_format: function (frm) { + var value = frm.doc.custom_format ? 0 : 1; + frm.set_value("align_labels_right", value); + frm.set_value("show_section_headings", value); + frm.set_value("line_breaks", value); + frm.trigger("render_buttons"); + }, + doc_type: function (frm) { + frm.trigger("hide_absolute_value_field"); + }, + hide_absolute_value_field: function (frm) { + // TODO: make it work with frm.doc.doc_type + // Problem: frm isn't updated in some random cases + const doctype = locals[frm.doc.doctype][frm.doc.name].doc_type; + if (doctype) { + xhiveframework.model.with_doctype(doctype, () => { + const meta = xhiveframework.get_meta(doctype); + const has_int_float_currency_field = meta.fields.filter((df) => + ["Int", "Float", "Currency"].includes(df.fieldtype) + ); + frm.toggle_display("absolute_value", has_int_float_currency_field.length); + }); + } + }, +}); diff --git a/xhiveframework/printing/doctype/print_format/print_format.json b/xhiveframework/printing/doctype/print_format/print_format.json new file mode 100644 index 0000000..d182cc8 --- /dev/null +++ b/xhiveframework/printing/doctype/print_format/print_format.json @@ -0,0 +1,290 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2013-01-23 19:54:43", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "doc_type", + "module", + "default_print_language", + "column_break_3", + "standard", + "custom_format", + "disabled", + "section_break_6", + "print_format_type", + "raw_printing", + "html", + "raw_commands", + "section_break_9", + "margin_top", + "margin_bottom", + "margin_left", + "margin_right", + "align_labels_right", + "show_section_headings", + "line_breaks", + "absolute_value", + "column_break_11", + "font_size", + "font", + "page_number", + "css_section", + "css", + "custom_html_help", + "section_break_13", + "print_format_help", + "format_data", + "print_format_builder", + "print_format_builder_beta" + ], + "fields": [ + { + "fieldname": "doc_type", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "No", + "fieldname": "standard", + "fieldtype": "Select", + "in_filter": 1, + "label": "Standard", + "no_copy": 1, + "oldfieldname": "standard", + "oldfieldtype": "Select", + "options": "No\nYes", + "reqd": 1, + "search_index": 1 + }, + { + "default": "0", + "fieldname": "custom_format", + "fieldtype": "Check", + "label": "Custom Format" + }, + { + "depends_on": "custom_format", + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "default": "Jinja", + "depends_on": "custom_format", + "fieldname": "print_format_type", + "fieldtype": "Select", + "label": "Print Format Type", + "options": "Jinja\nJS" + }, + { + "default": "0", + "fieldname": "raw_printing", + "fieldtype": "Check", + "label": "Raw Printing" + }, + { + "depends_on": "eval:!doc.raw_printing", + "fieldname": "html", + "fieldtype": "Code", + "label": "HTML", + "oldfieldname": "html", + "oldfieldtype": "Text Editor", + "options": "Jinja" + }, + { + "depends_on": "raw_printing", + "description": "Any string-based printer languages can be used. Writing raw commands requires knowledge of the printer's native language provided by the printer manufacturer. Please refer to the developer manual provided by the printer manufacturer on how to write their native commands. These commands are rendered on the server side using the Jinja Templating Language.", + "fieldname": "raw_commands", + "fieldtype": "Code", + "label": "Raw Commands", + "options": "Jinja" + }, + { + "depends_on": "eval:!doc.custom_format", + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Style Settings" + }, + { + "default": "0", + "fieldname": "align_labels_right", + "fieldtype": "Check", + "label": "Align Labels to the Right" + }, + { + "default": "0", + "fieldname": "show_section_headings", + "fieldtype": "Check", + "label": "Show Section Headings" + }, + { + "default": "0", + "fieldname": "line_breaks", + "fieldtype": "Check", + "label": "Show Line Breaks after Sections" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_print_language", + "fieldtype": "Link", + "label": "Default Print Language", + "options": "Language" + }, + { + "depends_on": "eval:!doc.custom_format", + "fieldname": "font", + "fieldtype": "Data", + "label": "Google Font" + }, + { + "depends_on": "eval:!doc.raw_printing", + "fieldname": "css_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "css", + "fieldtype": "Code", + "label": "Custom CSS", + "options": "CSS" + }, + { + "fieldname": "custom_html_help", + "fieldtype": "HTML", + "label": "Custom HTML Help", + "options": "

      Custom CSS Help

      \n\n

      Notes:

      \n\n
        \n
      1. All field groups (label + value) are set attributes data-fieldtype and data-fieldname
      2. \n
      3. All values are given class value
      4. \n
      5. All Section Breaks are given class section-break
      6. \n
      7. All Column Breaks are given class column-break
      8. \n
      \n\n

      Examples

      \n\n

      1. Left align integers

      \n\n
      [data-fieldtype=\"Int\"] .value { text-align: left; }
      \n\n

      1. Add border to sections except the last section

      \n\n
      .section-break { padding: 30px 0px; border-bottom: 1px solid #eee; }\n.section-break:last-child { padding-bottom: 0px; border-bottom: 0px;  }
      \n" + }, + { + "depends_on": "custom_format", + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "depends_on": "custom_format", + "fieldname": "print_format_help", + "fieldtype": "HTML", + "label": "Print Format Help", + "options": "

      Print Format Help

      \n
      \n

      Introduction

      \n

      Print Formats are rendered on the server side using the Jinja Templating Language. All forms have access to the doc object which contains information about the document that is being formatted. You can also access common utilities via the xhiveframework module.

      \n

      For styling, the Boostrap CSS framework is provided and you can enjoy the full range of classes.

      \n
      \n

      References

      \n
        \n\t
      1. Jinja Templating Language
      2. \n\t
      3. Bootstrap CSS Framework
      4. \n
      \n
      \n

      Example

      \n
      <h3>{{ doc.select_print_heading or \"Invoice\" }}</h3>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Customer Name</div>\n\t<div class=\"col-md-9\">{{ doc.customer_name }}</div>\n</div>\n<div class=\"row\">\n\t<div class=\"col-md-3 text-right\">Date</div>\n\t<div class=\"col-md-9\">{{ doc.get_formatted(\"invoice_date\") }}</div>\n</div>\n<table class=\"table table-bordered\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<th>Sr</th>\n\t\t\t<th>Item Name</th>\n\t\t\t<th>Description</th>\n\t\t\t<th class=\"text-right\">Qty</th>\n\t\t\t<th class=\"text-right\">Rate</th>\n\t\t\t<th class=\"text-right\">Amount</th>\n\t\t</tr>\n\t\t{%- for row in doc.items -%}\n\t\t<tr>\n\t\t\t<td style=\"width: 3%;\">{{ row.idx }}</td>\n\t\t\t<td style=\"width: 20%;\">\n\t\t\t\t{{ row.item_name }}\n\t\t\t\t{% if row.item_code != row.item_name -%}\n\t\t\t\t<br>Item Code: {{ row.item_code}}\n\t\t\t\t{%- endif %}\n\t\t\t</td>\n\t\t\t<td style=\"width: 37%;\">\n\t\t\t\t<div style=\"border: 0px;\">{{ row.description }}</div></td>\n\t\t\t<td style=\"width: 10%; text-align: right;\">{{ row.qty }} {{ row.uom or row.stock_uom }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"rate\", doc) }}</td>\n\t\t\t<td style=\"width: 15%; text-align: right;\">{{\n\t\t\t\trow.get_formatted(\"amount\", doc) }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>
      \n
      \n

      Common Functions

      \n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n
      doc.get_formatted(\"[fieldname]\", [parent_doc])Get document value formatted as Date, Currency, etc. Pass parent doc for currency type fields.
      xhiveframework.db.get_value(\"[doctype]\", \"[name]\", \"fieldname\")Get value from another document.
      \n" + }, + { + "fieldname": "format_data", + "fieldtype": "Code", + "hidden": 1, + "label": "Format Data" + }, + { + "default": "0", + "fieldname": "print_format_builder", + "fieldtype": "Check", + "hidden": 1, + "label": "Print Format Builder" + }, + { + "default": "0", + "depends_on": "doc_type", + "description": "If checked, negative numeric values of Currency, Quantity or Count would be shown as positive", + "fieldname": "absolute_value", + "fieldtype": "Check", + "label": "Show Absolute Values" + }, + { + "default": "0", + "fieldname": "print_format_builder_beta", + "fieldtype": "Check", + "label": "Print Format Builder Beta" + }, + { + "default": "15", + "fieldname": "margin_top", + "fieldtype": "Float", + "label": "Margin Top" + }, + { + "default": "15", + "fieldname": "margin_bottom", + "fieldtype": "Float", + "label": "Margin Bottom" + }, + { + "default": "15", + "fieldname": "margin_left", + "fieldtype": "Float", + "label": "Margin Left" + }, + { + "default": "15", + "fieldname": "margin_right", + "fieldtype": "Float", + "label": "Margin Right" + }, + { + "default": "14", + "fieldname": "font_size", + "fieldtype": "Int", + "hidden": 1, + "label": "Font Size" + }, + { + "default": "Hide", + "fieldname": "page_number", + "fieldtype": "Select", + "label": "Page Number", + "options": "Hide\nTop Left\nTop Center\nTop Right\nBottom Left\nBottom Center\nBottom Right" + } + ], + "icon": "fa fa-print", + "idx": 1, + "links": [], + "modified": "2023-12-12 19:59:37.133301", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Format", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "role": "Desk User", + "select": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/xhiveframework/printing/doctype/print_format/print_format.py b/xhiveframework/printing/doctype/print_format/print_format.py new file mode 100644 index 0000000..2168b31 --- /dev/null +++ b/xhiveframework/printing/doctype/print_format/print_format.py @@ -0,0 +1,166 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import json + +import xhiveframework +import xhiveframework.utils +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils.jinja import validate_template +from xhiveframework.utils.weasyprint import download_pdf, get_html + + +class PrintFormat(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + absolute_value: DF.Check + align_labels_right: DF.Check + css: DF.Code | None + custom_format: DF.Check + default_print_language: DF.Link | None + disabled: DF.Check + doc_type: DF.Link + font: DF.Data | None + font_size: DF.Int + format_data: DF.Code | None + html: DF.Code | None + line_breaks: DF.Check + margin_bottom: DF.Float + margin_left: DF.Float + margin_right: DF.Float + margin_top: DF.Float + module: DF.Link | None + page_number: DF.Literal[ + "Hide", "Top Left", "Top Center", "Top Right", "Bottom Left", "Bottom Center", "Bottom Right" + ] + print_format_builder: DF.Check + print_format_builder_beta: DF.Check + print_format_type: DF.Literal["Jinja", "JS"] + raw_commands: DF.Code | None + raw_printing: DF.Check + show_section_headings: DF.Check + standard: DF.Literal["No", "Yes"] + + # end: auto-generated types + + def onload(self): + templates = xhiveframework.get_all( + "Print Format Field Template", + fields=["template", "field", "name"], + filters={"document_type": self.doc_type}, + ) + self.set_onload("print_templates", templates) + + def get_html(self, docname, letterhead=None): + return get_html(self.doc_type, docname, self.name, letterhead) + + def download_pdf(self, docname, letterhead=None): + return download_pdf(self.doc_type, docname, self.name, letterhead) + + def validate(self): + if ( + self.standard == "Yes" + and not xhiveframework.local.conf.get("developer_mode") + and not (xhiveframework.flags.in_import or xhiveframework.flags.in_test) + ): + xhiveframework.throw(xhiveframework._("Standard Print Format cannot be updated")) + + # old_doc_type is required for clearing item cache + self.old_doc_type = xhiveframework.db.get_value("Print Format", self.name, "doc_type") + + self.extract_images() + + if not self.module: + self.module = xhiveframework.db.get_value("DocType", self.doc_type, "module") + + if self.html and self.print_format_type != "JS": + validate_template(self.html) + + if self.custom_format and self.raw_printing and not self.raw_commands: + xhiveframework.throw(_("{0} are required").format(xhiveframework.bold(_("Raw Commands"))), xhiveframework.MandatoryError) + + if self.custom_format and not self.html and not self.raw_printing: + xhiveframework.throw(_("{0} is required").format(xhiveframework.bold(_("HTML"))), xhiveframework.MandatoryError) + + def extract_images(self): + from xhiveframework.core.doctype.file.utils import extract_images_from_html + + if self.print_format_builder_beta: + return + + if self.format_data: + data = json.loads(self.format_data) + for df in data: + if df.get("fieldtype") and df["fieldtype"] in ("HTML", "Custom HTML") and df.get("options"): + df["options"] = extract_images_from_html(self, df["options"]) + self.format_data = json.dumps(data) + + def on_update(self): + if hasattr(self, "old_doc_type") and self.old_doc_type: + xhiveframework.clear_cache(doctype=self.old_doc_type) + if self.doc_type: + xhiveframework.clear_cache(doctype=self.doc_type) + + self.export_doc() + + def after_rename(self, old: str, new: str, *args, **kwargs): + if self.doc_type: + xhiveframework.clear_cache(doctype=self.doc_type) + + # update property setter default_print_format if set + xhiveframework.db.set_value( + "Property Setter", + { + "doctype_or_field": "DocType", + "doc_type": self.doc_type, + "property": "default_print_format", + "value": old, + }, + "value", + new, + ) + + def export_doc(self): + from xhiveframework.modules.utils import export_module_json + + return export_module_json(self, self.standard == "Yes", self.module) + + def on_trash(self): + if self.doc_type: + xhiveframework.clear_cache(doctype=self.doc_type) + + +@xhiveframework.whitelist() +def make_default(name): + """Set print format as default""" + xhiveframework.has_permission("Print Format", "write") + + print_format = xhiveframework.get_doc("Print Format", name) + + doctype = xhiveframework.get_doc("DocType", print_format.doc_type) + if doctype.custom: + doctype.default_print_format = name + doctype.save() + else: + # "Customize form" + xhiveframework.make_property_setter( + { + "doctype_or_field": "DocType", + "doctype": print_format.doc_type, + "property": "default_print_format", + "value": name, + } + ) + + xhiveframework.msgprint( + xhiveframework._("{0} is now default print format for {1} doctype").format( + xhiveframework.bold(name), xhiveframework.bold(print_format.doc_type) + ) + ) diff --git a/xhiveframework/printing/doctype/print_format/test_print_format.py b/xhiveframework/printing/doctype/print_format/test_print_format.py new file mode 100644 index 0000000..9046b99 --- /dev/null +++ b/xhiveframework/printing/doctype/print_format/test_print_format.py @@ -0,0 +1,65 @@ +# Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE +import os +import re +import unittest +from typing import TYPE_CHECKING + +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + +if TYPE_CHECKING: + from xhiveframework.printing.doctype.print_format.print_format import PrintFormat + +test_records = xhiveframework.get_test_records("Print Format") + + +class TestPrintFormat(XhiveFrameworkTestCase): + def test_print_user(self, style=None): + print_html = xhiveframework.get_print("User", "Administrator", style=style) + self.assertTrue("" in print_html) + self.assertTrue(re.findall(r'
      [\s]*administrator[\s]*
      ', print_html)) + return print_html + + def test_print_user_standard(self): + print_html = self.test_print_user("Standard") + self.assertTrue(re.findall(r"\.print-format {[\s]*font-size: 9pt;", print_html)) + self.assertFalse(re.findall(r"th {[\s]*background-color: #eee;[\s]*}", print_html)) + self.assertFalse("font-family: serif;" in print_html) + + def test_print_user_modern(self): + print_html = self.test_print_user("Modern") + self.assertTrue("/* modern format: for-test */" in print_html) + + def test_print_user_classic(self): + print_html = self.test_print_user("Classic") + self.assertTrue("/* classic format: for-test */" in print_html) + + @unittest.skipUnless( + os.access(xhiveframework.get_app_path("xhiveframework"), os.W_OK), "Only run if xhiveframework app paths is writable" + ) + def test_export_doc(self): + doc: "PrintFormat" = xhiveframework.get_doc("Print Format", test_records[0]["name"]) + + # this is only to make export_doc happy + doc.standard = "Yes" + _before = xhiveframework.conf.developer_mode + xhiveframework.conf.developer_mode = True + export_path = doc.export_doc() + xhiveframework.conf.developer_mode = _before + + exported_doc_path = f"{export_path}.json" + doc.reload() + doc_dict = doc.as_dict(no_nulls=True, convert_dates_to_str=True) + + self.assertTrue(os.path.exists(exported_doc_path)) + + with open(exported_doc_path) as f: + exported_doc = xhiveframework.parse_json(f.read()) + + for key, value in exported_doc.items(): + if key in doc_dict: + with self.subTest(key=key): + self.assertEqual(value, doc_dict[key]) + + self.addCleanup(os.remove, exported_doc_path) diff --git a/xhiveframework/printing/doctype/print_format/test_records.json b/xhiveframework/printing/doctype/print_format/test_records.json new file mode 100644 index 0000000..c772383 --- /dev/null +++ b/xhiveframework/printing/doctype/print_format/test_records.json @@ -0,0 +1,9 @@ +[ + { + "doctype": "Print Format", + "name": "_Test Print Format 1", + "module": "Core", + "doc_type": "User", + "html": "" + } +] diff --git a/xhiveframework/printing/doctype/print_format_field_template/__init__.py b/xhiveframework/printing/doctype/print_format_field_template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.js b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.js new file mode 100644 index 0000000..059dc98 --- /dev/null +++ b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.js @@ -0,0 +1,7 @@ +// Copyright (c) 2021, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Print Format Field Template", { + // refresh: function(frm) { + // } +}); diff --git a/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.json b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.json new file mode 100644 index 0000000..3b79aae --- /dev/null +++ b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.json @@ -0,0 +1,101 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2021-10-05 14:23:56.508499", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "field", + "template_file", + "column_break_3", + "module", + "standard", + "section_break_5", + "template" + ], + "fields": [ + { + "depends_on": "eval:!doc.multiple", + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "mandatory_depends_on": "eval:!doc.multiple", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "field", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Default Template For Field" + }, + { + "depends_on": "eval:!doc.standard", + "fieldname": "template", + "fieldtype": "Code", + "label": "Template", + "mandatory_depends_on": "eval:!doc.standard", + "options": "HTML" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "depends_on": "standard", + "fieldname": "module", + "fieldtype": "Link", + "label": "Module", + "options": "Module Def" + }, + { + "default": "0", + "fieldname": "standard", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Standard" + }, + { + "depends_on": "eval:doc.standard", + "fieldname": "template_file", + "fieldtype": "Data", + "label": "Template File", + "mandatory_depends_on": "eval:doc.standard" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-10-19 17:47:59.577949", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Format Field Template", + "naming_rule": "Set by user", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.py b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.py new file mode 100644 index 0000000..a7f90cd --- /dev/null +++ b/xhiveframework/printing/doctype/print_format_field_template/print_format_field_template.py @@ -0,0 +1,59 @@ +# Copyright (c) 2021, XhiveFramework Technologies and contributors +# For license information, please see license.txt + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document + + +class PrintFormatFieldTemplate(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + document_type: DF.Link + field: DF.Data | None + module: DF.Link | None + standard: DF.Check + template: DF.Code | None + template_file: DF.Data | None + + # end: auto-generated types + def validate(self): + if self.standard and not (xhiveframework.conf.developer_mode or xhiveframework.flags.in_patch): + xhiveframework.throw(_("Enable developer mode to create a standard Print Template")) + + def before_insert(self): + self.validate_duplicate() + + def on_update(self): + self.validate_duplicate() + self.export_doc() + + def validate_duplicate(self): + if not self.standard: + return + if not self.field: + return + + filters = {"document_type": self.document_type, "field": self.field} + if not self.is_new(): + filters.update({"name": ("!=", self.name)}) + result = xhiveframework.get_all("Print Format Field Template", filters=filters, limit=1) + if result: + xhiveframework.throw( + _("A template already exists for field {0} of {1}").format( + xhiveframework.bold(self.field), xhiveframework.bold(self.document_type) + ), + xhiveframework.DuplicateEntryError, + title=_("Duplicate Entry"), + ) + + def export_doc(self): + from xhiveframework.modules.utils import export_module_json + + export_module_json(self, self.standard, self.module) diff --git a/xhiveframework/printing/doctype/print_format_field_template/test_print_format_field_template.py b/xhiveframework/printing/doctype/print_format_field_template/test_print_format_field_template.py new file mode 100644 index 0000000..60af6ef --- /dev/null +++ b/xhiveframework/printing/doctype/print_format_field_template/test_print_format_field_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2021, XhiveFramework Technologies and Contributors +# See license.txt + +# import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPrintFormatFieldTemplate(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/printing/doctype/print_heading/__init__.py b/xhiveframework/printing/doctype/print_heading/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/print_heading/print_heading.js b/xhiveframework/printing/doctype/print_heading/print_heading.js new file mode 100644 index 0000000..c1507b5 --- /dev/null +++ b/xhiveframework/printing/doctype/print_heading/print_heading.js @@ -0,0 +1,6 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Print Heading", { + refresh: function (frm) {}, +}); diff --git a/xhiveframework/printing/doctype/print_heading/print_heading.json b/xhiveframework/printing/doctype/print_heading/print_heading.json new file mode 100644 index 0000000..9951b53 --- /dev/null +++ b/xhiveframework/printing/doctype/print_heading/print_heading.json @@ -0,0 +1,143 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:print_heading", + "beta": 0, + "creation": "2013-01-10 16:34:24", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "print_heading", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Print Heading", + "length": 0, + "no_copy": 0, + "oldfieldname": "print_heading", + "oldfieldtype": "Data", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "300px" + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-font", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2017-05-03 05:59:09.131569", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Heading", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "All", + "share": 0, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "search_fields": "print_heading", + "show_name_in_global_search": 0, + "sort_order": "DESC", + "track_changes": 0, + "track_seen": 0 +} diff --git a/xhiveframework/printing/doctype/print_heading/print_heading.py b/xhiveframework/printing/doctype/print_heading/print_heading.py new file mode 100644 index 0000000..099eb40 --- /dev/null +++ b/xhiveframework/printing/doctype/print_heading/print_heading.py @@ -0,0 +1,20 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class PrintHeading(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + description: DF.SmallText | None + print_heading: DF.Data + # end: auto-generated types + pass diff --git a/xhiveframework/printing/doctype/print_heading/test_print_heading.py b/xhiveframework/printing/doctype/print_heading/test_print_heading.py new file mode 100644 index 0000000..ab3b660 --- /dev/null +++ b/xhiveframework/printing/doctype/print_heading/test_print_heading.py @@ -0,0 +1,8 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPrintHeading(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/printing/doctype/print_settings/__init__.py b/xhiveframework/printing/doctype/print_settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/print_settings/print_settings.js b/xhiveframework/printing/doctype/print_settings/print_settings.js new file mode 100644 index 0000000..23012f1 --- /dev/null +++ b/xhiveframework/printing/doctype/print_settings/print_settings.js @@ -0,0 +1,23 @@ +// Copyright (c) 2018, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Print Settings", { + print_style: function (frm) { + xhiveframework.db.get_value("Print Style", frm.doc.print_style, "preview").then((r) => { + if (r.message.preview) { + frm.get_field("print_style_preview").$wrapper.html( + `` + ); + } else { + frm.get_field("print_style_preview").$wrapper.html( + `

      ${__( + "No Preview" + )}

      ` + ); + } + }); + }, + onload: function (frm) { + frm.script_manager.trigger("print_style"); + }, +}); diff --git a/xhiveframework/printing/doctype/print_settings/print_settings.json b/xhiveframework/printing/doctype/print_settings/print_settings.json new file mode 100644 index 0000000..a67440b --- /dev/null +++ b/xhiveframework/printing/doctype/print_settings/print_settings.json @@ -0,0 +1,198 @@ +{ + "actions": [], + "creation": "2014-07-17 06:54:20.782907", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "pdf_settings", + "send_print_as_pdf", + "repeat_header_footer", + "column_break_4", + "pdf_page_size", + "pdf_page_height", + "pdf_page_width", + "view_link_in_email", + "with_letterhead", + "allow_print_for_draft", + "add_draft_heading", + "column_break_10", + "allow_page_break_inside_tables", + "allow_print_for_cancelled", + "server_printer", + "enable_print_server", + "raw_printing_section", + "enable_raw_printing", + "print_style_section", + "print_style", + "print_style_preview", + "section_break_8", + "font", + "font_size" + ], + "fields": [ + { + "fieldname": "pdf_settings", + "fieldtype": "Section Break", + "label": "PDF Settings" + }, + { + "default": "1", + "description": "Send Email Print Attachments as PDF (Recommended)", + "fieldname": "send_print_as_pdf", + "fieldtype": "Check", + "label": "Send Print as PDF" + }, + { + "default": "1", + "fieldname": "repeat_header_footer", + "fieldtype": "Check", + "label": "Repeat Header and Footer" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "A4", + "fieldname": "pdf_page_size", + "fieldtype": "Select", + "label": "PDF Page Size", + "options": "A0\nA1\nA2\nA3\nA4\nA5\nA6\nA7\nA8\nA9\nB0\nB1\nB2\nB3\nB4\nB5\nB6\nB7\nB8\nB9\nB10\nC5E\nComm10E\nDLE\nExecutive\nFolio\nLedger\nLegal\nLetter\nTabloid\nCustom" + }, + { + "fieldname": "view_link_in_email", + "fieldtype": "Section Break", + "label": "Page Settings" + }, + { + "default": "1", + "fieldname": "with_letterhead", + "fieldtype": "Check", + "label": "Print with letterhead" + }, + { + "default": "1", + "fieldname": "allow_print_for_draft", + "fieldtype": "Check", + "label": "Allow Print for Draft" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "default": "1", + "fieldname": "add_draft_heading", + "fieldtype": "Check", + "label": "Always add \"Draft\" Heading for printing draft documents" + }, + { + "default": "0", + "fieldname": "allow_page_break_inside_tables", + "fieldtype": "Check", + "label": "Allow page break inside tables" + }, + { + "default": "0", + "fieldname": "allow_print_for_cancelled", + "fieldtype": "Check", + "label": "Allow Print for Cancelled" + }, + { + "fieldname": "server_printer", + "fieldtype": "Section Break", + "label": "Print Server" + }, + { + "default": "0", + "depends_on": "enable_print_server", + "fieldname": "enable_print_server", + "fieldtype": "Check", + "label": "Enable Print Server", + "mandatory_depends_on": "enable_print_server" + }, + { + "fieldname": "raw_printing_section", + "fieldtype": "Section Break", + "label": "Raw Printing" + }, + { + "default": "0", + "fieldname": "enable_raw_printing", + "fieldtype": "Check", + "label": "Enable Raw Printing" + }, + { + "fieldname": "print_style_section", + "fieldtype": "Section Break", + "label": "Print Style" + }, + { + "default": "Redesign", + "fieldname": "print_style", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Print Style", + "options": "Print Style" + }, + { + "fieldname": "print_style_preview", + "fieldtype": "HTML", + "label": "Print Style Preview" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Fonts" + }, + { + "default": "Default", + "fieldname": "font", + "fieldtype": "Select", + "label": "Font", + "options": "Default\nHelvetica Neue\nArial\nHelvetica\nInter\nVerdana\nMonospace" + }, + { + "description": "In points. Default is 9.", + "fieldname": "font_size", + "fieldtype": "Float", + "label": "Font Size" + }, + { + "depends_on": "eval:doc.pdf_page_size == \"Custom\"", + "fieldname": "pdf_page_height", + "fieldtype": "Float", + "label": "PDF Page Height (in mm)" + }, + { + "depends_on": "eval:doc.pdf_page_size == \"Custom\"", + "fieldname": "pdf_page_width", + "fieldtype": "Float", + "label": "PDF Page Width (in mm)" + } + ], + "icon": "fa fa-cog", + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2023-05-30 14:55:25.740691", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/printing/doctype/print_settings/print_settings.py b/xhiveframework/printing/doctype/print_settings/print_settings.py new file mode 100644 index 0000000..e587ff1 --- /dev/null +++ b/xhiveframework/printing/doctype/print_settings/print_settings.py @@ -0,0 +1,83 @@ +# Copyright (c) 2018, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework import _ +from xhiveframework.model.document import Document +from xhiveframework.utils import cint + + +class PrintSettings(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + add_draft_heading: DF.Check + allow_page_break_inside_tables: DF.Check + allow_print_for_cancelled: DF.Check + allow_print_for_draft: DF.Check + enable_print_server: DF.Check + enable_raw_printing: DF.Check + font: DF.Literal["Default", "Helvetica Neue", "Arial", "Helvetica", "Inter", "Verdana", "Monospace"] + font_size: DF.Float + pdf_page_height: DF.Float + pdf_page_size: DF.Literal[ + "A0", + "A1", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "B0", + "B1", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B9", + "B10", + "C5E", + "Comm10E", + "DLE", + "Executive", + "Folio", + "Ledger", + "Legal", + "Letter", + "Tabloid", + "Custom", + ] + pdf_page_width: DF.Float + print_style: DF.Link | None + repeat_header_footer: DF.Check + send_print_as_pdf: DF.Check + with_letterhead: DF.Check + + # end: auto-generated types + def validate(self): + if self.pdf_page_size == "Custom" and not (self.pdf_page_height and self.pdf_page_width): + xhiveframework.throw(_("Page height and width cannot be zero")) + + def on_update(self): + xhiveframework.clear_cache() + + +@xhiveframework.whitelist() +def is_print_server_enabled(): + if not hasattr(xhiveframework.local, "enable_print_server"): + xhiveframework.local.enable_print_server = cint( + xhiveframework.db.get_single_value("Print Settings", "enable_print_server") + ) + + return xhiveframework.local.enable_print_server diff --git a/xhiveframework/printing/doctype/print_settings/test_print_settings.py b/xhiveframework/printing/doctype/print_settings/test_print_settings.py new file mode 100644 index 0000000..1c18f35 --- /dev/null +++ b/xhiveframework/printing/doctype/print_settings/test_print_settings.py @@ -0,0 +1,7 @@ +# Copyright (c) 2018, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPrintSettings(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/printing/doctype/print_style/__init__.py b/xhiveframework/printing/doctype/print_style/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/doctype/print_style/print_style.js b/xhiveframework/printing/doctype/print_style/print_style.js new file mode 100644 index 0000000..7d0ba8c --- /dev/null +++ b/xhiveframework/printing/doctype/print_style/print_style.js @@ -0,0 +1,10 @@ +// Copyright (c) 2017, XhiveFramework Technologies and contributors +// For license information, please see license.txt + +xhiveframework.ui.form.on("Print Style", { + refresh: function (frm) { + frm.add_custom_button(__("Print Settings"), () => { + xhiveframework.set_route("Form", "Print Settings"); + }); + }, +}); diff --git a/xhiveframework/printing/doctype/print_style/print_style.json b/xhiveframework/printing/doctype/print_style/print_style.json new file mode 100644 index 0000000..1d3c9a6 --- /dev/null +++ b/xhiveframework/printing/doctype/print_style/print_style.json @@ -0,0 +1,75 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:print_style_name", + "creation": "2017-08-17 01:25:56.910716", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "print_style_name", + "disabled", + "standard", + "css", + "preview" + ], + "fields": [ + { + "fieldname": "print_style_name", + "fieldtype": "Data", + "label": "Print Style Name", + "reqd": 1, + "unique": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disabled" + }, + { + "default": "0", + "fieldname": "standard", + "fieldtype": "Check", + "label": "Standard" + }, + { + "fieldname": "css", + "fieldtype": "Code", + "label": "CSS", + "reqd": 1 + }, + { + "fieldname": "preview", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Preview" + } + ], + "image_field": "preview", + "links": [], + "modified": "2022-08-03 12:20:51.295775", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Style", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/xhiveframework/printing/doctype/print_style/print_style.py b/xhiveframework/printing/doctype/print_style/print_style.py new file mode 100644 index 0000000..9f331ce --- /dev/null +++ b/xhiveframework/printing/doctype/print_style/print_style.py @@ -0,0 +1,39 @@ +# Copyright (c) 2017, XhiveFramework Technologies and contributors +# License: MIT. See LICENSE + +import xhiveframework +from xhiveframework.model.document import Document + + +class PrintStyle(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from xhiveframework.types import DF + + css: DF.Code + disabled: DF.Check + preview: DF.AttachImage | None + print_style_name: DF.Data + standard: DF.Check + + # end: auto-generated types + def validate(self): + if ( + self.standard == 1 + and not xhiveframework.local.conf.get("developer_mode") + and not (xhiveframework.flags.in_import or xhiveframework.flags.in_test) + ): + xhiveframework.throw(xhiveframework._("Standard Print Style cannot be changed. Please duplicate to edit.")) + + def on_update(self): + self.export_doc() + + def export_doc(self): + # export + from xhiveframework.modules.utils import export_module_json + + export_module_json(self, self.standard == 1, "Printing") diff --git a/xhiveframework/printing/doctype/print_style/test_print_style.py b/xhiveframework/printing/doctype/print_style/test_print_style.py new file mode 100644 index 0000000..c3d6592 --- /dev/null +++ b/xhiveframework/printing/doctype/print_style/test_print_style.py @@ -0,0 +1,8 @@ +# Copyright (c) 2017, XhiveFramework Technologies and Contributors +# License: MIT. See LICENSE +import xhiveframework +from xhiveframework.tests.utils import XhiveFrameworkTestCase + + +class TestPrintStyle(XhiveFrameworkTestCase): + pass diff --git a/xhiveframework/printing/form_tour/letter_head/letter_head.json b/xhiveframework/printing/form_tour/letter_head/letter_head.json new file mode 100644 index 0000000..66730b4 --- /dev/null +++ b/xhiveframework/printing/form_tour/letter_head/letter_head.json @@ -0,0 +1,53 @@ +{ + "creation": "2021-11-22 15:26:53.878805", + "docstatus": 0, + "doctype": "Form Tour", + "idx": 0, + "is_standard": 1, + "modified": "2021-11-22 15:26:53.878805", + "modified_by": "Administrator", + "module": "Printing", + "name": "Letter Head", + "owner": "Administrator", + "reference_doctype": "Letter Head", + "save_on_complete": 1, + "steps": [ + { + "description": "Let's name your first Letter Head with your company's name", + "field": "", + "fieldname": "letter_head_name", + "fieldtype": "Data", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Letter Head Name", + "parent_field": "", + "position": "Right", + "title": "Letter Head Name" + }, + { + "description": "Select the image containing only header part of your letter Head.", + "field": "", + "fieldname": "image", + "fieldtype": "Attach Image", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Image", + "parent_field": "", + "position": "Right", + "title": "Image" + }, + { + "description": "You can mark the Letter Head as default", + "field": "", + "fieldname": "is_default", + "fieldtype": "Check", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Default Letter Head", + "parent_field": "", + "position": "Right", + "title": "Default Letter Head" + } + ], + "title": "Letter Head" +} \ No newline at end of file diff --git a/xhiveframework/printing/form_tour/print_format/print_format.json b/xhiveframework/printing/form_tour/print_format/print_format.json new file mode 100644 index 0000000..0f2b303 --- /dev/null +++ b/xhiveframework/printing/form_tour/print_format/print_format.json @@ -0,0 +1,94 @@ +{ + "creation": "2021-11-24 17:31:44.978996", + "docstatus": 0, + "doctype": "Form Tour", + "first_document": 0, + "idx": 0, + "include_name_field": 1, + "is_standard": 1, + "modified": "2021-11-24 17:58:00.807972", + "modified_by": "Administrator", + "module": "Printing", + "name": "Print Format", + "owner": "Administrator", + "reference_doctype": "Print Format", + "save_on_complete": 1, + "steps": [ + { + "description": "Select a Doctype for which you want to create a Print Format", + "field": "", + "fieldname": "doc_type", + "fieldtype": "Link", + "has_next_condition": 0, + "is_table_field": 0, + "label": "DocType", + "parent_field": "", + "position": "Right", + "title": "Doctype" + }, + { + "description": "You can modify the style of the Print Format from this section", + "field": "", + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Style Settings", + "parent_field": "", + "position": "Top", + "title": "Style Settings" + }, + { + "description": "You can add custom css for your Print Format from this section", + "field": "", + "fieldname": "css", + "fieldtype": "Code", + "has_next_condition": 0, + "is_table_field": 0, + "label": "Custom CSS", + "parent_field": "", + "position": "Top", + "title": "Custom CSS" + }, + { + "description": "Check this if you want to add custom Jinja Code or JavaScript to your Print Format", + "field": "", + "fieldname": "custom_format", + "fieldtype": "Check", + "has_next_condition": 1, + "is_table_field": 0, + "label": "Custom Format", + "next_step_condition": "eval: doc.custom_format", + "parent_field": "", + "position": "Left", + "title": "Custom Format" + }, + { + "description": "Select the type of Print Format", + "field": "", + "fieldname": "print_format_type", + "fieldtype": "Select", + "has_next_condition": 1, + "is_table_field": 0, + "label": "Print Format Type", + "next_step_condition": "eval: doc.custom_format", + "parent_field": "", + "position": "Right", + "title": "Print Format Type" + }, + { + "description": "Enter the code based on the Print Format Type you selected above", + "field": "", + "fieldname": "html", + "fieldtype": "Code", + "has_next_condition": 1, + "is_table_field": 0, + "label": "HTML", + "next_step_condition": "eval:doc.html", + "parent_field": "", + "position": "Right", + "title": "Code" + } + ], + "title": "Print Format" +} \ No newline at end of file diff --git a/xhiveframework/printing/page/__init__.py b/xhiveframework/printing/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/page/print/__init__.py b/xhiveframework/printing/page/print/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/page/print/print.js b/xhiveframework/printing/page/print/print.js new file mode 100644 index 0000000..9706eb1 --- /dev/null +++ b/xhiveframework/printing/page/print/print.js @@ -0,0 +1,846 @@ +xhiveframework.pages["print"].on_page_load = function (wrapper) { + xhiveframework.ui.make_app_page({ + parent: wrapper, + }); + + let print_view = new xhiveframework.ui.form.PrintView(wrapper); + + $(wrapper).bind("show", () => { + const route = xhiveframework.get_route(); + const doctype = route[1]; + const docname = route.slice(2).join("/"); + if (!xhiveframework.route_options || !xhiveframework.route_options.frm) { + xhiveframework.model.with_doc(doctype, docname, () => { + let frm = { doctype: doctype, docname: docname }; + frm.doc = xhiveframework.get_doc(doctype, docname); + xhiveframework.model.with_doctype(doctype, () => { + frm.meta = xhiveframework.get_meta(route[1]); + print_view.show(frm); + }); + }); + } else { + print_view.frm = xhiveframework.route_options.frm.doctype + ? xhiveframework.route_options.frm + : xhiveframework.route_options.frm.frm; + xhiveframework.route_options.frm = null; + print_view.show(print_view.frm); + } + }); +}; + +xhiveframework.ui.form.PrintView = class { + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + this.make(); + } + + make() { + this.print_wrapper = this.page.main.empty().html( + ` +
      + +
      + ` + ); + + this.print_settings = xhiveframework.model.get_doc(":Print Settings", "Print Settings"); + this.setup_menu(); + this.setup_toolbar(); + this.setup_sidebar(); + this.setup_keyboard_shortcuts(); + } + + set_title() { + this.page.set_title(this.frm.docname); + } + + setup_toolbar() { + this.page.set_primary_action(__("Print"), () => this.printit(), "printer"); + + this.page.add_button(__("Full Page"), () => this.render_page("/printview?"), { + icon: "full-page", + }); + + this.page.add_button(__("PDF"), () => this.render_pdf(), { icon: "small-file" }); + + this.page.add_button(__("Refresh"), () => this.refresh_print_format(), { + icon: "refresh", + }); + + this.page.add_action_icon( + "es-line-filetype", + () => { + this.go_to_form_view(); + }, + "", + __("Form") + ); + } + + setup_sidebar() { + this.sidebar = this.page.sidebar.addClass("print-preview-sidebar"); + + this.print_format_selector = this.add_sidebar_item({ + fieldtype: "Link", + fieldname: "print_format", + options: "Print Format", + label: __("Print Format"), + get_query: () => { + return { filters: { doc_type: this.frm.doctype } }; + }, + change: () => this.refresh_print_format(), + }).$input; + + this.language_selector = this.add_sidebar_item({ + fieldtype: "Link", + fieldname: "language", + label: __("Language"), + options: "Language", + change: () => { + this.set_user_lang(); + this.preview(); + }, + }).$input; + + let description = ""; + if (!cint(this.print_settings.repeat_header_footer)) { + description = + "
      " + + __("Footer might not be visible as {0} option is disabled
      ", [ + `${__( + "Repeat Header and Footer" + )}`, + ]); + } + const print_view = this; + this.letterhead_selector = this.add_sidebar_item({ + fieldtype: "Link", + fieldname: "letterhead", + options: "Letter Head", + label: __("Letter Head"), + description: description, + change: function () { + this.set_description(this.get_value() ? description : ""); + print_view.preview(); + }, + }).$input; + this.sidebar_dynamic_section = $(`
      `).appendTo( + this.sidebar + ); + } + + add_sidebar_item(df, is_dynamic) { + if (df.fieldtype == "Select") { + df.input_class = "btn btn-default btn-sm text-left"; + } + + let field = xhiveframework.ui.form.make_control({ + df: df, + parent: is_dynamic ? this.sidebar_dynamic_section : this.sidebar, + render_input: 1, + }); + + if (df.default != null) { + field.set_input(df.default); + } + + return field; + } + + setup_menu() { + this.page.clear_menu(); + + this.page.add_menu_item(__("Print Settings"), () => { + xhiveframework.set_route("Form", "Print Settings"); + }); + + if (this.print_settings.enable_raw_printing == "1") { + this.page.add_menu_item(__("Raw Printing Setting"), () => { + this.printer_setting_dialog(); + }); + } + + if (xhiveframework.model.can_create("Print Format")) { + this.page.add_menu_item(__("Customize"), () => this.edit_print_format()); + } + + if (cint(this.print_settings.enable_print_server)) { + this.page.add_menu_item(__("Select Network Printer"), () => + this.network_printer_setting_dialog() + ); + } + } + + show(frm) { + this.frm = frm; + this.set_title(); + this.set_breadcrumbs(); + this.setup_customize_dialog(); + + // print designer link + if (Object.keys(xhiveframework.boot.versions).includes("print_designer")) { + this.page.add_inner_message(` + + ${__("Try the new Print Designer")} + + `); + } else { + this.page.add_inner_message(` + + ${__("Try the new Print Designer")} + + `); + } + let tasks = [ + this.set_default_print_format, + this.set_default_print_language, + this.set_default_letterhead, + this.preview, + ].map((fn) => fn.bind(this)); + + this.setup_additional_settings(); + return xhiveframework.run_serially(tasks); + } + + set_breadcrumbs() { + xhiveframework.breadcrumbs.add(this.frm.meta.module, this.frm.doctype); + } + + setup_additional_settings() { + this.additional_settings = {}; + this.sidebar_dynamic_section.empty(); + xhiveframework + .xcall("xhiveframework.printing.page.print.print.get_print_settings_to_show", { + doctype: this.frm.doc.doctype, + docname: this.frm.doc.name, + }) + .then((settings) => this.add_settings_to_sidebar(settings)); + } + + add_settings_to_sidebar(settings) { + for (let df of settings) { + let field = this.add_sidebar_item( + { + ...df, + change: () => { + const val = field.get_value(); + this.additional_settings[field.df.fieldname] = val; + this.preview(); + }, + }, + true + ); + } + } + + edit_print_format() { + let print_format = this.get_print_format(); + let is_custom_format = + print_format.name && + (print_format.print_format_builder || print_format.print_format_builder_beta) && + print_format.standard === "No"; + let is_standard_but_editable = print_format.name && print_format.custom_format; + + if (is_standard_but_editable) { + xhiveframework.set_route("Form", "Print Format", print_format.name); + return; + } + if (is_custom_format) { + if (print_format.print_format_builder_beta) { + xhiveframework.set_route("print-format-builder-beta", print_format.name); + } else { + xhiveframework.set_route("print-format-builder", print_format.name); + } + return; + } + // start a new print format + xhiveframework.prompt( + [ + { + label: __("New Print Format Name"), + fieldname: "print_format_name", + fieldtype: "Data", + reqd: 1, + }, + { + label: __("Based On"), + fieldname: "based_on", + fieldtype: "Read Only", + default: print_format.name || "Standard", + }, + { + label: __("Use the new Print Format Builder"), + fieldname: "beta", + fieldtype: "Check", + }, + ], + (data) => { + xhiveframework.route_options = { + make_new: true, + doctype: this.frm.doctype, + name: data.print_format_name, + based_on: data.based_on, + beta: data.beta, + }; + xhiveframework.set_route("print-format-builder"); + this.print_format_selector.val(data.print_format_name); + }, + __("New Custom Print Format"), + __("Start") + ); + } + + refresh_print_format() { + this.set_default_print_language(); + this.toggle_raw_printing(); + this.preview(); + } + + // bind_events () { + // // // hide print view on pressing escape, only if there is no focus on any input + // // $(document).on("keydown", function (e) { + // // if (e.which === 27 && me.frm && e.target === document.body) { + // // me.hide(); + // // } + // // }); + // } + + setup_customize_dialog() { + let print_format = this.get_print_format(); + $(document).on("new-print-format", (e) => { + xhiveframework.prompt( + [ + { + label: __("New Print Format Name"), + fieldname: "print_format_name", + fieldtype: "Data", + reqd: 1, + }, + { + label: __("Based On"), + fieldname: "based_on", + fieldtype: "Read Only", + default: print_format.name || "Standard", + }, + ], + (data) => { + xhiveframework.route_options = { + make_new: true, + doctype: this.frm.doctype, + name: data.print_format_name, + based_on: data.based_on, + }; + xhiveframework.set_route("print-format-builder"); + }, + __("New Custom Print Format"), + __("Start") + ); + }); + } + + setup_keyboard_shortcuts() { + this.wrapper.find(".print-toolbar a.btn-default").each((i, el) => { + xhiveframework.ui.keys.get_shortcut_group(this.frm.page).add($(el)); + }); + } + + set_default_letterhead() { + if (this.frm.doc.letter_head) { + this.letterhead_selector.val(this.frm.doc.letter_head); + return; + } + + return xhiveframework.db + .get_value("Letter Head", { disabled: 0, is_default: 1 }, "name") + .then(({ message }) => this.letterhead_selector.val(message.name)); + } + + set_user_lang() { + this.lang_code = this.language_selector.val(); + } + + set_default_print_language() { + let print_format = this.get_print_format(); + this.lang_code = + this.frm.doc.language || print_format.default_print_language || xhiveframework.boot.lang; + this.language_selector.val(this.lang_code); + } + + toggle_raw_printing() { + const is_raw_printing = this.is_raw_printing(); + this.wrapper.find(".btn-print-preview").toggle(!is_raw_printing); + this.wrapper.find(".btn-download-pdf").toggle(!is_raw_printing); + } + + preview() { + let print_format = this.get_print_format(); + if (print_format.print_format_builder_beta) { + this.print_wrapper.find(".print-preview-wrapper").hide(); + this.print_wrapper.find(".preview-beta-wrapper").show(); + this.preview_beta(); + return; + } + + this.print_wrapper.find(".preview-beta-wrapper").hide(); + this.print_wrapper.find(".print-preview-wrapper").show(); + + const $print_format = this.print_wrapper.find("iframe"); + this.$print_format_body = $print_format.contents(); + this.get_print_html((out) => { + if (!out.html) { + out.html = this.get_no_preview_html(); + } + + this.setup_print_format_dom(out, $print_format); + + const print_height = $print_format.get(0).offsetHeight; + const $message = this.wrapper.find(".page-break-message"); + + const print_height_inches = xhiveframework.dom.pixel_to_inches(print_height); + // if contents are large enough, indicate that it will get printed on multiple pages + // Maximum height for an A4 document is 11.69 inches + if (print_height_inches > 11.69) { + $message.text(__("This may get printed on multiple pages")); + } else { + $message.text(""); + } + }); + } + + preview_beta() { + let print_format = this.get_print_format(); + const iframe = this.print_wrapper.find(".preview-beta-wrapper iframe"); + let params = new URLSearchParams({ + doctype: this.frm.doc.doctype, + name: this.frm.doc.name, + print_format: print_format.name, + }); + let letterhead = this.get_letterhead(); + if (letterhead) { + params.append("letterhead", letterhead); + } + iframe.prop("src", `/printpreview?${params.toString()}`); + } + + setup_print_format_dom(out, $print_format) { + this.print_wrapper.find(".print-format-skeleton").remove(); + let base_url = xhiveframework.urllib.get_base_url(); + let print_css = xhiveframework.assets.bundled_asset( + "print.bundle.css", + xhiveframework.utils.is_rtl(this.lang_code) + ); + this.$print_format_body + .find("html") + .attr("dir", xhiveframework.utils.is_rtl(this.lang_code) ? "rtl" : "ltr"); + this.$print_format_body.find("html").attr("lang", this.lang_code); + this.$print_format_body.find("head").html( + ` + ` + ); + + this.$print_format_body + .find("body") + .html(``); + + this.show_footer(); + + this.$print_format_body.find(".print-format").css({ + display: "flex", + flexDirection: "column", + }); + + this.$print_format_body.find(".page-break").css({ + display: "flex", + "flex-direction": "column", + flex: "1", + }); + + setTimeout(() => { + $print_format.height(this.$print_format_body.find(".print-format").outerHeight()); + }, 500); + } + + hide() { + if (this.frm.setup_done && this.frm.page.current_view_name === "print") { + this.frm.page.set_view( + this.frm.page.previous_view_name === "print" + ? "main" + : this.frm.page.previous_view_name || "main" + ); + } + } + + go_to_form_view() { + xhiveframework.route_options = { + frm: this, + }; + xhiveframework.set_route("Form", this.frm.doctype, this.frm.docname); + } + + show_footer() { + // footer is hidden by default as reqd by pdf generation + // simple hack to show it in print preview + + this.$print_format_body.find("#footer-html").attr( + "style", + ` + display: block !important; + order: 1; + margin-top: auto; + padding-top: var(--padding-xl) + ` + ); + } + + printit() { + let me = this; + + if (cint(me.print_settings.enable_print_server)) { + if (localStorage.getItem("network_printer")) { + me.print_by_server(); + } else { + me.network_printer_setting_dialog(() => me.print_by_server()); + } + } else if (me.get_mapped_printer().length === 1) { + // printer is already mapped in localstorage (applies for both raw and pdf ) + if (me.is_raw_printing()) { + me.get_raw_commands(function (out) { + xhiveframework.ui.form + .qz_connect() + .then(function () { + let printer_map = me.get_mapped_printer()[0]; + let data = [out.raw_commands]; + let config = qz.configs.create(printer_map.printer); + return qz.print(config, data); + }) + .then(xhiveframework.ui.form.qz_success) + .catch((err) => { + xhiveframework.ui.form.qz_fail(err); + }); + }); + } else { + xhiveframework.show_alert( + { + message: __('PDF printing via "Raw Print" is not supported.'), + subtitle: __( + "Please remove the printer mapping in Printer Settings and try again." + ), + indicator: "info", + }, + 14 + ); + //Note: need to solve "Error: Cannot parse (FILE) as a PDF file" to enable qz pdf printing. + } + } else if (me.is_raw_printing()) { + // printer not mapped in localstorage and the current print format is raw printing + xhiveframework.show_alert( + { + message: __("Printer mapping not set."), + subtitle: __( + "Please set a printer mapping for this print format in the Printer Settings" + ), + indicator: "warning", + }, + 14 + ); + me.printer_setting_dialog(); + } else { + me.render_page("/printview?", true); + } + } + + print_by_server() { + let me = this; + if (localStorage.getItem("network_printer")) { + xhiveframework.call({ + method: "xhiveframework.utils.print_format.print_by_server", + args: { + doctype: me.frm.doc.doctype, + name: me.frm.doc.name, + printer_setting: localStorage.getItem("network_printer"), + print_format: me.selected_format(), + no_letterhead: me.with_letterhead(), + letterhead: me.get_letterhead(), + }, + callback: function () {}, + }); + } + } + network_printer_setting_dialog(callback) { + xhiveframework.call({ + method: "xhiveframework.printing.doctype.network_printer_settings.network_printer_settings.get_network_printer_settings", + callback: function (r) { + if (r.message) { + let d = new xhiveframework.ui.Dialog({ + title: __("Select Network Printer"), + fields: [ + { + label: "Printer", + fieldname: "printer", + fieldtype: "Select", + reqd: 1, + options: r.message, + }, + ], + primary_action: function () { + localStorage.setItem("network_printer", d.get_values().printer); + if (typeof callback == "function") { + callback(); + } + d.hide(); + }, + primary_action_label: __("Select"), + }); + d.show(); + } + }, + }); + } + + render_pdf() { + let print_format = this.get_print_format(); + if (print_format.print_format_builder_beta) { + let params = new URLSearchParams({ + doctype: this.frm.doc.doctype, + name: this.frm.doc.name, + print_format: print_format.name, + letterhead: this.get_letterhead(), + }); + let w = window.open(`/api/method/xhiveframework.utils.weasyprint.download_pdf?${params}`); + if (!w) { + xhiveframework.msgprint(__("Please enable pop-ups")); + return; + } + } else { + this.render_page("/api/method/xhiveframework.utils.print_format.download_pdf?"); + } + } + + render_page(method, printit = false) { + let w = window.open( + xhiveframework.urllib.get_full_url( + method + + "doctype=" + + encodeURIComponent(this.frm.doc.doctype) + + "&name=" + + encodeURIComponent(this.frm.doc.name) + + (printit ? "&trigger_print=1" : "") + + "&format=" + + encodeURIComponent(this.selected_format()) + + "&no_letterhead=" + + (this.with_letterhead() ? "0" : "1") + + "&letterhead=" + + encodeURIComponent(this.get_letterhead()) + + "&settings=" + + encodeURIComponent(JSON.stringify(this.additional_settings)) + + (this.lang_code ? "&_lang=" + this.lang_code : "") + ) + ); + if (!w) { + xhiveframework.msgprint(__("Please enable pop-ups")); + return; + } + } + + get_print_html(callback) { + let print_format = this.get_print_format(); + if (print_format.raw_printing) { + callback({ + html: this.get_no_preview_html(), + }); + return; + } + if (this._req) { + this._req.abort(); + } + this._req = xhiveframework.call({ + method: "xhiveframework.www.printview.get_html_and_style", + args: { + doc: this.frm.doc, + print_format: this.selected_format(), + no_letterhead: !this.with_letterhead() ? 1 : 0, + letterhead: this.get_letterhead(), + settings: this.additional_settings, + _lang: this.lang_code, + }, + callback: function (r) { + if (!r.exc) { + callback(r.message); + } + }, + }); + } + + get_letterhead() { + return this.letterhead_selector.val() || __("No Letterhead"); + } + + get_no_preview_html() { + return `
      + ${__("No Preview Available")} +
      `; + } + + get_raw_commands(callback) { + // fetches rendered raw commands from the server for the current print format. + xhiveframework.call({ + method: "xhiveframework.www.printview.get_rendered_raw_commands", + args: { + doc: this.frm.doc, + print_format: this.selected_format(), + _lang: this.lang_code, + }, + callback: function (r) { + if (!r.exc) { + callback(r.message); + } + }, + }); + } + + get_mapped_printer() { + // returns a list of "print format: printer" mapping filtered by the current print format + let print_format_printer_map = this.get_print_format_printer_map(); + if (print_format_printer_map[this.frm.doctype]) { + return print_format_printer_map[this.frm.doctype].filter( + (printer_map) => printer_map.print_format == this.selected_format() + ); + } else { + return []; + } + } + + get_print_format_printer_map() { + // returns the whole object "print_format_printer_map" stored in the localStorage. + try { + return JSON.parse(localStorage.print_format_printer_map); + } catch (e) { + return {}; + } + } + + set_default_print_format() { + if ( + xhiveframework.meta + .get_print_formats(this.frm.doctype) + .includes(this.print_format_selector.val()) || + !this.frm.meta.default_print_format + ) + return; + + this.print_format_selector.empty(); + this.print_format_selector.val(this.frm.meta.default_print_format); + } + + selected_format() { + return this.print_format_selector.val() || "Standard"; + } + + is_raw_printing(format) { + return this.get_print_format(format).raw_printing === 1; + } + + get_print_format(format) { + let print_format = {}; + if (!format) { + format = this.selected_format(); + } + + if (locals["Print Format"] && locals["Print Format"][format]) { + print_format = locals["Print Format"][format]; + } + + return print_format; + } + + with_letterhead() { + return cint(this.get_letterhead() !== __("No Letterhead")); + } + + set_style(style) { + xhiveframework.dom.set_style(style || xhiveframework.boot.print_css, "print-style"); + } + + printer_setting_dialog() { + // dialog for the Printer Settings + this.print_format_printer_map = this.get_print_format_printer_map(); + this.data = this.print_format_printer_map[this.frm.doctype] || []; + this.printer_list = []; + xhiveframework.ui.form.qz_get_printer_list().then((data) => { + this.printer_list = data; + const dialog = new xhiveframework.ui.Dialog({ + title: __("Printer Settings"), + fields: [ + { + fieldtype: "Section Break", + }, + { + fieldname: "printer_mapping", + fieldtype: "Table", + label: __("Printer Mapping"), + in_place_edit: true, + data: this.data, + get_data: () => { + return this.data; + }, + fields: [ + { + fieldtype: "Select", + fieldname: "print_format", + default: 0, + options: xhiveframework.meta.get_print_formats(this.frm.doctype), + read_only: 0, + in_list_view: 1, + label: __("Print Format"), + }, + { + fieldtype: "Select", + fieldname: "printer", + default: 0, + options: this.printer_list, + read_only: 0, + in_list_view: 1, + label: __("Printer"), + }, + ], + }, + ], + primary_action: () => { + let printer_mapping = dialog.get_values()["printer_mapping"]; + if (printer_mapping && printer_mapping.length) { + let print_format_list = printer_mapping.map((a) => a.print_format); + let has_duplicate = print_format_list.some( + (item, idx) => print_format_list.indexOf(item) != idx + ); + if (has_duplicate) + xhiveframework.throw( + __( + "Cannot have multiple printers mapped to a single print format." + ) + ); + } else { + printer_mapping = []; + } + dialog.print_format_printer_map = this.get_print_format_printer_map(); + dialog.print_format_printer_map[this.frm.doctype] = printer_mapping; + localStorage.print_format_printer_map = JSON.stringify( + dialog.print_format_printer_map + ); + dialog.hide(); + }, + primary_action_label: __("Save"), + }); + dialog.show(); + if (!(this.printer_list && this.printer_list.length)) { + xhiveframework.throw(__("No Printer is Available.")); + } + }); + } +}; diff --git a/xhiveframework/printing/page/print/print.json b/xhiveframework/printing/page/print/print.json new file mode 100644 index 0000000..bea659c --- /dev/null +++ b/xhiveframework/printing/page/print/print.json @@ -0,0 +1,18 @@ +{ + "content": null, + "creation": "2020-10-09 17:23:15.163030", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2020-10-09 17:23:15.163030", + "modified_by": "Administrator", + "module": "Printing", + "name": "print", + "owner": "Administrator", + "page_name": "Print", + "roles": [], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0 +} \ No newline at end of file diff --git a/xhiveframework/printing/page/print/print.py b/xhiveframework/printing/page/print/print.py new file mode 100644 index 0000000..7dd333c --- /dev/null +++ b/xhiveframework/printing/page/print/print.py @@ -0,0 +1,22 @@ +import xhiveframework + + +@xhiveframework.whitelist() +def get_print_settings_to_show(doctype, docname): + doc = xhiveframework.get_doc(doctype, docname) + print_settings = xhiveframework.get_single("Print Settings") + + if hasattr(doc, "get_print_settings"): + fields = doc.get_print_settings() or [] + else: + return [] + + print_settings_fields = [] + for fieldname in fields: + df = print_settings.meta.get_field(fieldname) + if not df: + continue + df.default = print_settings.get(fieldname) + print_settings_fields.append(df) + + return print_settings_fields diff --git a/xhiveframework/printing/page/print/print_skeleton_loading.html b/xhiveframework/printing/page/print/print_skeleton_loading.html new file mode 100644 index 0000000..c1e6a0d --- /dev/null +++ b/xhiveframework/printing/page/print/print_skeleton_loading.html @@ -0,0 +1,164 @@ + diff --git a/xhiveframework/printing/page/print_format_builder/__init__.py b/xhiveframework/printing/page/print_format_builder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder.css b/xhiveframework/printing/page/print_format_builder/print_format_builder.css new file mode 100644 index 0000000..b2ca181 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder.css @@ -0,0 +1,161 @@ +.print-format-builder-section { + border: 1px solid var(--dark-border-color); + border-radius: var(--border-radius); + margin: 0px; + margin-bottom: var(--margin-md); +} + +.print-format-builder-add-section, .print-format-builder-header { + border: 1px dashed var(--dark-border-color); + border-radius: var(--border-radius); + padding: var(--padding-sm); + width: 100%; + display: inline-block; + background: var(--bg-color); + cursor: pointer; +} + +.print-format-builder-header-edit { + margin-bottom: var(--margin-sm); +} + +.print-format-builder-header { + margin-bottom: var(--margin-md); +} + +.print-format-builder-add-section { + color: var(--text-light); + align-items: center; + padding: var(--padding-lg); + display: flex; + justify-content: center; +} + +.print-format-builder-add-section .icon { + margin-right: var(--margin-sm); +} + +.print-format-builder-column { + border-radius: var(--border-radius); +} + +.print-format-builder-section .section-column { + padding: var(--padding-xs) 0 var(--padding-md) var(--padding-md); +} + +.print-format-builder-section .section-column:last-child { + padding-right: var(--padding-md); +} + +.print-format-builder-field { + padding: 8px; + width: 100%; + display: inline-block; + border-radius: var(--border-radius); + background: var(--bg-light-gray); + margin-bottom: var(--margin-sm); + font-size: var(--text-md); + color: var(--text-color); +} + +.print-format-builder-field:last-child { + margin-bottom: 0; +} + +.print-format-builder-field .field-label { + vertical-align: middle; +} + +.print-format-builder-column .print-format-builder-field { + cursor: move; +} + +.print-format-builder-section-head .section-label { + font-size: var(--text-lg); + font-weight: var(--weight-medium); + letter-spacing: 0.015em; + color: var(--text-color); + vertical-align: middle; + margin-left: var(--margin-sm); +} + +.print-format-builder-section-head { + cursor: move; + padding: var(--padding-md) calc(var(--padding-md) + 8px) + var(--padding-sm) calc(var(--padding-md) + 8px); +} + +.column-selector-row { + margin-bottom: var(--margin-xs); + padding: var(--padding-xs) 0; + cursor: grab; +} + +.column-selector-row:hover { + background-color: var(--highlight-color); +} + +.column-selector-row .drag-handle { + margin-right: var(--margin-sm); +} + +.print-format-builder-field .drag-handle { + margin-right: var(--margin-sm); +} + +.print-format-builder-sidebar .sidebar-field { + width: 100%; + padding: 4px 8px; + color: var(--text-on-light-gray); + /* color: var(--text-light); */ + text-align: left; + cursor: grab; +} + +.print-format-builder-sidebar .sidebar-custom-field { + background-color: var(--gray-300); +} + +.print-format-builder-sidebar { + top: calc(var(--navbar-height) + 70px); + position: sticky; +} + +.print-format-builder-sidebar-fields { + padding: var(--padding-xs); + overflow-y: auto; + height: 75vh; +} + +.print-format-builder-field-placeholder { + margin-bottom: var(--margin-sm); +} + +.print-format-builder-field-placeholder:last-child { + margin-bottom: 0; +} + +.print-format-builder-field-placeholder .drag-handle { + margin-right: var(--margin-sm); +} + +.filter-searchbox { + padding: 0 var(--padding-xs); + margin-bottom: var(--margin-sm); +} + +.filter-searchbox input { + background-color: var(--control-bg-on-gray); +} + +.print-format-builder-main { + display: inline-block; + vertical-align: top; + border-top: 0px; + padding: var(--padding-lg); +} + +.print-format-help-message { + font-size: var(--text-md); + margin-bottom: var(--margin-md); +} diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder.js b/xhiveframework/printing/page/print_format_builder/print_format_builder.js new file mode 100644 index 0000000..32fc607 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder.js @@ -0,0 +1,849 @@ +xhiveframework.pages["print-format-builder"].on_page_load = function (wrapper) { + xhiveframework.print_format_builder = new xhiveframework.PrintFormatBuilder(wrapper); + xhiveframework.breadcrumbs.add("Setup", "Print Format"); +}; + +xhiveframework.pages["print-format-builder"].on_page_show = function (wrapper) { + var route = xhiveframework.get_route(); + if (route.length > 1) { + xhiveframework.model.with_doc("Print Format", route[1], function () { + xhiveframework.print_format_builder.print_format = xhiveframework.get_doc("Print Format", route[1]); + xhiveframework.print_format_builder.refresh(); + }); + } else if (xhiveframework.route_options) { + if (xhiveframework.route_options.make_new) { + let { doctype, name, based_on, beta } = xhiveframework.route_options; + xhiveframework.route_options = null; + xhiveframework.print_format_builder.setup_new_print_format(doctype, name, based_on, beta); + } else { + xhiveframework.print_format_builder.print_format = xhiveframework.route_options.doc; + xhiveframework.route_options = null; + xhiveframework.print_format_builder.refresh(); + } + } +}; + +xhiveframework.PrintFormatBuilder = class PrintFormatBuilder { + constructor(parent) { + this.parent = parent; + this.make(); + this.refresh(); + } + refresh() { + this.custom_html_count = 0; + if (!this.print_format) { + this.show_start(); + } else { + this.page.set_title(this.print_format.name); + this.setup_print_format(); + } + } + make() { + this.page = xhiveframework.ui.make_app_page({ + parent: this.parent, + title: __("Print Format Builder"), + }); + + this.page.main.css({ "border-color": "transparent" }); + + this.page.sidebar = $('').appendTo( + this.page.sidebar + ); + this.page.main = $( + '' + ).appendTo(this.page.main); + + // future-bindings for buttons on sections / fields + // bind only once + this.setup_section_settings(); + this.setup_column_selector(); + this.setup_edit_custom_html(); + // $(this.page.sidebar).css({"position": 'fixed'}); + // $(this.page.main).parent().css({"margin-left": '16.67%'}); + } + show_start() { + this.page.main.html(xhiveframework.render_template("print_format_builder_start", {})); + this.page.clear_actions(); + this.page.set_title(__("Print Format Builder")); + this.start_edit_print_format(); + this.start_new_print_format(); + } + start_edit_print_format() { + // print format control + var me = this; + this.print_format_input = xhiveframework.ui.form.make_control({ + parent: this.page.main.find(".print-format-selector"), + df: { + fieldtype: "Link", + options: "Print Format", + filters: { + print_format_builder: 1, + }, + label: __("Select Print Format to Edit"), + only_select: true, + }, + render_input: true, + }); + + // create a new print format. + this.page.main.find(".btn-edit-print-format").on("click", function () { + var name = me.print_format_input.get_value(); + if (!name) return; + xhiveframework.model.with_doc("Print Format", name, function (doc) { + xhiveframework.set_route("print-format-builder", name); + }); + }); + } + start_new_print_format() { + var me = this; + this.doctype_input = xhiveframework.ui.form.make_control({ + parent: this.page.main.find(".doctype-selector"), + df: { + fieldtype: "Link", + options: "DocType", + filters: { + istable: 0, + issingle: 0, + }, + label: __("Select a DocType to make a new format"), + }, + render_input: true, + }); + + this.name_input = xhiveframework.ui.form.make_control({ + parent: this.page.main.find(".name-selector"), + df: { + fieldtype: "Data", + label: __("Name of the new Print Format"), + }, + render_input: true, + }); + + this.page.main.find(".btn-new-print-format").on("click", function () { + var doctype = me.doctype_input.get_value(), + name = me.name_input.get_value(); + if (!(doctype && name)) { + xhiveframework.msgprint(__("Both DocType and Name required")); + return; + } + me.setup_new_print_format(doctype, name); + }); + } + setup_new_print_format(doctype, name, based_on, beta) { + xhiveframework.call({ + method: "xhiveframework.printing.page.print_format_builder.print_format_builder.create_custom_format", + args: { + doctype: doctype, + name: name, + based_on: based_on, + beta: Boolean(beta), + }, + callback: (r) => { + if (r.message) { + let print_format = r.message; + if (print_format.print_format_builder_beta) { + xhiveframework.set_route("print-format-builder-beta", print_format.name); + } else { + this.print_format = print_format; + this.refresh(); + } + } + }, + }); + } + setup_print_format() { + var me = this; + xhiveframework.model.with_doctype(this.print_format.doc_type, function (doctype) { + me.meta = xhiveframework.get_meta(me.print_format.doc_type); + me.setup_sidebar(); + me.render_layout(); + me.page.set_primary_action(__("Save"), function () { + me.save_print_format(); + }); + me.page.clear_menu(); + me.page.add_menu_item( + __("Start new Format"), + function () { + me.print_format = null; + me.refresh(); + }, + true + ); + me.page.clear_inner_toolbar(); + me.page.add_inner_button(__("Edit Properties"), function () { + xhiveframework.set_route("Form", "Print Format", me.print_format.name); + }); + }); + } + setup_sidebar() { + // prepend custom HTML field + var fields = [this.get_custom_html_field()].concat(this.meta.fields); + this.page.sidebar.html( + $(xhiveframework.render_template("print_format_builder_sidebar", { fields: fields })) + ); + this.setup_field_filter(); + } + get_custom_html_field() { + return { + fieldtype: "Custom HTML", + fieldname: "_custom_html", + label: __("Custom HTML"), + }; + } + render_layout() { + this.page.main.empty(); + this.prepare_data(); + $( + xhiveframework.render_template("print_format_builder_layout", { + data: this.layout_data, + me: this, + }) + ).appendTo(this.page.main); + this.setup_sortable(); + this.setup_add_section(); + this.setup_edit_heading(); + this.setup_field_settings(); + this.setup_html_data(); + } + prepare_data() { + this.print_heading_template = null; + this.data = JSON.parse(this.print_format.format_data || "[]"); + if (!this.data.length) { + // new layout + this.data = this.meta.fields; + } else { + // extract print_heading_template if found + if (this.data[0].fieldname === "print_heading_template") { + this.print_heading_template = this.data[0].options; + this.data = this.data.splice(1); + } + } + + if (!this.print_heading_template) { + // default print heading template + this.print_heading_template = + ''; + } + + this.layout_data = []; + this.fields_dict = {}; + this.custom_html_dict = {}; + var section = null, + column = null, + me = this, + custom_html_count = 0; + + // create a new placeholder for column and set + // it as "column" + var set_column = function () { + if (!section) set_section(); + column = me.get_new_column(); + section.columns.push(column); + section.no_of_columns += 1; + }; + + var set_section = function (label) { + section = me.get_new_section(); + if (label) section.label = label; + column = null; + me.layout_data.push(section); + }; + + // break the layout into sections and columns + // so that it is easier to render in a template + $.each(this.data, function (i, f) { + me.fields_dict[f.fieldname] = f; + if (!f.name && f.fieldname) { + // from format_data (designed format) + // print_hide should always be false + if (f.fieldname === "_custom_html") { + f.label = "Custom HTML"; + f.fieldtype = "Custom HTML"; + + // set custom html id to map data properties later + custom_html_count++; + f.custom_html_id = custom_html_count; + me.custom_html_dict[f.custom_html_id] = f; + } else { + f = $.extend( + xhiveframework.meta.get_docfield(me.print_format.doc_type, f.fieldname) || {}, + f + ); + } + } + + if (f.fieldtype === "Section Break") { + set_section(f.label); + } else if (f.fieldtype === "Column Break") { + set_column(); + } else if (!xhiveframework.model.layout_fields.includes(f.fieldtype)) { + if (!column) set_column(); + + if (f.fieldtype === "Table") { + me.add_table_properties(f); + } + + if (!f.print_hide) { + column.fields.push(f); + section.has_fields = true; + } + } + }); + + // strip out empty sections + this.layout_data = $.map(this.layout_data, function (s) { + return s.has_fields ? s : null; + }); + } + get_new_section() { + return { columns: [], no_of_columns: 0, label: "" }; + } + get_new_column() { + return { fields: [] }; + } + add_table_properties(f) { + // build table columns and widths in a dict + // visible_columns + var me = this; + if (!f.visible_columns) { + me.init_visible_columns(f); + } + } + init_visible_columns(f) { + f.visible_columns = []; + $.each(xhiveframework.get_meta(f.options).fields, function (i, _f) { + if ( + !["Section Break", "Column Break", "Tab Break"].includes(_f.fieldtype) && + !_f.print_hide && + f.label + ) { + // column names set as fieldname|width + f.visible_columns.push({ + fieldname: _f.fieldname, + print_width: _f.width || "", + print_hide: 0, + }); + } + }); + } + setup_sortable() { + var me = this; + + // drag from fields library + Sortable.create(this.page.sidebar.find(".print-format-builder-sidebar-fields").get(0), { + group: { + name: "field", + put: true, + pull: "clone", + }, + sort: false, + onAdd: function (evt) { + // on drop, trash! + $(evt.item).fadeOut(); + }, + }); + + // sort, drag and drop between columns + this.page.main.find(".print-format-builder-column").each(function () { + me.setup_sortable_for_column(this); + }); + + // section sorting + Sortable.create(this.page.main.find(".print-format-builder-layout").get(0), { + handle: ".print-format-builder-section-head", + }); + } + setup_sortable_for_column(col) { + var me = this; + Sortable.create(col, { + group: { + name: "field", + put: true, + pull: true, + }, + onAdd: function (evt) { + // on drop, change the HTML + + var $item = $(evt.item); + if (!$item.hasClass("print-format-builder-field")) { + var fieldname = $item.attr("data-fieldname"); + + let field; + if (fieldname === "_custom_html") { + field = me.get_custom_html_field(); + } else { + field = xhiveframework.meta.get_docfield(me.print_format.doc_type, fieldname); + } + + var html = xhiveframework.render_template("print_format_builder_field", { + field: field, + me: me, + }); + + $item.replaceWith(html); + } + }, + }); + } + setup_field_filter() { + var me = this; + this.page.sidebar.find(".filter-fields").on("keyup", function () { + var text = $(this).val(); + me.page.sidebar.find(".field-label").each(function () { + var show = + !text || $(this).text().toLowerCase().indexOf(text.toLowerCase()) !== -1; + $(this).parent().toggle(show); + }); + }); + } + setup_section_settings() { + var me = this; + this.page.main.on("click", ".section-settings", function () { + var section = $(this).parent().parent(); + var no_of_columns = section.find(".section-column").length; + var label = section.attr("data-label"); + + // new dialog + var d = new xhiveframework.ui.Dialog({ + title: "Edit Section", + fields: [ + { + label: __("No of Columns"), + fieldname: "no_of_columns", + fieldtype: "Select", + options: ["1", "2", "3", "4"], + }, + { + label: __("Section Heading"), + fieldname: "label", + fieldtype: "Data", + description: __("Will only be shown if section headings are enabled"), + }, + { + label: __("Remove Section"), + fieldname: "remove_section", + fieldtype: "Button", + click: function () { + d.hide(); + section.fadeOut(function () { + section.remove(); + }); + }, + input_class: "btn-danger", + input_css: { + "margin-top": "20px", + }, + }, + ], + }); + + d.set_input("no_of_columns", no_of_columns + ""); + d.set_input("label", label || ""); + + d.set_primary_action(__("Update"), function () { + // resize number of columns + me.update_columns_in_section( + section, + no_of_columns, + cint(d.get_value("no_of_columns")) + ); + + section.attr("data-label", d.get_value("label") || ""); + section.find(".section-label").html(d.get_value("label") || ""); + + d.hide(); + }); + + d.show(); + + return false; + }); + } + setup_field_settings() { + this.page.main.find(".field-settings").on("click", (e) => { + const field = $(e.currentTarget).parent(); + // new dialog + var d = new xhiveframework.ui.Dialog({ + title: __("Set Properties"), + fields: [ + { + label: __("Label"), + fieldname: "label", + fieldtype: "Data", + }, + { + label: __("Align Value"), + fieldname: "align", + fieldtype: "Select", + options: [ + { label: __("Left", null, "alignment"), value: "left" }, + { label: __("Right", null, "alignment"), value: "right" }, + ], + }, + { + label: __("Remove Field"), + fieldtype: "Button", + click: function () { + d.hide(); + field.remove(); + }, + input_class: "btn-danger", + }, + ], + }); + + d.set_value("label", field.attr("data-label")); + + d.set_primary_action(__("Update"), function () { + field.attr("data-align", d.get_value("align")); + field.attr("data-label", d.get_value("label")); + field.find(".field-label").html(d.get_value("label")); + d.hide(); + }); + + // set current value + if (field.attr("data-align")) { + d.set_value("align", field.attr("data-align")); + } else { + d.set_value("align", "left"); + } + + d.show(); + + return false; + }); + } + setup_html_data() { + // set JQuery `data` for Custom HTML fields, since editing the HTML + // directly causes problem becuase of HTML reformatting + // + // this is based on a dummy attribute custom_html_id, since all custom html + // fields have the same fieldname `_custom_html` + var me = this; + this.page.main.find('[data-fieldtype="Custom HTML"]').each(function () { + var fieldname = $(this).attr("data-fieldname"); + var content = $($(this).find(".html-content")[0]); + var html = me.custom_html_dict[parseInt(content.attr("data-custom-html-id"))].options; + content.data("content", html); + }); + } + update_columns_in_section(section, no_of_columns, new_no_of_columns) { + var col_size = 12 / new_no_of_columns, + me = this, + resize = function () { + section + .find(".section-column") + .removeClass() + .addClass("section-column") + .addClass("col-md-" + col_size); + }; + + if (new_no_of_columns < no_of_columns) { + // move contents of last n columns to previous column + for (var i = no_of_columns; i > new_no_of_columns; i--) { + var $col = $(section.find(".print-format-builder-column").get(i - 1)); + var prev = section.find(".print-format-builder-column").get(i - 2); + + // append each field to prev + $col.parent().addClass("to-drop"); + $col.find(".print-format-builder-field").each(function () { + $(this).appendTo(prev); + }); + } + + // drop columns + section.find(".to-drop").remove(); + + // resize + resize(); + } else if (new_no_of_columns > no_of_columns) { + // add empty column and resize old columns + for (let i = no_of_columns; i < new_no_of_columns; i++) { + var col = $( + '
      \ +
      ' + ).appendTo(section); + me.setup_sortable_for_column(col.find(".print-format-builder-column").get(0)); + } + // resize + resize(); + } + } + setup_add_section() { + var me = this; + this.page.main.find(".print-format-builder-add-section").on("click", function () { + // boostrap new section info + var section = me.get_new_section(); + section.columns.push(me.get_new_column()); + section.no_of_columns = 1; + + var $section = $( + xhiveframework.render_template("print_format_builder_section", { + section: section, + me: me, + }) + ).appendTo(me.page.main.find(".print-format-builder-layout")); + + me.setup_sortable_for_column($section.find(".print-format-builder-column").get(0)); + }); + } + setup_edit_heading() { + var me = this; + var $heading = this.page.main.find(".print-format-builder-print-heading"); + + // set content property + $heading.data("content", this.print_heading_template); + + this.page.main.find(".edit-heading").on("click", function () { + var d = me.get_edit_html_dialog(__("Edit Heading"), __("Heading"), $heading); + }); + } + setup_column_selector() { + var me = this; + this.page.main.on("click", ".select-columns", function () { + var parent = $(this).parents(".print-format-builder-field:first"), + doctype = parent.attr("data-doctype"), + label = parent.attr("data-label"), + columns = parent.attr("data-columns").split(","), + column_names = $.map(columns, function (v) { + return v.split("|")[0]; + }), + widths = {}; + + $.each(columns, function (i, v) { + var parts = v.split("|"); + widths[parts[0]] = parts[1] || ""; + }); + + var d = new xhiveframework.ui.Dialog({ + title: __("Select Table Columns for {0}", [label]), + }); + + var $body = $(d.body); + + var doc_fields = xhiveframework.get_meta(doctype).fields; + var docfields_by_name = {}; + + // docfields by fieldname + $.each(doc_fields, function (j, f) { + if (f) docfields_by_name[f.fieldname] = f; + }); + + // add field which are in column_names first to preserve order + var fields = []; + $.each(column_names, function (i, v) { + if (Object.keys(docfields_by_name).includes(v)) { + fields.push(docfields_by_name[v]); + } + }); + // add remaining fields + $.each(doc_fields, function (j, f) { + if ( + f && + !column_names.includes(f.fieldname) && + !["Section Break", "Column Break", "Tab Break"].includes(f.fieldtype) && + f.label + ) { + fields.push(f); + } + }); + // render checkboxes + $( + xhiveframework.render_template("print_format_builder_column_selector", { + fields: fields, + column_names: column_names, + widths: widths, + }) + ).appendTo(d.body); + + Sortable.create($body.find(".column-selector-list").get(0)); + + var get_width_input = function (fieldname) { + return $body.find(".column-width[data-fieldname='" + fieldname + "']"); + }; + + // update data-columns property on update + d.set_primary_action(__("Update"), function () { + var visible_columns = []; + $body.find("input:checked").each(function () { + var fieldname = $(this).attr("data-fieldname"), + width = get_width_input(fieldname).val() || ""; + visible_columns.push(fieldname + "|" + width); + }); + parent.attr("data-columns", visible_columns.join(",")); + d.hide(); + }); + + let update_column_count_message = () => { + // show a warning if user selects more than 10 columns for a table + let columns_count = $body.find("input:checked").length; + $body.find(".help-message").toggle(columns_count > 10); + }; + update_column_count_message(); + + // enable / disable input based on selection + $body.on("click", "input[type='checkbox']", function () { + var disabled = !$(this).prop("checked"), + input = get_width_input($(this).attr("data-fieldname")); + + input.prop("disabled", disabled); + if (disabled) input.val(""); + + update_column_count_message(); + }); + + d.show(); + + return false; + }); + } + get_visible_columns_string(f) { + if (!f.visible_columns) { + this.init_visible_columns(f); + } + return $.map(f.visible_columns, function (v) { + return v.fieldname + "|" + (v.print_width || ""); + }).join(","); + } + get_no_content() { + return __("Edit to add content"); + } + setup_edit_custom_html() { + var me = this; + this.page.main.on("click", ".edit-html", function () { + me.get_edit_html_dialog( + __("Edit Custom HTML"), + __("Custom HTML"), + $(this).parents(".print-format-builder-field:first").find(".html-content") + ); + }); + } + get_edit_html_dialog(title, label, $content) { + var me = this; + var d = new xhiveframework.ui.Dialog({ + title: title, + fields: [ + { + fieldname: "content", + fieldtype: "Code", + label: label, + options: "HTML", + }, + { + fieldname: "help", + fieldtype: "HTML", + options: + "

      " + + __( + "You can add dynamic properties from the document by using Jinja templating." + ) + + __("For example: If you want to include the document ID, use {0}", [ + "{{ doc.name }}", + ]) + + "

      ", + }, + ], + }); + + // set existing content in input + var content = $content.data("content") || ""; + if (content.indexOf(me.get_no_content()) !== -1) content = ""; + d.set_input("content", content); + + d.set_primary_action(__("Update"), function () { + $($content[0]).data("content", d.get_value("content")); + $content.html(d.get_value("content")); + d.hide(); + }); + + d.show(); + + return d; + } + save_print_format() { + var data = [], + me = this; + + // add print heading as the first field + // this will be removed and set as a doc property + // before rendering + data.push({ + fieldname: "print_heading_template", + fieldtype: "Custom HTML", + options: this.page.main.find(".print-format-builder-print-heading").data("content"), + }); + + // add pages + this.page.main.find(".print-format-builder-section").each(function () { + var section = { fieldtype: "Section Break", label: $(this).attr("data-label") || "" }; + data.push(section); + $(this) + .find(".print-format-builder-column") + .each(function () { + data.push({ fieldtype: "Column Break" }); + $(this) + .find(".print-format-builder-field") + .each(function () { + var $this = $(this), + fieldtype = $this.attr("data-fieldtype"), + align = $this.attr("data-align"), + label = $this.attr("data-label"), + df = { + fieldname: $this.attr("data-fieldname"), + print_hide: 0, + }; + + if (align) { + df.align = align; + } + + if (label) { + df.label = label; + } + + if (fieldtype === "Table") { + // append the user selected columns to visible_columns + var columns = $this.attr("data-columns").split(","); + df.visible_columns = []; + $.each(columns, function (i, c) { + var parts = c.split("|"); + df.visible_columns.push({ + fieldname: parts[0], + print_width: parts[1], + print_hide: 0, + }); + }); + } + if (fieldtype === "Custom HTML") { + // custom html as HTML field + df.fieldtype = "HTML"; + df.options = $($this.find(".html-content")[0]).data("content"); + } + data.push(df); + }); + }); + }); + + // save format_data + xhiveframework.call({ + method: "xhiveframework.client.set_value", + args: { + doctype: "Print Format", + name: this.print_format.name, + fieldname: "format_data", + value: JSON.stringify(data), + }, + freeze: true, + btn: this.page.btn_primary, + callback: function (r) { + me.print_format = r.message; + locals["Print Format"][me.print_format.name] = r.message; + xhiveframework.show_alert({ message: __("Saved"), indicator: "green" }); + }, + }); + } +}; diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder.json b/xhiveframework/printing/page/print_format_builder/print_format_builder.json new file mode 100644 index 0000000..81cb213 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2015-01-27 04:35:43.872918", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2017-05-03 05:59:33.702308", + "modified_by": "Administrator", + "module": "Printing", + "name": "print-format-builder", + "owner": "Administrator", + "page_name": "print-format-builder", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Print Format Builder" +} \ No newline at end of file diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder.py b/xhiveframework/printing/page/print_format_builder/print_format_builder.py new file mode 100644 index 0000000..b05bb63 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder.py @@ -0,0 +1,19 @@ +import xhiveframework + + +@xhiveframework.whitelist() +def create_custom_format(doctype, name, based_on="Standard", beta=False): + doc = xhiveframework.new_doc("Print Format") + doc.doc_type = doctype + doc.name = name + beta = xhiveframework.parse_json(beta) + + if beta: + doc.print_format_builder_beta = 1 + else: + doc.print_format_builder = 1 + doc.format_data = ( + xhiveframework.db.get_value("Print Format", based_on, "format_data") if based_on != "Standard" else None + ) + doc.insert() + return doc diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_column_selector.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_column_selector.html new file mode 100644 index 0000000..adc87ff --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_column_selector.html @@ -0,0 +1,36 @@ +

      {{ __("Check columns to select, drag to set order.") }} + {{ __("Widths can be set in px or %.") }}

      +

      + {{ __("Some columns might get cut off when printing to PDF. Try to keep number of columns under 10.") }} +

      +
      +

      {{ __("Column") }}

      +

      {{ __("Width") }}

      +
      +
      + {% for (i=0; i < fields.length; i++) { var f = fields[i]; %} + {% var selected = in_list(column_names, f.fieldname) %} +
      +
      +
      + +
      +
      + +
      +
      +
      + +
      +
      + {% } %} +
      diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_field.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_field.html new file mode 100644 index 0000000..5e900df --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_field.html @@ -0,0 +1,46 @@ + diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_layout.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_layout.html new file mode 100644 index 0000000..a4b3962 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_layout.html @@ -0,0 +1,30 @@ +
      + + + + +
      diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_section.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_section.html new file mode 100644 index 0000000..902c85b --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_section.html @@ -0,0 +1,23 @@ + diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_sidebar.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_sidebar.html new file mode 100644 index 0000000..2b5b040 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_sidebar.html @@ -0,0 +1,21 @@ + + diff --git a/xhiveframework/printing/page/print_format_builder/print_format_builder_start.html b/xhiveframework/printing/page/print_format_builder/print_format_builder_start.html new file mode 100644 index 0000000..dc01bba --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder/print_format_builder_start.html @@ -0,0 +1,18 @@ +
      +

      {%= __("Select an existing format to edit or start a new format.") %}

      +
      +
      + +

      + +

      +
      +
      +
      +
      +

      + +

      +
      diff --git a/xhiveframework/printing/page/print_format_builder_beta/__init__.py b/xhiveframework/printing/page/print_format_builder_beta/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.css b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.css new file mode 100644 index 0000000..0bd8d9c --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.css @@ -0,0 +1,3 @@ +.layout-main-section-wrapper { + margin-bottom: 0; +} diff --git a/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.js b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.js new file mode 100644 index 0000000..1955cb9 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.js @@ -0,0 +1,110 @@ +xhiveframework.pages["print-format-builder-beta"].on_page_load = function (wrapper) { + xhiveframework.ui.make_app_page({ + parent: wrapper, + title: __("Print Format Builder"), + single_column: true, + }); + + // hot reload in development + if (xhiveframework.boot.developer_mode) { + xhiveframework.hot_update = xhiveframework.hot_update || []; + xhiveframework.hot_update.push(() => load_print_format_builder_beta(wrapper)); + } +}; + +xhiveframework.pages["print-format-builder-beta"].on_page_show = function (wrapper) { + load_print_format_builder_beta(wrapper); +}; + +function load_print_format_builder_beta(wrapper) { + let route = xhiveframework.get_route(); + let $parent = $(wrapper).find(".layout-main-section"); + $parent.empty(); + + if (route.length > 1) { + xhiveframework.require("print_format_builder.bundle.js").then(() => { + xhiveframework.print_format_builder = new xhiveframework.ui.PrintFormatBuilder({ + wrapper: $parent, + page: wrapper.page, + print_format: route[1], + }); + }); + } else { + let d = new xhiveframework.ui.Dialog({ + title: __("Create or Edit Print Format"), + fields: [ + { + label: __("Action"), + fieldname: "action", + fieldtype: "Select", + options: [ + { label: __("Create New"), value: "Create" }, + { label: __("Edit Existing"), value: "Edit" }, + ], + change() { + let action = d.get_value("action"); + d.get_primary_btn().text(action === "Create" ? __("Create") : __("Edit")); + }, + }, + { + label: __("Select Document Type"), + fieldname: "doctype", + fieldtype: "Link", + options: "DocType", + filters: { + istable: 0, + }, + reqd: 1, + default: xhiveframework.route_options ? xhiveframework.route_options.doctype : null, + }, + { + label: __("New Print Format Name"), + fieldname: "print_format_name", + fieldtype: "Data", + depends_on: (doc) => doc.action === "Create", + mandatory_depends_on: (doc) => doc.action === "Create", + }, + { + label: __("Select Print Format"), + fieldname: "print_format", + fieldtype: "Link", + options: "Print Format", + only_select: 1, + depends_on: (doc) => doc.action === "Edit", + get_query() { + return { + filters: { + doc_type: d.get_value("doctype"), + print_format_builder_beta: 1, + }, + }; + }, + mandatory_depends_on: (doc) => doc.action === "Edit", + }, + ], + primary_action_label: __("Edit"), + primary_action({ action, doctype, print_format, print_format_name }) { + if (action === "Edit") { + xhiveframework.set_route("print-format-builder-beta", print_format); + } else if (action === "Create") { + d.get_primary_btn().prop("disabled", true); + xhiveframework.db + .insert({ + doctype: "Print Format", + name: print_format_name, + doc_type: doctype, + print_format_builder_beta: 1, + }) + .then((doc) => { + xhiveframework.set_route("print-format-builder-beta", doc.name); + }) + .finally(() => { + d.get_primary_btn().prop("disabled", false); + }); + } + }, + }); + d.set_value("action", "Create"); + d.show(); + } +} diff --git a/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.json b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.json new file mode 100644 index 0000000..a5b1288 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.json @@ -0,0 +1,22 @@ +{ + "content": null, + "creation": "2021-07-10 12:22:16.138485", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-07-10 12:22:16.138485", + "modified_by": "Administrator", + "module": "Printing", + "name": "print-format-builder-beta", + "owner": "Administrator", + "page_name": "Print Format Builder Beta", + "roles": [ + { + "role": "System Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0 +} \ No newline at end of file diff --git a/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.py b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.py new file mode 100644 index 0000000..4e37c50 --- /dev/null +++ b/xhiveframework/printing/page/print_format_builder_beta/print_format_builder_beta.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021, XhiveFramework Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + + +import functools + +import xhiveframework + + +@xhiveframework.whitelist() +def get_google_fonts(): + return _get_google_fonts() + + +@functools.lru_cache +def _get_google_fonts(): + file_path = xhiveframework.get_app_path("xhiveframework", "data", "google_fonts.json") + return xhiveframework.parse_json(xhiveframework.read_file(file_path)) diff --git a/xhiveframework/printing/print_style/__init__.py b/xhiveframework/printing/print_style/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/print_style/classic/__init__.py b/xhiveframework/printing/print_style/classic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/print_style/classic/classic.json b/xhiveframework/printing/print_style/classic/classic.json new file mode 100644 index 0000000..f732fd9 --- /dev/null +++ b/xhiveframework/printing/print_style/classic/classic.json @@ -0,0 +1,15 @@ +{ + "creation": "2017-08-17 02:00:12.502887", + "css": "/*\n\tcommon style for whole page\n\tThis should include:\n\t+ page size related settings\n\t+ font family settings\n\t+ line spacing settings\n*/\n.print-format div,\n.print-format span,\n.print-format td,\n.print-format h1,\n.print-format h2,\n.print-format h3,\n.print-format h4 {\n\tfont-family: Georgia, serif;\n}\n\n/* classic format: for-test */", + "disabled": 0, + "docstatus": 0, + "doctype": "Print Style", + "idx": 1, + "modified": "2017-08-18 00:43:48.675833", + "modified_by": "Administrator", + "name": "Classic", + "owner": "Administrator", + "preview": "/assets/xhiveframework/images/help/print-style-classic.png", + "print_style_name": "Classic", + "standard": 1 +} diff --git a/xhiveframework/printing/print_style/modern/__init__.py b/xhiveframework/printing/print_style/modern/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/print_style/modern/modern.json b/xhiveframework/printing/print_style/modern/modern.json new file mode 100644 index 0000000..4508288 --- /dev/null +++ b/xhiveframework/printing/print_style/modern/modern.json @@ -0,0 +1,15 @@ +{ + "creation": "2017-08-17 02:16:58.060374", + "css": ".print-heading {\n\ttext-align: right;\n\ttext-transform: uppercase;\n\tcolor: #666;\n\tpadding-bottom: 20px;\n\tmargin-bottom: 20px;\n\tborder-bottom: 1px solid #d1d8dd;\n}\n\n.print-heading h2 {\n\tfont-size: 24px;\n}\n\n.print-format th {\n\tbackground-color: #eee !important;\n\tborder-bottom: 0px !important;\n}\n\n.print-format .primary.compact-item {\n font-weight: bold;\n}\n\n/* modern format: for-test */", + "disabled": 0, + "docstatus": 0, + "doctype": "Print Style", + "idx": 1, + "modified": "2020-11-10 13:59:09.976381", + "modified_by": "Administrator", + "name": "Modern", + "owner": "Administrator", + "preview": "/assets/xhiveframework/images/help/print-style-modern.png", + "print_style_name": "Modern", + "standard": 1 +} diff --git a/xhiveframework/printing/print_style/monochrome/__init__.py b/xhiveframework/printing/print_style/monochrome/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/print_style/monochrome/monochrome.json b/xhiveframework/printing/print_style/monochrome/monochrome.json new file mode 100644 index 0000000..7d2f925 --- /dev/null +++ b/xhiveframework/printing/print_style/monochrome/monochrome.json @@ -0,0 +1,15 @@ +{ + "creation": "2017-08-17 02:16:20.992989", + "css": ".print-format * {\n\tcolor: #000 !important;\n}\n\n.print-format .alert {\n\tbackground-color: inherit;\n\tborder: 1px dashed #333;\n}\n\n.print-format .table-bordered,\n.print-format .table-bordered > thead > tr > th,\n.print-format .table-bordered > tbody > tr > th,\n.print-format .table-bordered > tfoot > tr > th,\n.print-format .table-bordered > thead > tr > td,\n.print-format .table-bordered > tbody > tr > td,\n.print-format .table-bordered > tfoot > tr > td {\n\tborder: 1px solid #333;\n}\n\n.print-format hr {\n\tborder-top: 1px solid #333;\n}\n\n.print-heading {\n\tborder-bottom: 2px solid #333;\n}\n", + "disabled": 0, + "docstatus": 0, + "doctype": "Print Style", + "idx": 0, + "modified": "2017-08-18 00:44:25.023898", + "modified_by": "Administrator", + "name": "Monochrome", + "owner": "Administrator", + "preview": "/assets/xhiveframework/images/help/print-style-monochrome.png", + "print_style_name": "Monochrome", + "standard": 1 +} diff --git a/xhiveframework/printing/print_style/redesign/__init__.py b/xhiveframework/printing/print_style/redesign/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xhiveframework/printing/print_style/redesign/redesign.json b/xhiveframework/printing/print_style/redesign/redesign.json new file mode 100644 index 0000000..952b667 --- /dev/null +++ b/xhiveframework/printing/print_style/redesign/redesign.json @@ -0,0 +1,14 @@ +{ + "creation": "2020-10-22 00:00:08.161999", + "css": ".print-format {\n font-size: 13px;\n background: white;\n}\n\n.print-heading {\n border-bottom: 1px solid #f4f5f6;\n padding-bottom: 5px;\n margin-bottom: 10px;\n}\n\n.print-heading h2 {\n font-size: 24px;\n}\n\n.print-heading h2 div {\n font-weight: 600;\n}\n\n.print-heading small {\n font-size: 13px !important;\n font-weight: normal;\n line-height: 2.5;\n color: #4c5a67;\n}\n\n.print-format .letter-head {\n margin-bottom: 30px;\n}\n\n.print-format label {\n font-weight: normal;\n font-size: 13px;\n color: #4C5A67;\n margin-bottom: 0;\n}\n\n.print-format .data-field {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.print-format .value {\n color: #192734;\n line-height: 1.8;\n}\n\n.print-format .section-break:not(:last-child) {\n margin-bottom: 0;\n}\n\n.print-format .row:not(.section-break) {\n line-height: 1.6;\n margin-top: 15px !important;\n}\n\n.print-format .important .value {\n font-size: 13px;\n font-weight: 600;\n}\n\n.print-format th {\n color: #74808b;\n font-weight: normal;\n border-bottom-width: 1px !important;\n}\n\n.print-format .table-bordered td, .print-format .table-bordered th {\n border: 1px solid #f4f5f6;\n}\n\n.print-format .table-bordered {\n border: 1px solid #f4f5f6;\n}\n\n.print-format td, .print-format th {\n padding: 10px !important;\n}\n\n.print-format .primary.compact-item {\n font-weight: normal;\n}\n\n.print-format table td .value {\n font-size: 12px;\n line-height: 1.8;\n}\n", + "disabled": 0, + "docstatus": 0, + "doctype": "Print Style", + "idx": 0, + "modified": "2020-12-14 17:56:37.421390", + "modified_by": "Administrator", + "name": "Redesign", + "owner": "Administrator", + "print_style_name": "Redesign", + "standard": 1 +} \ No newline at end of file diff --git a/xhiveframework/public/css/bootstrap.css b/xhiveframework/public/css/bootstrap.css new file mode 100644 index 0000000..ab16552 --- /dev/null +++ b/xhiveframework/public/css/bootstrap.css @@ -0,0 +1,5819 @@ +/*! + * Bootstrap v3.3.1 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000; + background: transparent; + text-shadow: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd; + } +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #36414c; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #36414c; + text-decoration: none; +} +a:hover, +a:focus { + color: #161b1f; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #d1d8dd; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.3em; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 24px; +} +h2, +.h2 { + font-size: 20px; +} +h3, +.h3 { + font-size: 18px; +} +h4, +.h4 { + font-size: 16px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: transparent; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #8d99a6; +} +.text-primary { + color: #5e64ff; +} +a.text-primary:hover { + color: #2b33ff; +} +.text-success { + color: #98d85b; +} +a.text-success:hover { + color: #7ece32; +} +.text-info { + color: #935eff; +} +a.text-info:hover { + color: #712bff; +} +.text-warning { + color: #ffa00a; +} +a.text-warning:hover { + color: #d68300; +} +.text-danger { + color: #ff5858; +} +a.text-danger:hover { + color: #ff2525; +} +.bg-primary { + color: #fff; + background-color: #5e64ff; +} +a.bg-primary:hover { + background-color: #2b33ff; +} +.bg-success { + background-color: transparent; +} +a.bg-success:hover { + background-color: rgba(0, 0, 0, 0); +} +.bg-info { + background-color: transparent; +} +a.bg-info:hover { + background-color: rgba(0, 0, 0, 0); +} +.bg-warning { + background-color: transparent; +} +a.bg-warning:hover { + background-color: rgba(0, 0, 0, 0); +} +.bg-danger { + background-color: transparent; +} +a.bg-danger:hover { + background-color: rgba(0, 0, 0, 0); +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #8d99a6; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #d1d8dd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #d1d8dd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #d1d8dd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #d1d8dd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #d1d8dd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: transparent; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: rgba(0, 0, 0, 0); +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: transparent; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: rgba(0, 0, 0, 0); +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: transparent; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: rgba(0, 0, 0, 0); +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: transparent; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: rgba(0, 0, 0, 0); +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #d1d8dd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 5px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 30px; + padding: 4px 10px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #d1d8dd; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #ced5db; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(206, 213, 219, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(206, 213, 219, .6); +} +.form-control:focus { + border-color: #ced5db; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 3px rgba(206, 213, 219, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 3px rgba(206, 213, 219, .6); +} +.form-control::-moz-placeholder { + color: #d1d8dd; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #d1d8dd; +} +.form-control::-webkit-input-placeholder { + color: #d1d8dd; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: 30px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 5px; + padding-bottom: 5px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.form-group-sm .form-control { + height: 30px; + line-height: 30px; +} +textarea.form-group-sm .form-control, +select[multiple].form-group-sm .form-control { + height: auto; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.form-group-lg .form-control { + height: 46px; + line-height: 46px; +} +textarea.form-group-lg .form-control, +select[multiple].form-group-lg .form-control { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 37.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #98d85b; +} +.has-success .form-control { + border-color: #98d85b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #7ece32; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ccecad; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ccecad; +} +.has-success .input-group-addon { + color: #98d85b; + background-color: transparent; + border-color: #98d85b; +} +.has-success .form-control-feedback { + color: #98d85b; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #ffa00a; +} +.has-warning .form-control { + border-color: #ffa00a; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #d68300; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ffc870; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ffc870; +} +.has-warning .input-group-addon { + color: #ffa00a; + background-color: transparent; + border-color: #ffa00a; +} +.has-warning .form-control-feedback { + color: #ffa00a; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #ff5858; +} +.has-error .form-control { + border-color: #ff5858; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #ff2525; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ffbebe; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ffbebe; +} +.has-error .input-group-addon { + color: #ff5858; + background-color: transparent; + border-color: #ff5858; +} +.has-error .form-control-feedback { + color: #ff5858; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #6b8196; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 5px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 25px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 5px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.3px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + } +} +.btn { + display: inline-block; + padding: 4px 10px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: inherit; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: inherit; + background-color: #f0f4f7; + border-color: transparent; +} +.btn-default:hover, +.btn-default:focus, +.btn-default.focus, +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: inherit; + background-color: #cfdce5; + border-color: rgba(0, 0, 0, 0); +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #f0f4f7; + border-color: transparent; +} +.btn-default .badge { + color: #f0f4f7; + background-color: inherit; +} +.btn-primary { + color: #fff; + background-color: #5e64ff; + border-color: #444bff; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary.focus, +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #2b33ff; + border-color: #0711ff; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #5e64ff; + border-color: #444bff; +} +.btn-primary .badge { + color: #5e64ff; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #98d85b; + border-color: #8bd346; +} +.btn-success:hover, +.btn-success:focus, +.btn-success.focus, +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #7ece32; + border-color: #6db22a; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #98d85b; + border-color: #8bd346; +} +.btn-success .badge { + color: #98d85b; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #8d99a6; + border-color: #7f8c9b; +} +.btn-info:hover, +.btn-info:focus, +.btn-info.focus, +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #707f90; + border-color: #616e7c; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #8d99a6; + border-color: #7f8c9b; +} +.btn-info .badge { + color: #8d99a6; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #ffa00a; + border-color: #f09300; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning.focus, +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #d68300; + border-color: #b26d00; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #ffa00a; + border-color: #f09300; +} +.btn-warning .badge { + color: #ffa00a; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #ff5858; + border-color: #ff3f3f; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger.focus, +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #ff2525; + border-color: #ff0101; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #ff5858; + border-color: #ff3f3f; +} +.btn-danger .badge { + color: #ff5858; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #36414c; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #161b1f; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; + visibility: hidden; +} +.collapse.in { + display: block; + visibility: visible; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0px 0px 1px rgba(0,0,0,0.200), 0px 1px 3px rgba(0,0,0,0.050), 0px 10px 24px -3px rgba(0,0,0,0.100); + box-shadow: 0px 0px 1px rgba(0,0,0,0.200), 0px 1px 3px rgba(0,0,0,0.050), 0px 10px 24px -3px rgba(0,0,0,0.100); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #d8dfe5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f0f4f7; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: inherit; + text-decoration: none; + background-color: #f0f4f7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 99; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 4px 10px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #f0f4f7; + border: 1px solid #d1d8dd; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #f7fafc; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #f7fafc; + border-color: #36414c; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: inherit; + background-color: #f7fafc; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; + visibility: hidden; +} +.tab-content > .active { + display: block; + visibility: visible; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 40px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + visibility: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 40px; + padding: 10px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 3px; + margin-right: 15px; + margin-bottom: 3px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 5px; + margin-right: -15px; + margin-bottom: 5px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 5px; + margin-bottom: 5px; +} +.navbar-btn.btn-sm { + margin-top: 5px; + margin-bottom: 5px; +} +.navbar-btn.btn-xs { + margin-top: 9px; + margin-bottom: 9px; +} +.navbar-text { + margin-top: 10px; + margin-bottom: 10px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f5f7fa; + border-color: #ebeff2; +} +.navbar-default .navbar-brand { + color: #6c7680; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #36414c; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #6c7680; +} +.navbar-default .navbar-nav > li > a { + color: #6c7680; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #36414c; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #dfe5ef; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #ebeff2; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #dfe5ef; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #6c7680; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #36414c; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #dfe5ef; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #6c7680; +} +.navbar-default .navbar-link:hover { + color: #36414c; +} +.navbar-default .btn-link { + color: #6c7680; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #36414c; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #35414b; + border-color: #2a343c; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #b8c2cb; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #20272d; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #475765; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #475765; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #475765; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #262f36; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #20272d; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #2a343c; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #2a343c; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #20272d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #475765; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #475765; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 4px 10px; + margin-left: -1px; + line-height: 1.42857143; + color: #36414c; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #161b1f; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #5e64ff; + border-color: #5e64ff; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: inherit; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #f0f4f7; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #cfdce5; +} +.label-primary { + background-color: #d9f6ff; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #a6eaff; +} +.label-success { + background-color: #e4ffc1; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #ceff8e; +} +.label-info { + background-color: #e8ddff; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #c6aaff; +} +.label-warning { + background-color: #ffe6bf; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ffd28c; +} +.label-danger { + background-color: #ffdcdc; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #ffa9a9; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #36414c; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #f0f4f7; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #36414c; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px 15px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding: 48px 0; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #36414c; +} +.thumbnail .caption { + padding: 9px; + color: #36414c; +} +.alert { + padding: 10px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 30px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #98d85b; + background-color: rgba(255, 255, 255, .9); + border-color: #98d85b; +} +.alert-success hr { + border-top-color: #8bd346; +} +.alert-success .alert-link { + color: #7ece32; +} +.alert-info { + color: #935eff; + background-color: rgba(255, 255, 255, .9); + border-color: #935eff; +} +.alert-info hr { + border-top-color: #8244ff; +} +.alert-info .alert-link { + color: #712bff; +} +.alert-warning { + color: #ffa00a; + background-color: rgba(255, 255, 255, .9); + border-color: #ffa00a; +} +.alert-warning hr { + border-top-color: #f09300; +} +.alert-warning .alert-link { + color: #d68300; +} +.alert-danger { + color: #ff5858; + background-color: rgba(255, 255, 255, .9); + border-color: #ff5858; +} +.alert-danger hr { + border-top-color: #ff3f3f; +} +.alert-danger .alert-link { + color: #ff2525; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #36414c; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #98d85b; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #935eff; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #ffa00a; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #ff5858; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: inherit; + background-color: #f0f4f7; + border-color: #f0f4f7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #fff; +} +.list-group-item-success { + color: #98d85b; + background-color: transparent; +} +a.list-group-item-success { + color: #98d85b; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #98d85b; + background-color: rgba(0, 0, 0, 0); +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #98d85b; + border-color: #98d85b; +} +.list-group-item-info { + color: #935eff; + background-color: transparent; +} +a.list-group-item-info { + color: #935eff; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #935eff; + background-color: rgba(0, 0, 0, 0); +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #935eff; + border-color: #935eff; +} +.list-group-item-warning { + color: #ffa00a; + background-color: transparent; +} +a.list-group-item-warning { + color: #ffa00a; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #ffa00a; + background-color: rgba(0, 0, 0, 0); +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #ffa00a; + border-color: #ffa00a; +} +.list-group-item-danger { + color: #ff5858; + background-color: transparent; +} +a.list-group-item-danger { + color: #ff5858; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #ff5858; + background-color: rgba(0, 0, 0, 0); +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #ff5858; + border-color: #ff5858; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #d1d8dd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ced5db; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f7fafc; + border-color: #ced5db; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ced5db; +} +.panel-default > .panel-heading .badge { + color: #f7fafc; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ced5db; +} +.panel-primary { + border-color: #5e64ff; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #5e64ff; + border-color: #5e64ff; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #5e64ff; +} +.panel-primary > .panel-heading .badge { + color: #5e64ff; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #5e64ff; +} +.panel-success { + border-color: rgba(0, 0, 0, 0); +} +.panel-success > .panel-heading { + color: #98d85b; + background-color: transparent; + border-color: rgba(0, 0, 0, 0); +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0); +} +.panel-success > .panel-heading .badge { + color: transparent; + background-color: #98d85b; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0); +} +.panel-info { + border-color: rgba(0, 0, 0, 0); +} +.panel-info > .panel-heading { + color: #935eff; + background-color: transparent; + border-color: rgba(0, 0, 0, 0); +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0); +} +.panel-info > .panel-heading .badge { + color: transparent; + background-color: #935eff; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0); +} +.panel-warning { + border-color: rgba(0, 0, 0, 0); +} +.panel-warning > .panel-heading { + color: #ffa00a; + background-color: transparent; + border-color: rgba(0, 0, 0, 0); +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0); +} +.panel-warning > .panel-heading .badge { + color: transparent; + background-color: #ffa00a; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0); +} +.panel-danger { + border-color: rgba(0, 0, 0, 0); +} +.panel-danger > .panel-heading { + color: #ff5858; + background-color: transparent; + border-color: rgba(0, 0, 0, 0); +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: rgba(0, 0, 0, 0); +} +.panel-danger > .panel-heading .badge { + color: transparent; + background-color: #ff5858; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: rgba(0, 0, 0, 0); +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #fafbfc; + border: 1px solid #d1d8dd; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: #334143; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif; + font-size: 12px; + font-weight: normal; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000; + perspective: 1000; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -15px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -15px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +body { + text-rendering: optimizeLegibility; +} +.list-group-item { + padding: 8px 15px; +} +h3, +h4 { + font-weight: bold; +} +ul.with-margin li, +ol.with-margin li { + margin: 7px auto; +} +.form-control, +.btn:active, +.btn:focus, +.btn.active, +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: none; + box-shadow: none; +} +a { + cursor: pointer; +} +a:hover, +a:focus { + text-decoration: underline; +} +.navbar a:hover, +.navbar a:focus, +.dropdown-menu a:hover, +.dropdown-menu a:focus { + border-bottom: none; +} +h4.modal-title { + font-weight: normal; +} +.modal-content { + border-radius: 3px; +} +.navbar-inverse .form-control { + color: #b8c2cb; + background-color: #475765; + border: 1px solid #475765; +} +.navbar-inverse .form-control::-webkit-input-placeholder { + color: #b8c2cb; +} +.navbar-inverse .form-control::-moz-placeholder { + color: #b8c2cb; +} +.navbar-inverse .form-control:-ms-input-placeholder { + color: #b8c2cb; +} +.navbar-inverse .form-control:focus { + -webkit-box-shadow: none; + box-shadow: none; +} +.navbar-search-icon { + color: #b8c2cb; +} diff --git a/xhiveframework/public/css/fonts/fontawesome/FontAwesome.otf b/xhiveframework/public/css/fonts/fontawesome/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70 GIT binary patch literal 134808 zcmbTed0Z368#p`*x!BDCB%zS7iCT}g-at@1S{090>rJgUas+}vf=M{#z9E1d;RZp( zTk)*csx3XW+FN?rySCrfT6=x96PQ4M&nDV$`+NU*-_Pr^*_qjA=9!u2oM&cT84zXq}B5k!$BD4Vu&?bM+1pscNs?|}TanB=Gw z>T*v6IVvN? z<7If|L2rZi0%KIN{&DZI4@2I75Kod~vRI*C@Lrk$zoRI`^F$Oyi5HuU*7@mriz!*p z<-;A`Xy{#P=sl02_dFc|Je%0lCgxR=#y~GBP(blD-RPP8(7$Z9zY}6%V9+^PV9-}S zeJrBBmiT&{^*|I7AO`uM0Hi@<&?Gbsg`hd;akL06LCaAD+KeKR9vM(F+JQ1r4k|#^ zs1dcJZgd2lM9-ss^cuQ?K0u$NAJA{;Pc%#+ibshkZ%Rq2DJ}Id^(YlWJx)DIMNpAc z5|u*jq{^s9s)OpGj#8(nv(yXJOVn%B73xFkTk0q37wW$hrbawy4?hpJ#{`cMkGUR8 zJl1$@@QCv;d1QK&dhGIO_1Npt2c7Ttc++FR<7`t1o^76cJ&$`{^t|GE>K)k3GNh{I92zC*(@N#&?yeeKjuZ6dlx1V>2carxUub+37cb#{GcawLQFW@Wryy^!4biE!Rvyz z1Ro2&68s>zBluk~A`}Rv!iR*c@Dbr8VURFXxJ0-?Xb@%!i-a}8CSkYmfbf{`wD2Y2 zHQ|TCuZ2Gd?+E`8Iz?iUS~N~HT@)&sEqYwENVHt^j3`EwC^CsML}j8zQLCs&bWn6u zbWZe&=$hzV(PyIXMgJ8IdI`P!y)<59y>wnnyw-WednI|Lc%^yedzE{&dmZ&U;dS2Y zC9k)=KJoh6>nE?fUc)p+Gqf+QqQ}#Z(Ua+EbTA!ChtYHBC+G$AVtOSVNypHsw2f|| z57Ecylk_F}HTnwuKK%v#9sN5!#306#5i&|f&5UPs%mQXL6UD?a$&8iBWb&C3W*5`Q zv@>1IKIR~ElsV0uWu9j)F|RV0nGcyynO~Sc#7N8&dy5s~(c*F9N5zxH)5SV*n0T&u zzW7P;)8bX)2=RLHX7M(0tk@t<5~ql*;tX-NIA2^QwuyI%8^q1xc5#<@ulRuYi1@hp zwD_F(g7_uz8{)Uc?~6Yae=7b${Ehf~@h$Nk@$ce$;z9ASgp!CPGKrr=CDBO6NhV2x zB{L+mB~M7gB}*jBBr7HBBpW4LCDD>N$##iRVwR*yvLv~ZLP@ElQc@#nl(b4ZC3__M zB!?u&Bqt@$NzO|yNnVz`E_qY(w&Z=uhmubvUr4@@d@s2rxg+^qa!)cS8J1E~zSK)9 zk@`rL(f}zd9W5OveN;MGI$f%hhDqm2=Svq!mr7Si*GSh%H%hlkqor}u?NX!EEKQSU zNpq!z(o$)qv_@JlZIZT0cT0Pu`=y7aebQ6Xv(gu&FG^pLz9GFTeMkC%^dspF>6g-P zrT>xsB>hGDhxAYBkaR@mArr`GnN;R0^OLD$8rc}xc-dpJDY770sBD((aoGadV%bvJ z3fUUjI@w0qR#~(xPPScUl$m8|vMgDytWZ`etCZEq>Sax`HrZ}jk8Ho}u&ht^oa~~k zU-p{pitJt4N3t8TFJ<4#{v-QI_KWNf*`Kl@*@(A?x4@hBmU{bo`+2LpHQr;q$9q5K zJ;gi7JIs5Y_Y&_F-p_b%_Kxx1?!Ci1!#mHr)Vtc-?%nR)<9*2cg!eh`7rkHie#`s1 z_YLoFynpom)%#EHVIQ6kPx>cKQ_h zRQS~TH2duK+2?cA=d{lYJ}>)R@p;$hBcCsPzVo^5^M}u%FY*=oN_~BO1AIsMPVk-L ztMi@Xo9LSspA==WB&S*uVl4V7bBsZ6Ow%WsQuJUl%vOsv%FNx7`s5UAW~xPRj!Q^N zwi+UnqRjDntAR@;SgfW*vp(6Brq42&k|Pt0u7@erYKn`qB*Yt|l44BpR&$iaU;sM- z4d^4IlC0K*WWCuG6&q_xHzvW8D|?VmP2oxsjM1iyl%%N4$e09kOp@NLPtiwN&H6aA z-eTa;a#fN{F^O?WQSqF~OEH*?dP|xqDK%Li3CQoKxK{5cQ&V=BV@$F7Xc#FxtWojs zXNfkM61h7$%AA;DPB2qoM4Ov7+011Nf%sPRE(aRk;t@!SiLC) z(4}(2HO9bnN2Nq^J%e^*xrU$#s~$RKF+`d5K(ClYZt5*oeM)3>R7_%elsPso3MS`4 z=E0Mj$&@IdAbalxm6OD4U#Myq|K@ z-&JTzbUk*Y0-^+{&H*ME<4mrECC04R8!ZMC(2?u*ebPc5H;tpCU=m%_jxw7~>F%j@ zrQFl$N~Wf`Uvh+X%>u^=z!V8t`pCG{q@?>vOLA0Fl0G9QDJnVY@1Ddb#95Q{QE_nz z(2-1F6PRS~8IxqP=wV8rtMRU$!gLw+F;Pi+V=Q2cGRB&cV@%1(K)mFrc%%OB*-1@# zFgILx%zA6OUJtY}rKE5z#efjS0T1cTZVdO+9M=22Ow*gK34rH*)?hLxWC7zvB>|5{ z#sH12*7O8mIkT%*9G`Hk>dLs;G!k%{O^NzUkTT2tE?TUH)Z}POWNL~_)Z7`ae_Ylj z(7?KJE)jQ&Hb*3o*rWtwBJh@*Xep@{0}KNAUT+2=21z$2x`_$+QVf~#34kTq)f2bC zy5teaYIF&ri#6S?KM*c=&h^$+?f%Ff49eYLDyV~)MBo$Pac=%%%@&IxHZ~dv3zK7v z)+Z&!aB~(1vu4#BfHILT-f*QjQFJ9zQ(O;j%x->){2xR8tH4$FUnM|M7YE+2!8H+| zWQx|On?W8yq%DaSP+~AC(dGnwTuhWj&oP~wvyCRJen%=uy)iDqm|)FJ(pxO9f_SqD zCJAN`7%eq6S|0`S9FuB|F{OY|rnuN6A;l5}g3RfWXkb3jsU|ZpPHK`V$znApB!a$$ zM&b>rphC>h6sWK0Bt38=XbW>{Od`+XNK_^W~`uM1%SkU{?CLrT| z*5rU5a4DAt4QsU|SYaF~z_MnbZd3}WFFoi`11Pc7q-YRfpk=(?HFGY!oON*L+>FN= zrpV-2sAV;nKn7Cumed63yhYD(iyLEHoL(PiGR3;=k4uAd$Ws$QzZ>JBRtl%)qmlt( zlrcu1tdC7hu*PwHfTp+Wtez}SISAlE3{#BBi@~MV=s9VU~oa*A29jU;4uHLv)t`=cj zMkBD=0}Gn;Kx|?3|5QxeB>h7H-63>M1rORUPw)_81!IgVnE33zbVFL~|4d{TmH>B{(ST?=mZBvFKDQ zs6e71u%5ZNZgM&lh)@6d3N{!aL268{00aWAef0lv1i^_}z`hyP% zyasc1UyCFdAscUwN{$1kE)jexW8Cx^)1woB65NEk+OUEqN;12DT?I)dX#Iaq$3L>1 z0{Z(M#~c61xyK|v7Q!EnR;&(y&k3ik}S zXTlwpYD`!>eg3q#=~2@ogTnwcEEv)N8U~)gNue|5Zu9Vhq$UQ zm=4KMxM#pU6K(*VJ`HXtpAMkY0d#r@+&Z`cZaTnC2e|2O?BUZ~t%L(~5I_e3bPzxX z0dx>R2LW^tKnFpq!O&_jzy$+bFu(=7JFw8*!oumUh8A)!p+c~``Gq=nX{h@Ft%X3% z5Wo-u7(xI;2v-IbLfjP=0TLY`(Lp;p0M!Ag4nTDPssm6Rfa;(#p#T>OaG?Mf3UHzB z&MfAN0W@?*-1IoE7(i!0*$e=k0iZLWYz8zr1Dc!>3NSJ7geGSI+)RL*32;EO5TIEI z&@2RK76LR20h)yX%|d1ZTo}NG0UQu4Bn;rfLgIqB84nAECszh=Krr33X>d=6I|%Mz zxI^I9!5s?s47g{)9hRo&)&V*omkuiHfLuBtmk!9K19ItrTsk0^ZaOp=1PulO91uze zgwg?_bU-K_5K0Gx(gC4#Kqws$N(Y3}0ikq2C>;pDE*Ri~0WKKefIhllfC~Y*5P%B- zI3SA-$f5(X=zuIbAd3#jq6+~y9l!xibU+gw&_o9`(E&|#KocF%L`hz;)DWmLP3;5fv}-Kn^2%lD9|PpXcG#w z2?g4O0&PNpHlaY9P@qjH&?XdU6AH8m1=@rHZ9;)Ip+K8ZpiO9yi^YTHyZbQTB``tr zgIpb(AMAd(*f?muyEF4$ViPofhWp)2_v3ym^WC`x?nk)$vC#ck*h}=pfDBO)G+>I#QjVRoW zDBO)G+>I#QjVRoWDBO)G+>I#QjVRoWDBO)G+>OYsYl7UmCTO7>(Ly((g>FP{jT5xc zjcB18(Ly((g>FO(-G~;t5iN8hTIfc!(2Z!3d+HXsN3_U|XptMyA~&K%?h!3=BU%JB z4s&B!kI%_aQR>IrR=x#+$+m z;mzdD<1ON?aK+rWLd3m{XXDlKF7tlj5kBJc_#(bPKaf9_AIz`iH}m)K`}oiCFYx>M zm-%n=-{;@vV?KeH`Llwpf*3)(AW4u1G4l#RpWvL}qTr5jrf`mMv2dxdS=b@mD?BVb zC463ZN%*qxvhY3O_rhO=4pE>e9OBP801EGXWnOSFyAwG zTv6*$;wj=_@l5eN@nZ2Zh*qaSY`R=r4N>V1@qY0M@g?y!@q6OWAO?L){EI{=882BR ziIpTnM7d02lhi{L`JCic$vcvdC7(mg_&<_gB)>zHn1$%@bchNskS>9k@H5g)QoS@! z+A2K_vEG-ZuS?&8IPWLY-yx#=u>zUPB{q&{POCP9RCmd^r+u&(rp@QL@y@~QS|_v!Z8?{m!OIiHIVSH0@lOL9!ke`vC zm%k`~TmGs1M>&>{C?twN#iNRuig}8ainWUMip`2>g+Y;`$W@dm8Wf$1Ud1uRDa8fF z%Zkg2w-oOyK2dzBxT(0M_(gG7NhzgDwQ`Jdsxm}5Tls`?vGQr%R{`icA`e!hMW`33q-@SEfp919`B@V$_Hqg<(g&v8BX9I=vHqtmmC?CQiTI)~<@i|)VblQ3H8$=5wV+lKpUN(tkX3=CokeSoksl^f7X+{TA zIF)6dh2AY2%Q6!H89e$99_(Y*(NEJ_CXL1~&@gHZ!{tKhI3Nu-(Ha=IyBUSBv$eHT zgB60#)|^Z&R`8NoCM!ETi&2iFnc+MaF`j>W($I9M|{Fdn9I0?i2Fo&$U{Z$8c3Z@s||tuw%~3Wi@-Qn;%~T~t_BQle$H z(%4@xz~aD7*k|q?4X(!xeC$IzBLc~&skAbfW@1}K{oBs2(=e?$os8k2kr~4h zJ2O0>T)++~{L*NRd_Vq^9U6!SiC8JPP*C~V5;d_4fTOkv@S@>s{2b%v$CGe8J!BW$ zWJe|m8oOG%dsIDzy=8keLkF>xe{|R014mR+Y`{OWCs<;@^T<4GVD_^hV!}nQuYO;{ z5XCB*xT4s7O{^guzsd)gfXJQqzy2L25&H1IC#;IT7k4stQAl`4B!EN5{B z%pdSc|Jk$sj4=3m_)QJ7aLt;9j9?+l;Lq7qmdS+Ivq3g^vuWr9Ori3g?wip|f$O8$ zKoRc7K@j_H<&QM^hJ3>(Z90(msVr_2V938oGun{|A+`@ijA8@%`OHKb zX4RUNno+1Fsm@K#$_0FLSyEoIDzhc4IalLA zb%1SMvT*GQkdEyv6C56npQmv*NZ^3*=Jo3^6G|OS!ffJ!A0cyp)U<7ESpTewESXBe z$ZR6j5FVLIBA1gywK2K6+Nce~K6us!{FM628+DDZYQJ1{Yuj%-_7@*4Jyh0S(blr7 zQ-nqAuHCuK`7N>MB2OiJDPqjMF*dWAQ9BcC&ID(IiorKn=&gOoj_sZd&SY^p4GIN6 z$ujr8`Q{!onZ=4VG(+JDv?mkDM~vf;4L=7e7Nj%+!^8^nu>vGj-o{J^t(iXu^z1a6 z0mZ>6lSYiTBz1Onc}b2oGRqXbRTVgdgMEsSh7)?(We#mOJJ+mOJP0 z(|Qi(A6B=uRoAs@&vhI)^SmmM?4jyV%qZQ#(?JiOp< zO{!&p^j-9@LQu~-JXr0BLP+N0wPX}7F42$#vX!5n)@nGY9y%j9*xJ{XrX>k@D<2ov z;k9@ap064LgRzKg!4DG~FhVD&S$f$cv~yq~%`67qSK?$420t)W6Gjt0(Gb6%U_j&E zc%%E!0Zp~w;f&=Ih*)jhQCFX?&9BMdRk$mb@co-hTT9zZMTPrL6hE)Vh1dg|@K!K* zTZoNO{z3a$X(ofl(}7b#UtVCzXvSV&Z`U&KzyA9B4F4p{ELy#Kk(SYcNpULjSf-&I zC$NOGes#q~y9(8uDPS^NbFd%F(Htv)nK+TfCuw38tlM_BUwZ`qLE~4!4&lS}a0Gsy z)i@LaJOb1^3B(c{rnOE5SBkCp2Rcz0O>36T0c(Z(aF&Ay)hz3moP-^ynaT#zZENX=Dem$rBj#FkIX-f$24$w)OS~yvH)( z;A7l3ngKsZp>)h9ckmtOY_fr@okIf1XkZJh%-n6NwH5?e3U*p|sN8HWU{vQg zCL+RkEEHe`i*@)@mf6%Uu+exiEpRDX8aihIL)OnReaLhgw+fiIp;iYz59ArZ1N^$W z8he9^5ti4N)s@r@Zyem{Z|+Sm1c_1NM_Js=uBDk{aG(Y}0$W-k%aA^j1y>(PYAw(T z+zKnO1%98!@D$>A;fbvRM)^KWHGP|@VZn;bpoa!(Sl4WS1|n(q!%|jb6E0=7PP@Zy zghoFgO>licKEUwAAHdZF*9VMpB6Jp?IRcHAdma(6LTQ!$uG!tPgz^r867LH@VA>{RgLukD%WQ6OsZCj^x4qz~8LrOebNhkr? zhA-l$aTnNsJcl$2$S9Iwjw&rKE3POGC>Jna&>Jp23*GpIQ^=f)f@R}>BQhZ34VuY? zuC(OB3vdOMU^W>c_GFn)xdG!Q_8Z-3M%jIh-&wc2wL|T=E9h*@$t=;PE#qgFWaMP2 zop%M91+ATRTE++?hk@I073jMNb_UCs&9<0cGt&Zt&uwAA!5GR1s|QvN61bM;yqFCe zz`4P-q;?feYH=;olG|l#X$fGIj>qtqNu8Y&vpO-(hm zc5O#vb9>EhY+ptD@9Hhso7N_RG2mP_3t9*N6mMs3^hANHvM2Ut83!nEPIqgioI}Ap z1!jzd;1ZSz)l6Zhy;JQJHyHgbL5aKZA zb(hGdvC@4#?Ry)wjXk9YGCG;OyqzUk>a3l0&3WL4tcPibPCGDuVP>#WUrwqV58>0~87#&v_za1|68Z4FK;8kSI~i6PbuJ&@4!#2{Vqkt@6*CBW zq^@pPT}^!eGrVzlV@XL_NqKPqQ_g}FCW-|#)7xu1ZSDo{#df;4m&vN%*__AV_vnc< ztWQ9f&-r{KOo>#5r5CZsjn6eVW?h8olB$@4yBkiYA0i8Ii+|h6)AqA!ybzBiW646s z&sK&@$s>5K20Z3KVyGY+Z7N$isbziwvcf!l0qZni2*D?ux8bmZ{_kk7Z*FE>ejwv4 zbdHCs&{^n!r=t+A@o*I~+Qz*6`kiWWejWLhq>&kaPQ)SF!4UxyB<#v;-jSl>Gy!K9 z_c!nB>ePHEWR}vf9AoeXS}I(AX~Ua%53qTT!;@|Wis8qh2iyWg3#%=of#GLn7MRT{ zbECO46BI#;)taIiFG#WW?AHQuh+RiB*5cfVZ=^pjXXMwjsOc zkew0cLXVfj0@@R=uF#&k)P3!ms3YH}Sa6as z-+zA+GXolCB%%>8a~>xQfqOv4<#Gf8qw+ZQUkE=Sl(6)xtKZdNR{`&U2{nTY%Z=Gy zQU@?kaW+rLjjCYpK2>ky-cG170gvZ*bTZ5S3j(38Pj8ECkL-!*sp+ZT(;%wrtK`(y z01g4q*A56nU{!-dJel_Py5?r>pr_+!zTJ*f@D^OGV%D(a3?88IT_J;)u-qaoyN@E#8N z^ERHLWduYvems$BhX*iN))}m0fC1Zjm{SewU=_fC!sS8&%w(Ed<}e?+tO*DVTnibc zjb?5OCxLy>IcnXjVQj0odcrtYOZ@ACHWTkB^Kz9)IrK@#E)UG?-_@ zyb8?I6c$t!s-r5ImuYEjb4^RDid!giOzq+bATcBw*$R$JIHO+5-eYcF4-aNs#yc&Z9}$OTab3Op!K zsi#?r5kN3(ctA*k8KJ|2W*Y1@b#+WBhy@XXJaSCQxr>XI5JASqMq`;Kld-bAz#$00 ztpcFt_QsBe-J-5)tZZ$AWh9Fys_?{Bn4R>8<~U#wLVSWzwKg=i)@Xj{dgtn?uS85y zNkc=G_ASRGep6Lr12>{F&gJADOr+tAHu+dj#*69~_v}8z2!d$r2jgt0YpT~ab=W(b zJ47G74Bb=05~M-RRIo}0>@4_3J@h$l%(1K^1eme4Lj_D}-_=l8r>SE?z=CZ86S8e& zIUj#3z}tqF^W95v5&=;zj_qMSouCH^rw1L}n$iK99dvpj=Sq}-Dj0CFsFSua$FYND zPO;olnE~&00?SOH$8oJ(gUJSmPspUu-~}@~tUIj*+5$_hX?G^01!GoJsIuU3WGsOG zeQ|v1iw{E-Ah;}8oko^b*A#PdasuQbgi|n#U^C0)=GoF(@|bS?1w>+UwkN0(S{Y$D zjA$O7#}Jli^7AV*8gm0cg@;4M8|<=lUq&}-bjUY<-uw33dw(+NiCU5+%q}j@)-ak$ zV^=|)i7GM?C@UchsS@NB+89kuQDJqV8u;ga?>H6f4(GwZl=v*SS`x%#fq>y#dXDBC zQ-e)v&&jOPGW^b}cJMHP-VQ#;_zG|&m|oztI3heD0H^c?uuv@gfh7oFhvfqi-60R*koEXQCOtVrdnj{zmqE>_i9bPb`GX62 z%G49LQ6IZ8mJvQn#{n`8INIQ-m3v0MgE_nfH^4OB@{rAN`_R8NF9v=C!@fh5W57ik%-Mi>^{T} zAofqh{)IFXkmhluc?M}pk>(20Qb_wa(#9a|5E``xjrtsoo`yz$h{jApW459(SJ1=L z(8JwmtQd{mfyRE0#@D3Q85wBC1vJxu!iLbSwP*{{<~*LE-IaVGUYz04?rEOYWd2m!c<6qo?@jsR*<}jaD?G6O-_{*1Urv_MvB%pml+0-2t@jI9m56dX`1&r=tz)(Z<)&rip0N z%V={r+TxA2^rJ0KwAGFxC!)wO6uAUNnowi|iu?dYeupA|N0EP_ZFMNhA4M%e(V-~% zB^3P~idltXE~D59DE0=@uRw82P+SL!yMy8%NAaH_Lpd_MixMWIgnX3n9ojw$ZNGsM z(^1kml+=onXQ1RRl>7!t{uLR=BI9giT#1Y^$XJYwmyq!-Wc&=7#voHYGQEaUSd=mz zr96&O)}tL1+CifoImrAJGS?%^Ok|mbEOU^h8d<(XmLX)VM5&c1Z4OF*3Z)xR`T)vU zf->GgnWIo<5y~2mc7~#zsc7f(C|irN3sLq*DCb3#%SX9wDEBv%>qL3aq5N=^-+}T! zK?OdjU^yx%K?S!^VHhg%Mn&PMC>s^EqoT8@I0zNjppu!WWF0Emg-U)!rK?bBIV$r) zWihDiYgDd4V8{4#1uMy)hzZ9r`lYF~xgO{l#ab@ZdokJ0YwXm=&r zeFJqphPpCP*Bhw27InXa_PmAmhoA#-=-?D|$P*oU5*_*o9af{m&!8il(UITK(dp>u zPw3bW==d&l!UvtWicU^IC&SUnbae7CI{7?0wF#XXM5mucr@PUa{ph)JbXJ7UJ%Y}) zq32oj{2g>Y8l8U^z3?`=a2#EnjV^wUE-BEZqv*w@sDCGV`8;}c3VPiez21r5SdHE| zhAzjU%YEp|W9Z5!=*=tWYCF2tjNYn1Z&#tWucCJX&^y`a-EHXIBj|&T=z~r)@CX`s z1%0>_efSdkh(aIzfK(Dxss|NMo1u%aJ6M?c1+A06nYN$97~(e0z?XMgl_8M?Cr z-T4;%`ULv*F8b{&^t%cDu?78CgYHg8gHebqrBFBpTm7Eh6pu&oj!^t*6#son@FgXT zr-U~tQ3WOHr9@v*USlbUQ`6s4%nFKWqQotfWHBY3LU{*JJ_5=olk(j``F=<#Kc)Oa zD8KKhhlVKsbCjxyQct7;HB{hoDzJ@W=TMpwO1q01b(R|aI5qkkYRqhEjDZ^SCH1hJ zdbo-j8%>Rir^YX&#@A631k{9TYQkx1!e`WkFQ^G$QI7;tk6fZ2y+l1WhI(u-HL;PJ z_$4*z32IUbHR&uhc`-Hl87ky)D&!!g%cXR`QK3RAl%+z0snEx%&{}GS7d3MX71lz9 zy-m%UOwC?Q&Hj;^6GqJ;)Z7Ww+|AV7R%-4`)Z>2C6C0>`YpD6}Q420m3l-F&`PAYo z)RIc-$w#Osd#I=Q)KkgSvL)2hfz;EVP|LScD>hOqFHx&9sMYhRHBxHrIBIPYwe~M+ z-4W{9)71J|)cQ5l`hC>;@2CwTYQq+4!w1yHd}`y%)TW8lCL^`!3bi?w+FVC%iKn)1 zptk-%MFvrkH>qtpYTGp`Y7Z6l3l+0~iuI&oXH&7yQn6`NY&)eNO~v_BaX(P;CMy1I z%CLemyh0@;QrqWI+drieuTx21P|1aqv5PWwQz=erhk-KJQr7cSY9f`kfl7~~GJdAA z)=@jnRCXbiGnL8}P`S@jc|}ydlPWkt6+c52S5w6!RB0+zrlraiRK=TAivl7{e^0k;pVIJl=A~4Sr zmb^S=Ab*r20=5#I5klDC;VB10R?)*D;Aab@fkPikN5!xh;yZTFK>k%nmXhqoQ!w0D z`nqozt^_Q@9)>G(x>pzi$Zj&3k1q>vKz!ymnp_qFm9B;FD#iR^J1oBn=phB{wUU8ByI>H$ zx8!$q^&C71XwoQrfyNoM=PID%C?&UCEhwxkFVqYV5Ia96*Ay3}8rg(L(}Np?fUSV< zJO&x*C>!j`DNaJG(1B7|a?Yb+Ls8lddmB)K6#yE|o@S4?6&lz_NK%B zkq5-McvwqBqNhLl@$vtvtKdW3|Ni*N)sM7Ti$$=S=i!I3M{ifpp6J)(lYyQ1kItoa2CREud1?qW}t zM4Dkg^u(WZ_eR(ZM4m(7XDhLZ?W2K;DP&7Sv38K>`~~8??IrDMDYinNha}2FiOrT> z8fWDINp)=E?=H;RV^ycIj%P?dzqq-zv{ikudG9{VMbCj6I~)g<*PUTb3Et$Cl1&4S zF!BbzGapVPj0g@yT%AR8J2pNGeYam|7_VzY*!nqQF95f6X_??}N zy}c^XE;S%19?&dkI$yl~L4z+~*L5H4Us%Ws+y(Fdhs9L_Wq|Ns$Xsne`9HBgz|0BS zI@STA#{FWu!U-$<>onnZrtTk~;dZTr?qf9E#+Bd{t+{3f-o#en+%_)cTwCLKgmtMA7k=EzdSd(S4Zx%j-keF30X!bM3MnU- z8j66_NCc!Hx&=wlHNVnQJ)A2URP3aIH7R9BUVB!JhAcZ!a5U#=){%f?FPu1c?7XP9 zzNX%;g3X%JI!)9Yi{4y!QB+r42wTR5h2^k^M8=FVwk0x#IF2}DiCZ?|Z$P`9YMsJ2-1-0Jt2 z_iqvv*W1hNYCD9#;9S?}KM!Uf$~#;TaDY6`&#G?E?Nnnk?C&(U@6xtku6wKg%HhVt zEeG4Mh9EFTT+L%xjVB!0tF3bl7)na&HF3|!pG&ydez5sa(-FM{#m`cG+2uf29T+j|ZIiwhQQaBtkbmc4h zV*1L{>(re1uZ-E4u3bcC^U0g_kh{yHmH{o!S;O6yP*aK?eR8GlIrLf!WX=NQ} zl-0KC%4&`Cy2I$a?lkf%Dk~~fPAeR#xB?(fU;`Fg9OsoyEfw9lO~izk`a33NvE*4H zDaYHQ`j*(D3<1M2&fB^96=_Ym0dLN)Eomrgs0^@IHq_MD4nFDl(0}kr=ZE~#y84O+ z*T#55Rl}~@x;H=cmzD$PU^(bJoKBC1kexsZf?x%YLg6^$J~snT1>~(@NrtTWEt=dV zRujbWz^k~ed>8_3pfCq;1O%)v1quT_hi*GgD0fz6=Vhx&xga~cxxGreOSl(62#Z(X zA$BiBT+4)mHfOx@bpGk=;~J-K=pethAZ1UAn*0C&Z6t!9S(Tdu{5MOGncLb~rEP=Q zA4JN25TvA}nhUf}-N-?Hc6@$JjLO&$c~UbNA;^NWaaGzbFvNhS7h358Tb@~!1DmVx z_GH7kgD!P2M1wlDgH!Yx?Ti(0x{x0qw<&$Sdi|!Z<8fM|#({jN9*5Fk5_<})?K|KU zmm@-em$A+WVi)4C;e?7a!XImBM}#9{cW3Q^g1rIK4463J7MLW(%%QuEyEkF00SI&# ztib=vkwqK_V2*(>_Fql>G5CnGwz<5euo0wxz#mR_)WCtYqVkerExAsv^Gk}k5axK; zxQifne+6VXLfF#W&|Iq}e>l3s*zU9;pvZUhPy=xAB$!U%%Sjj>?+L1FtLmz2vB6R7 zKe%3i4bI}~(yEf`(g3_6S$RCaKj)Z+6gn>QkLJYeGpK>p4KX{m=V(cx^CCYdA%9)G z%9#ec&S$|3=!WwSJ$c>fO&aGJJdn|Bwx#C>r03)dc5? zAQ0>a{PHX8IojnXR?+w>n0uP|5v4zdlM-a@4YEOv+h{nRk@Oqv3y#+|w%B&(H3302 zFb9P-psFeh%SwwyME)q55Ke;Ccr1+{!rmJ~ZfWK3!4VwLFF=?C4hb%2TVh3I(i9Rll`K}nIa8lYHz#W$V$QxpPX|K7v9$=H{JrZm zcO;b$JTV5ZejGomcJT4@usihU*V?LTTTQj97t{otb%O!$v5Jf#YdC#@z-MFdPg<_)c3024Z7yxZ zX{0cYR~4RM2kwqx@c?f$?fNN&-YH+?3Lg9@h7}K-&Vd2f-t!U`HWFZyYv51X39AI~ zBX9(T6FB=2;R#CsyAn7C`_jOmcwiy~)DvNo8CR06cq{ZBo^VydlqG%zmI)R-aLjT5 z$dyKK>5V>R)dUhLoL@E5fxJJ2r+RwNoQHE^{mbI%NHP~hYPvefSlepSzD2Y|_7Y@a zY9_B;Mtrq9a*a8bouZ7Kyex}qI7>K%ZEmcoYtnoOJ5IB&!x3QPO*ozPv>IsY^U4*> z*B)%^X+5Emg1U4M0T>=S!tD|Oe|w&02Q^B^RHqOA)%h%3KIB*DR6=!)KK+QMYa?F1 zolmHPzs$mnI&mQlCiH1I%`|c5y19|sCC&VdHw&)4qr$J?mv9HZ1=mZYgS_%&!Lp3y znk9MsPa|jcPgEZfcCbf;nEB;%OdZtXwv~GsC3X${ug9SJyOXFjR#4I8w#6b(t)~he;onKx4+XoqKb%twrsn zZAAyN4`l6wgH|(%)(tK@K4CK-GAA#%E)mvA&e}}LB zbPKXq<#~VgU-fe&x{oiW!Qm^{3D50t!n3=}wnu%nO4-cj7ufO(*=D<~Nqwt`5sRB&PuCXhsj@dTi<<52H7)AFK>?QUJBFvcpvC)#G_5a`ys+bV zK%Y6Pd$W4DT9B1hT9&1)sv+{@MTCu79+c&8kM9}+SLzF>e;nb^MU4(oR}p)R0Md691%r!J&2P;SdP_oLMFu6B05;>kLWc4)lfKS#W5?wI%|hoq`hu zfx>*xp@_k|@M(qn0}BG5U2uozAAEj+p&UwrwSy6k5G4?GJvc;fo9Di~NbR%>7R`O; zDYJGxI8E>dA7Mun!eUxuWd+Mv?U2Gj!*NnrXHTVJbU#n}+OZll+_5Y9iNS;+y;7d? z0U39NOnr$=5>;koRA#6jd8DT55v}v3;fIx1->hl6s;zGAs%wRSh*vrmsjKW&cDt&} zw!3n-W=#W`Q1glEkfXx}Qs8t(5j3uAvN51y4j&X3@w_#tyW_a0#W72@XmpdFU zwJ9yH+wscx?pEEqr)oTK)^?2gpr4CX53 zcPo2r+|^&z-!C2~cl=iL+i$A+vuEqhsqt()|4CRs?j#ddlj!)ks=9cs^W=y`S&tXv zr`qw7n>R~ts_}XJHWt7kx;Qcy=3~uSSTJ3~f$!iYD%?V7I(K0-txXmcqySZXyRjTUA+J_CRG|P7^tz5RVVzNI33P*p{0cvi@F5gCc zd9^pcZTn6w?|%2a%F6e&m9M>#@!Fp5nmy`T)iJ zi=lMC;hb$h#99HCFYoKypK~Bm9XMDJ$omVwLyP3QFYmJ9%@>Y}x)1)@aYEgJAF9c2 z)i&ppg=eaWmym3&;~XW`(=}vo>PGl*;8;06R*8>kPqf&4t^!sXg3 zyyb<%qV~NwZ_jfNI?$F?O!A_$YqN7y!S&8$^IAY1T7g3=@eIwg!b&{JjXj_hEbf?M zEK@gLs48#JHgOB#!m5g1=*G$8(2d;8w4Btc06Xa<-6fg9;ABVdud~@CVJga}S!k|L*VRApay+;r@@byUz821q4~J zRS758;d>ePZy(nsI9jUgbCvnt|COeLwHvZ3H`A^ILubet?!ZuCk*cVsu&zYI9sA)v zGJ-=ekJDBN!^g7eup%3bP`Z!i!?_^tiz8UTLA=U2kV(7FZo5idXSW0S-A-#P3w{Nj z#x1Ip`*!wN8(l|0ir~;uNp7CjIl(!ekHdtIfqrddhhbmhzSf3??|2r^5;`V0C-8G2 zp!+swo#B{R1cZqcz)f(j2>j7O#ZZKi9kN3h(-{K00(PezY(t3a>=TKwvclWo?6?j! zLbP4j$>Kxc+4nnyU_25bKx%^sscYZxnb-e+vHdADl<>_>P5x zpDIf#N=i#L&Qs1){L)g$sB;VLEp^p(wY6HuDaR>(Z7pQfE%w4(?KAKd+3>*d0H5oW zaByI7fRDQ{d__>kl02Nt-)q_4nxIbDo@23U$t)7a?PuUwaDneIoL36}2_&4tfiFUa zAn?UGti?3u(<|zq-WQ>9P{VEf$gcA#7t|Nd??2bAb)dmE{=Qf0uU=8XY8@)wR>FsN zBLfiN2Ty$z&FzfXNgk*?ya#4VzDi!pZ9pg?WGC|4Kv;H%(9q*lmdqijRqPr8-i7{#0a<#Ka z5A34sT|ZkS-?m|P(&X__ha89P75E+j!zU9`_u}vNP>7p&4*P8`_~JPv#&?x#Z%=$x z0Jaepk7N=bf8zK}X)mnIE-WN}kU#tj3$rT=?S=NLHaPY82mZs~Zf~oy7m7Y}{zutT z)Rb4N$*aw+C@5IA%paJys7M9+aXkw`skXL?vNq5S%{6xW#f$#%HDzN(Q$=I3y>OSP zBQB;P24VoK*@;6T%HfdV5IzCM6%K|BhVbz;JWYAxgze3^6Pz33A9rH8EiP{ARDVt& ze)xgU1z#1V^kEjq555e8fJoOlWlN#ED>-F_g*&q|bJGh&`6b2qc`BH$^(^KI>T0X2 zYqckPp6|K@8%Z@yE$yn#?AHIo*qgvNRqXBKAkAX*;*td0q&cU`A_^i%0XJ5GB4sD+ zTiIy~rL^h3rEQvKY11T4_kE*4Tb5E4WZwiS2x8q)@hYHl-79m_N%8kgTD;!(zVGM% zH_{|0=ggTi=giD^d7ftyIjhwQxcS3R(fs)ulJ3q{k{2{UIQbT(B{>tpbN^YU_X^7vwhtHfNgl_b`YXRm)J{q|E5@CJ!g zqd#cHJIZvm>6|Iw1xR~&nWMOfhfi_;Qix(^97Aj)aHo)eB0q#H`mMKdbF;H^vRQ=2 zVBmv;+4#Vk*eU5@l*vE&JE!cgMz`2(7MnVsF%yp-?P++w|7v-X+Z(?wB z-|(ho*6{Fdb+_7=mXWfauYL@R9v*I8))ek1Oz})<3O{CTYVvcRcApmYC*Nz_E(~^$ zU|>Zo0g)MC>L1gzAaWu@9)-GGxE>E)aEz{EsPn)r19p)FYIyX81`QdH4=8}eMqssG zKt5B9(1>>n`XOm!@tl5Ln;C+#%^Q^l^1Zruv%mNQQm=6@C$X9~_U5k%z%Qh~zgP@= zf8qV#7|8q=jh`EDqWY*R*It!(U)Wpz{^Cbrw~Eq`h1eqeq1;n$ZQNS!-*wd;>$|l) zDtU{Fe5u(|pS-7>Llm54^d@bVd0by(#215ydrtv#`~HSdS??add23-sB}j>^dpU_i z)o{WWG=7XhBkEz$V7tGJT?ZmnuKWA7vEBVKTwptE)qaPlMA^oo@F=7|O%asHB0bQr zL^!34igLy6RU;+0*Hu*?#j}#raf#{v^dHJka0F;f@C*j~i)ZyEBf6^L8sz)?e83)T zib2jdUDKV|o#^|E#?9V(Xh&@H^TiIHMxoJHz#q~55^kb^uG{XX+2P%Z?nE4pA@gM% zE;M=?eLeVt_9fWVAamn)*s==J0r#r|L%H`I=RZmGGWI}-BQ?155^{-Q_FUpE>~WER zfyj83q@x|f<#GgI*ulLAbz`R<9ws@3$D?FhQzcqZqz7IT3RC6rJ=8r z*C}53n#6Fmi40de>LwDBhH?;3oQ!xvy!#OBQ)FOl6lXa$-n`ectPr*v zko3-Sb$L14c5{@dD9xFes7f>>;gswwY&W(sDNzLyL@esgShSB@J2moZf02*-O+qxD zgPwz|a;Qy`w>C(P-NUJSh%oHbw{DWzG7?K;h2g?5e7wa@XvpnGEm>>I`mp3k^LRWDvH1T?jtan@DV9 z6B+cTl=jWjkiHT!D1_j!H|Zd3c@Rl)q{aGS>LAfbOpv zKRSdAA!3;yTFATI`*{c*atr;zyNPPpM{M~62e22_;1iA#k#G`>6bB1-=eswvzBTw) z*0UOEqc44$JdOT5crfc%NOLyGgqMYvMdZmBaRfS-uIp2wzYL>Rfcpt0Jq_p242pl> z!OdsJaBibJOLTf{(-7KMbuWpYP%ivB>{rrHMNWZcWd?(%-)~{_zvhH3o)t=AJSeU| zGO{a3uRnUmdnSPN`XeK~{wPe~py3c4*S8(vSD+aXGq|$){A*k{V!4OOVNqRONpp(| z^nmC(ZqkRar^0*fsc62N@8(205-SU<)p2gVJAho4ee|)YuJ-;BwH!T6-WDNu^1-3= zSNNXuU>rV)D>{j+LQ86MbS>A-yZQTeT6juyG(TyQC|XB;(1g|LIC7Z2Eka#hTRk_3 z4IM#;=6=9ZHS{n&EQ)65u8ZbAnk3TIHG!*zz>wQpT3syr-n-TJnUZu9im%`Y_HcdF}k_D~uF=<@})!5YYhonVs3Y zQyu@&N21!gk|uVpN&cetzs?2A9p{>aU+>$WI@q7M!)T0NG!HYuk--+#>Uu3yT{J%# zSMI&0p7s>!*lBt$Du7w6z=;4~fYCOrUlNOZ?b9&!&kH?^7D+El_0vhPdbHBfaiYJY$^ zPrx*ddC;9L=n6IN8h2-ztUs0bi*EHT#vj~fim4&Iq$)n`ar+=o8&X~P@`35|dVDcl=B09QZcH;~+ee~(4 z5nb2_2K20<$h;5I++h%^t_}vFLfRHi8t&XzCWgrnWXO{|Ka-B5uX8I_uUWBtjWjJa z#gKqd|E|3i&XS^Hp5&7x5>JMbyJ|Lj3NEr-d1Dj0g=k#l%B5Nk`4L~wjL+!WASvDd z9Cgq*dQG*(w#5<3<;68D&X`Y^zdTSC>&$W`a;tV$ZoT-=^CaY$`rw^eNk{mtw|+{x zqb9@2u!C2Knnz@vBP+@3cG4~_Zg*a4XJK||cz9_&G!VKYj5^r^nLyWy!bIQIsU)`m zi+PRiB62RrV#*QinX`AqG@9?xhI-^GdW-1kYh)LdbC#SuizxiUmhavt`GU4ZkOM}A zd)Vbe2K5!RWDrs@7!!~{nMilhS@c6S{SbxDBG|zH03z1_gjhy?E?plKJN{Mhp2<#G z?5FF|HAlVz0{!DZ(5I!{8{lp2h>6)j#m_y5nPipB{Vn{}`b=aPIdU3>-Xv=&QBy*1 z(zO^*XYpyVnL1GK@FSGC`>P}yi|G&XXy*<%rr$(M-)Cg2>Eprs0B zgP}ULhGSvB$H-&!(JyCFA73IG|HF_EF@TJuMo2JBqi;n`roO(IS86e_#gL_Z>!H@8 zdyY$sYn;^$Xc;yJ5QPaYFB!wScmle3N^ci0DTRmtx;I@QF$*$fswFwSw}%%L^NGSL zk;7Ktw6h-W=rA2rxJ}JsEo2(`^;xzoQXOSe&z+O2(s^lACr_J|8YRvA) z%+D^c_~lq34}eGvf9DQ(R-k73G1^!WUQHf5JHTc3v)BO4P&=Kud3GS`?iA$Pi%ms- zG|)W@f!#58?zEG@;C8?M0VWw~YlmG73RocNJRxgpZ-V6&h@XKj@_t5Wzb_I|&6@TB zWWTH%dnqyEwE?7v4INC$2q+Rf|JXy&cI%XEC#~E2-t)a#bN`^8eKD?Ug7r9WhpZip zMi9^3y6(RU?I~-&423siei3y4bLanCkf|CqXB26Z#yz6zpprZ_gg)^lOOorrLq^Ph zSUXE#p5qUG-}c>^uccjG-3OI0>0J^!EEwU&f6V9CKeuj#c8ru3gN_=!mmE`L;D$iW zIm~%JJ$rtN@NYH9eEs<71yS=O7D{QKg|kLdzrRlMDaMOx2nh7!>(17n+jT}t`kc9V zi}frZ-*&i-+9x3?{8imB}-hQDf;E;tR8X9et2nNnd$w?yRZF35m(} zC@De+7L`4^I;keN)!ypdS3oAeMMi#sRDo1#eEX>BsG12nkydh-_j;1d4j2rpnucbC zgwRkI35F>l!6wgeME#En^O4{9m>d;`bN5_s@N~h%_Nv`g*#t*Jyg4e%GfZP8J@j4Q0){MqSXa@p0GkwiYhWH)s^sI;KZ@h78Ke` zfyH86edNLZBI?T{-HHMCp>j+B2{1WmE&Y89C*K7KF2gz8*IhDyj#>Qgx=Tr0S5NwH z-KDzBT4QaG?vi{QPAALhcANgend4zG<$b1djlMPRjCH?SE zxUM|3v~V+buR}bV$`%F9=jpee08vsxGU&dmkL&kwU4VNL*{Lh%c=D|fAS$aUt*cYf zJIK_e$vkau$TD*fK(;%`P5gN0I(hyYc}(r@5Cc>|cyDY4;B0o{eVYFY)!cJI9_Igu z&R`fve7qW#2C#(wl0FFfV0VS&Dttg#;D3c}$nKsPE^(zGf~r6_qAm{(f~Z@U3!ib2 zOUw>Y`U`plwG}KfF6|@k?)e$nakeX>#?-}twJtAejD-@~@U(Tkpxhp^dDFTGX-N;Znm8HfPX%B!iC5$rRL&dbFsRz#AdJHhgD9v z@v92*Emp26xjB8WMY`ZXXnTk1K;iz1J>2gw*Pefoyp|!&F13`GsfhIZ?}_yM>8N!F zxFfDZ6>W7%%fr^L+3}|1VBvvsDQ36D0UGyQ2p?=C$$kArkC9CButwN*Mn>k5*EH21 zYTgyz{GKQ-lP@&wEUb;7E1m#miedm5tYJnax$ad{m<52fjtf| zT~nr^mE8ld2@W_mx!{Gv!1a~16NShPT#}f|fW{#%B?RculHx7UDuNcpL4=kN(gjep znsr8`gSDuE_r0IH12xC zmAhyYDT7*HkF=TY`R8>zzJIwomdEr7b4c`Q=SiI2S4AS|F!C(jMz8n2w&B|_5&<0? z#mP@QIrr%9(SYQhX>UK{1@`hZl0@FQBZ{rQ{#=8)_V(>s9{pgOCOh_UEL!#!dr}pT zGa#dULKmK*BsdZtmvY*I`BSIOKYNX=$7AR7*SC8bx%2&VP%lET@g-$RdT|O+s>5qD z8q;>B?(}PH-Mw#Ds}!OW4yURSLqVS%b(}p5BMJf^W+MQqvKOL@q6&B9`{_W9C@~|E ztEO|rDQW2`*?j79qt>`AG9xNIDwRrZ`sR5Li~#udACYl95)tq^3^qev7T2_K_ol}6 zsZsi<%pLUkXkSFdlT%f6wj`w>wZzPk;nA+`MUf?uei0kCZHm|^h4KaD$0CRz+bt9ZLT*XdN{n;aOE!w+oRzx`lwePMlm19`sAw>Y<;v{;4A|1U~%Oco*| z-^k<>D%Sp-QN@uH2t?%gV6%Kmh)kY=pL%|f&%sX&P!0w^9K&uISa(RK(GL;7O1y1+V&ot2&<_2$EwcT0N3d7Hq*F&H4SI1QWS1z&0=&prF=_Fd6?qV`D7tp=xI;;ZU#v3%}Hw36h^ z?R}M}_yf>Q5$`23HNqD1xz(iKhs)4H^11eSGjJ>18@k#Bt5i61bXIg)EY}iVxqhW8 zJY{8UG>3iOwlt2~1em2oi9^pNo((_3IcjWmwJMzASn9E;x47JroYE3idu;oLW1L+g zf9oWfn*(+?XnktxBc>yuUa^c0;?pBu-nLy$(R6c9{?(8>#jQK8jM}}SWzF7@1MAp|nb3H6p8|Kf2UJp_-Dkw z^nUo-U+JDnlDcO~O1lD-uPYdJVIj&?m%7sCx(hY_9TdsY{mLAHD+IHS#fb$E_Ymr6A6=HRA6qzDZfUJTj*pk@D7$h z)P`!hwex{oLgt#KS*G;lji%D6-2vSJK{6KZU8HdbxC02bk@En1!Gu71Q^yk1ILNJN zX87e!$kGC&yt+7O`=(YqfK<3OMd-m=NhA~L@cz&WaUn>2_78y5+M`n;bTEuQQ7B#% zR=b~6(q(M`9QgmJx{H=gIZE|Ny&Ge9x;(`D=~3N-mX>M6!vI+DOgC@5vdnIW<*h42wveq+9)&bonRy7rn^5h8L%v`Y@9B zOl0u?mC7F3E{|5w`WB}pI+BnZ@`5q69xYJjAZ8$)0(TvcT93>Z8x|Orj-!3a6aGH? z;qnu16y^}bXB1B&i0X5gC;&5+I|Jk|AiSOCUamy6Y&m1Njo>0)q&|ihkW%Tlhl-c2 zj9IRh&kxv^RNKhERrAJSmE2x^J?gXTDw6d+X(p@5bKE;`ebjVir?lnkn|r@g%Z&k; zU_~p)L#?f@R&}1;YRTi}&PlGMoVfVa>8n?%78OQTuHeenyXYe;F+=1k+x5gxcaB4C z(wZ_#_8lrXd`R{Cy6aTTZP=K;kv>R8N9aRpxn&aVH)zwk!6+@@)vaSU1uc?nerdP!rjde;9Q??q^o2Mluhw;l}!xu)amWI!Z zpF2Y};=s5)W4W3+JLk1%JLv>O5Z96kPn`~ZC-Op!bnA_;Hh!mm?|fy`JN%*gGfmY; zrKQbf@9$%g)BA&6S0`gBu#w0++;xZ%wF$&nW$o^e4E-P4!^p)FWYxXn8wjE}(4P*G zcwP~nec{FnV?D2Uo)!7~eAeZX0JD~>$z(y~JIWntOVgvd*SFEfS4>yWn6tBXHcz*I zPBTcxD`dM=_ip5c_f%JpkjF3Y<_hYL7d5Eu4y)PDS7d!ihm>uX7RJ};bZh7nGdHN> zDxwM!xDToCt&zlcvNXM-KB21h5_#e+b!}~ozLIZDB10xS5~R5pS&SF}-4*By;32)` zFCK~Jpj> z9NuWMRJwgdl6J0&`kWp5&-vWq+-0R9byADfY*Eosq#v{|hi>BxkrCMu>e#qkTO8kp zPV&$Q@{~y$Nc&MhNr$N;qjGFJ_~*fZov@e$tA$(SQ$a6GEU}hYO8AS1PoI6OT?(9m z`yr?^eoc1u1-#{*eq9UwMV-pL$PxLpj~au|^I%Xocp5?T=~0s3Z6)uxt;8v5B}YZb zW6c-esC@^nJQ*eKKgwV9nSa;QWHO)}dx*Z>{VLfbKZI<=zY`$5JRU@(NZLlu4dz-6 zC3RJmmheKR8mGfv-OHGxOPOPLs zm&x0zuXbNKdWy@e+VSZde@NS_$kRius`3k$U6<6CE@vcO;H~88pW5TNH=f)vJ~K{w zbkXjhaVoG!X3V4$c_Yvb-3jiYtk3b#mm~uh27VBezxZL(tXq?6~(0hH^F} zXW2}4%ndeBd&~}#&1lY+?g_<^4Qh|w=&(5RY;A2*9Ms~LJY?RWRm4PEOaXJV?eI2{gG zE`GvPC;d0C1I@2R&_atmLYG!a25FH0=??q~Nd?JD%`nDI0awNKyrv!0o@ej~;RQ)H zyt%v-8GkX8iv&zJAsKpiKPDH$liXG*a3aQ{SD-+0X zn54b{OgD$-kX-r&d7A!KA+=bn7FKFn8lReGNJ6OtC1DNQTg;sBX{fN?v%cB$sWddV zaYu_9Iq`}zCs0botkiNT%d26i4a7eH%kjl+Ac1$h-x1KLXV^NV%>k9eUmqF>(hvnx zoiNf6S`4k!A@Qd#2s$MhCB%x#?Ult9YIm);qB1oR{_ZGGtcXm<@V7IwHnX0i%Y@%V z@9Sn9oviMz6;GbAd>YcE%RIk{GNUqekt*8Z)myzNtL{>hfAl3Uu+SPv7z&m{4TP=G zL3JL5+M`>AIO1kNg2dBk%-3}KIXeCJSW=k#F6sZ|m!qz~PbA|%Zv##Kp@Zb-2&f;f zK^2Bd5%xn#h@D(paCR!vc%EOBw1ljr4y^FuY?P8(32`xxa)na6~2q< z9D{ckzl!*shI%KNbJF(+o#%+EjB7CX)o1N=R#YPS#`z*g$B9ykD>EzA4rfk|gRgg1 zRXOU9ka@mj&SF#_JNmIpGt@68b9~9XBlV7|Drdc)!+UAc{$#kby;(tD>j^{r zaqVVDJKuKrz~SbT#nnYMMK#je!sA5Rs78S|J_;X(=V;i>St_C9-*Je)f)E~=xU|jr z=36QtP?Z0qqdC-sszT_*5%c+ND?`_9UMCHU2pY43InD5xQIqc8=)=XIHpN`vH~#*| zR^p>Z#G!hB@j=@gQZil)m2q$#NC1Lrxa4C*jsQ#$QLab7#kI4SJmN(>4j7;0dzaGJ z=mg}eafW_VjuII!k2qABQ)#Q<*4FCI9#+*k>WZp4`Suq>o8k|?t!gTHySk1w&h&Zj zT)lGP{ChkuOCI~;#bK9-LUre(rW-qtQIW2QE7BF|N@AK9A6V74N;;+e+NeL&O>h!{ zW%`k|FWL{a`2b!|#Jhif^o zxH+~srYNRJswi(81B157>**V` z-|{Jx#qV~-$LH7*__ewPx>f4vXh%^j9~!VfdiO}}z67dHKLQH3jE&s5PaJY?u7xY8A4g2Ey=^q|m{ z+oU7r(}^KerJ|$1fiLyy8*e+xT3NG!+KVQ{s2G4ABP9VG&Wsjr%{yGuQYl4k%q69k z5_Nlf^}%Dj-6E3j+fNo+ekUq23--LCQv-7^ud4)+>KQN@^fHe{jCAmPk^B&Vd;kZ^ zXFyhQtH~t|N~HMKbJ{sxd5&8n8ORWI zBY6YlhZwAnox=-Vv@__U(t92TqhzSco}wg?C`m$5M^Yz4VeATU9m8cz@8f=Pb_*bj z-vP1+OUm0O-ZJO0GUX_f)f_ER=WU6e3IY7sbJ;sI9*YFkoZr(d-rCu7{#_hLOsAoy zFE_i0rj$HhT2WbE3j3P|lD;EKtPOX|b81@15ZsF+WLooQUu4w0-PqtdQk8!qwu(qy z@-Lol(f@}j{y&#^kbi|e$WBj%ve1bPVs@d)m7SU)mH&v%S=mtUHoMHl+1VKl$)O2} zxzc<~RC10g!vYDv4&Z4_}n!6me}HSdsd^V&{SlxW)`I;n+x?$ski2O zN0K?qk*wF-Oy${``DqrDF+C$U(~(-RJu%rS&B@C)+jvu&!I_oaQ)7b>_z`1qR7!MC zq%^L0OQoK38F!mqc_j{Wp}ojn>~NIkyqO!e#h73M{KA|jHQVhuc6FZ3Zc{nZt4xj} zXIe={Zi+M|w>UXool>^ln9CQ&Rb*BbNHa|_dNY@9j<3!uv}Bu1CUbgGq9dcoY>RAj zP9dzilg$TFurRRbG+d-Lf3L#kA7~7p62h$Bg_>K4h8m_3%4P zx$7G&mOQ7$nPr#8Cl~BWw;||-Xx6#g*FU*)Qkvt)x8|!W%mvBC8M*fCe3RXlUzF>F ze^H#9pPl70)wa)zd?0h528FpM> zm{p`tPIp?GGmNQH2gLC6)hQ`{U0V&7YFoLr%Ft6niLn|_ zTb`rRuj2@_buvO+lsu`#iB%pXtn~$S=q*thCunr1`bsrgBw5vCUG% z6(m;`Ik^JIk#tv1a$@piC$gEKiL+m+jpo{)uWF+1{{@E~2rTuWh%!-DHd z&CANmC^Y3|NS%qMq}nW}xw6obEX{)xnxo1|aU_-J0&fv-HgQ=Q$+;OulO;OVW=buM zwIeIO4Izs;eD(9 z#i0;iXpfM&eT5g5^obKsbuJ-KbdT>I?|UEV`3JJNmu2n=?g=7ye<4U&l~x)TN0aH0 z_%Mzxx+?a-}=DwmHLVrl?oQ0E3%PCPMaq`bEC5si>{F2UFK$ z`2F?Q1GkA~qg~8NMT!;q<$Er;${7Hg0Epe2awdxI4&`Aa|9pD?AcRE~2(+~VQI+KH z^J%Y`37lUs(=bW*r2BdjB|s5yK>GJm$J~h$AzetnFKWUNHb_}2KutSA9;2P4uZDJlKju*+X(T|_ z_>1~=#lgp?gD@AC87|8NZM@6_?u{-f8Y;~?rqaxQ^##-qFZ>6+b8n?;{p!4uEIkSx zBvQtHA>O^P-(lJRw#*9Au;qk&Sux%{QLtAdWF$^2Ve%tAXF`&^SA7l%CLWYG5T%8i z@WYmT6mj#GswTI_R>LKStjSzO)dO$Ds;S&Y>t6;Nc*V~=QHkIC{QE<{+oWA*x*t=L z*u~^$dYB7EW`(CK@p_c-p?@tvF!t`VJqr*(1pZ%SEO?gwKHVFUNdel?D`+M_f=zkd zM(TmPj2$?Zs@1F31-WkjjLSE&Hl zZyj0BWcVQgw!5gdx{3>HZrpHOJzFM!tk3ZcjbY7PbyaQQE_HorypyftR*!Zw}*Q<8B_ zDZ3}A<^KAKQz8~E;+fpEXwl-WlP9Vs?0W6Amh;we(Wwu&eXRcM!=^K*`EN#x7HY#M zy{eMe^qIJ8%Be*h&|>RF+EX3dK2f8mdJA2@Y#&xao)iPMAq(F6OVXE42) zRE{9fgo9ke!P2*nlSWzaeBFjM9GN?T29qafm>NXHl$_)o=;jQc`XqvrK_@jp1pQMM zz`|91?=V^b`9|rnx?4oTz;?+uz=C6~xOUG#vB%ooBBBpXI{7SlQf&l07pAy zZTnt*=6GS%Tf74+M!K>{|0%xm%s#aLl#DEcAuGeLYR%HZh3e;qZd){#r+ueQADS`P zFn-s>vx}um&wLztQ!Ss{=ldUbpSr=52j0K>qw6(C3P@^}_pA z7u1K_(xMyq3kx?6p?!j+WV+y1LewNTH^*l4%Xd2R^Ya@Td_P;6k|~NyONIK89$+8( zvXTZ4+tHAjpOv4P?`O(2=a_97`M!w9VHH|NJB8a6+^zF;h=fjbea~m)b34SDY+V3x}2Jp%gDBiFvQMZ97*WtL%Tgf&op1gI_ zCf+j~hi=-mb@F0WH`F6=gwTdi_RGMIoJ2I$(?&y;@}I8K6ZC|He(#>B^nMaD0XXS7 zib25`zz>R{LLm5nSU~e9ID7Xxl}wfbkUu#Y+4GZxO*4-Yc^B5WA~y19-#paTf@!LV z$nl6LlVQqlHr<%@E{9b9r=o)!7S%3P(+9?kp$}+lwFfuw!U)d@aHk^y(T_>#oKFH8mN@We9wFK84Oj{SvKe?5tU17cH(ou#xL7cUOp39NB*9 zii$i5)P#gQb>-5wl}9+?H_z|hQeEomGiQ2A{S~pw52ifRHdqZT+AH7{Z5i^$GuK|@ z-4)&CqS^1>*a$6!kw~FEL`L!~k*7d=vxdj}2^pqah{7ob2yk$rGy{YI8fT@ZyMrmN zQU&YN9<;RJr3px?T9Z;rc+x^!M8&D)>*7`S7$mF<(N>BzELpG>VMlMQ6%MqrSIDE8 zH1`U5+{1mu$cfdRunemgh}zW|ps`{_tRXVR4R8^)puST$T8$ z`04ScKPtiJ2W0<2A|KQ#pQ#rf8>hUw=ERIL?gt_feS>8mhyNjwp9(lBk=Fz?HRm>| zEs~H8VM{l!YFOyoW@|SsRIT5XxMkzIs`^N7!Dtb7U45uM_M-atuiu3>UaniBd`c{T zAYd+)OKhK#ZOvq;>ZeyukC+&=VR{&MW1gt7eAn*1>gMW%P<|YZ-A-q#5^Q*Je2d^3CNzyBE}~D4|cajd*j-A?cb!F^7+;&ea?})XKFUx={78`txhs=DfqV zY~CBxGNi=p`&CwvO=K&}1v2MN@B&=xV&NJC7G&Ji9XMe zm(3Mq)@HQoNx*vF*bgt8PpiLt&slPkKUsXN_So*Dd-mKgXNwRaBEhKNAue_m@#ugiCkZPb|V#;zZ zeM{no9qZHLVq&-Iwnm2~ZP82P=LKg3sprotZJNuks|nwuYu$P(>AmdhDWuugLJ~x! zmdZNSr+II=3b^v(hWvx-H`{EEgS<;(ZqF$ZS&}0xYtp0Zsl33fU1(XLPFk32 ze~!0p*qF0Losw#`r1Ca&jzvYLQfq}p>My$L-<1XiCuqiEd2XOAhKal_@JbRZNQgJn zgYoKDHc$noVWjeDgh7E|Tn`1c<30tocg5e1o)v%bh_f{$cLKHJcI`y6%V!J*GMI#r z#O-1$D6<5Ph$-R@@fUCGyAyu^*xA`NR~c}Z(F^Yeh{%Wm@`70YGdKzm@^!s~><@#B-^0>eNJ0flHm`__ibB{HK#b)g zt+wFRsVcHpGx^hkV|=^#Z@C%8-@Y9CH2p*GG|}!JMP31efZ@P$;W<1*>$O_c)w-wtZA#C(ml() z6o3Bp&(&nek7O>{frJCnpL88fK?Z&bT|A>|<(^G^Nn&o6F)lkLGc-HZ7zZM?QyTEr zGJx$E$`@RyQlSr6kc+T>WgN&-uhJN5eR2Gu<2$(3bXrEJRh2X^Y+l4FY3%zS=s!kO zn}q^DaX*8lFb4ptG!(BK96kp#;KLdcEY3Qeaku6+tMiwnlZ!rT{Q!0Lx%AcbtIbPh zPhT@oH;j83b;e3#gZ>5H$9624>q8!eV0a?@tBF)QqiWS|)Hx~FV2o#VHl-Tly>)&P zb%va-ifkn_LB8oGZ(@PgO{nd0&>Ett>7@y89gpPJ(AQX{$So?#VJJLdX;MB0~bq;IOJ z4U0ssN2|DiOA|m!^iNcF#LqK3AWFk^g`X*>Xq|%vmCe|oS#ThoiL`o$y0R_Zl z0qri}_QkbW`qd?Yco!TE2zdbyi203iDcpU=AW^P=9_#&uGO>dWp@S>|;w^(IuXr(c zOP~OtOqJdHli^+ZwhKUYD!Mu#hw0IJwCMK+7Pm%tfyt!;_Sd_g75fPt=(b?LY6a~D z4QwOOR`C(ERp`O7+^jcmtpGw9V5z_Xb+WEbHwdVDn9Pt?_jE#eU2(4y;5|&uJwp|e z{%n})PQzOqswrqQ*l3oDEy3P;vkjlZ#Ybdj*Qf}-&1Z23ys(u1*1@eZXyPs zQzo4~Zs0`P*DJP8`wsm0-Elk}M;@ZDBDwrB5pAju-LYULk`XuOwf(ejGn3GwMzGj~;E z%eMu2238FJh5jPSKx98vg)F-(gWJ6=rg4>ehYs?6{N~UVn-}#i$|%4c z0;l2Bz9aiu_=?Jc+6L9(?KRtWa~ZB8W3jrp$nJs@iTbfXSY%|<){R)x%S&JX)6?fK z7WZA;Ek@$@KBDWGGIJ1AmIQ5(MwsM@QC?cz@>1-}k%OO_J!t3PowGZ4{#JAS>gmrM zzX*@}x?1*Dw`2e)*^*JUB{NhioT0x$pH<;j;9xC95uinBmE=Rs{WUD_VvYSfSD*Jo^h> z)_v3%TO3#<5k%ms%5K^Q|&OxjhJF!6tXXJZl+9IyZ!>?R9DwnsvjN%!w9VJBNzeM zy+`9foyTh&x?R9FfyJTl`l^9QzhXH8QFR#r+Ds zS3mm1(Gk-%t+JDMBd52@*kTod1A=$VSi78ykBLEqaO&8(Pp4Cnl*WtGiD>T6Q*Xr8 z##G1GNY@_S@m{+M-1aqCm-KaH@Ih5sLm#Fq5&9W`C}|Opgjn`~Yc0VnTSBD%zzhOXQLgGj!3au<~t<30!81F)>Lczcust)^ptahI1P)sxO{9 zaIS$rcYMz!Bn&c3_{NIz-OZ}HjM}7fuB_ZuTc>JHXo@K3^6%cdd-Y@K)sI`g{SEyP zP5hk<6A2LPUZE=gu4+7b_(Mu zjzI?o4Qp6$c%c(t@4!N)x*TBU@DSWD&>g5u1ksxV5UEpK(G!&Dq&i6g6x7)|jS$`c zo&1iK#R2bAyYfw04xV(s=6piTX1^)ef&(7jgXnHV<3tRDP_F{GQ$nGX_ekBuz8!IS)^gU^Pp~ww*BL z5jI!BBpR*BGFmJ~t~F-u&K2q`+1UlxYHOT@mAq#N_7;Xn^p!P+TF3-=@nVWmuY_&^cyLm?hAkz}3A_aL_-NCxL3E> z@)d2cqS!dC@FrQhI|l@l6ivIhi=mLw;>e`H6zbFEl7Oe#1}bSVzO^%UYW3eBZ0@sw zu>D`yw7-C9+`oZo{|hYbZ;lT@X-qtp-BnK%bWASS9ZIU zup-S~IoNi%pK$*FrJ-9O7p@;8>(*h7TZ}RDHBIf3f8q&ZX%=W*!?+WjWTP13jO4N= zV%L@}SlpcZ&u`rd$;&6Ed>qMjS7AjYca`MhohLf3tC%t~Xvi)xStR4T+nDGrQ>g{F z1#{L%8bq;PVlM69mp8cQ0@M%W4KHzJD0(2(DZ90!P_t0%?{ohn3vBit%^vfYyf7qu zU~xdAyD!J?YM&!RNKmURPcBX5g2jo+SQt8((cR0rb}SQ(u8vYVUf2Bp*y;bHjIo;O zOsx&;Qjyi5jT#w`6xKS>t&IB2%yl=+bu-L$Z_U}@Z)SayQP_TBji8W|MgLj%u^PE_ z>I5`jcN@xNrgu1knA*uQxk1!K7_k@ZR#0@j>H&9vjRRVii4Guw$wUW+!Aa?m$z@uv z0zrpFo;^))HQ{zZ*+49h+=EcF7E^8;ylKXE?Wr6*WUt%K>h}$*)#}xsU}FeID7m{D zeteLo*N@L}*s-cS^W%NxcTd{$3c)&&VrgG6lNBBp%qE39@DfC%WK`!J>k!buRM)0N zF-#m3&m8T5gTH0D*TKJg((BmeB!7>7n z$AIyK%ArF(DuZVRkIc#twWulv5&@@|-_`%S2H1*9U=yr69m~yP%9UW_J;i`GbyGaC~d(;h9^TFqXQ)@jnocO^>r&q`Vn_fX1_0n`m1*M?0IS zu3Z!iDJ4t+SA~DbhJl_h4i0Ze7C?R-AE}n;M8m}4;UcPS3MYz83Dri!vV)XPv?!A* z!oyL~rf`wG`HmQ8(}^H59f;#W=NI2WdDEGKRHq2vb?v0HNd$!pYm?PWlE*{z9dg3B zgFVdgZuFPUgM$Bh?WAi0QhOBjcSz`va}+1o1`68(2DM9#o<&T^61!GdoUKI zVB_K>#9Oy;g?~T<9sV=csL+zPHT}Kp2(1!AbR8ZSc8tV$vjc-Xth|mL%xgpxCorIg zL;=yd4%)#)>+t4Pt?K|`Zwq@6@zp64+5$A)X;_!J@1d^c{oKfUE5DF=G=le4Aj7O2 z4y$Oue{F+R!wxFOLBee`zMbu5hiKoQ=X<0#oTFPa;+t~U# zS=_N@ySz215k6xz=tK?J$xnH|y4!Gam=9z_4{9JuBeazuhnc^HDLWZgh;hr2tKus*svFgAdV_^LL1oe9v4<)!|`}_yfvd*_qPn~&EdoVR+inw z9>2)$xx8yJAt3UR=1p{abk&y_KZfbdGT}Se@*Pch3I#QU z+l+}A&#!A4+RBKr=vLh0?Qkm(!p38vG`0!9%5{B&TJn^VLD#3vUoe%;SJ%#-d!G}G zbe(bv8qcl8o4-%1$EdtE|Ln9anrUa}UxWO`y`^38%5Pr#V05Hx^arnf!y%cz9_bw? z_QPSQfRfw*=5u!+a!)4gL}BESA-~W^AZvwH<{@i^pn#q{@(V<;dL>R2z%TX+llhCE z^-7Zofl7ik(qNJ)4r?bGxl~xxv71l}-%6cD5Km=eEp^6{im*_B{!gvnE+Cpvx!bxNe z>{Tpc0d{-=Ei64bt;poUAGe*#d_?nT!3!YOC9H@^T z!hcU69&(kwpbia6oHR+bz%{=@%MGJG>w(xEqN4o@=|jhda0uLL1f`CYt05!tX9Glv zefeX*79!Z%57&Z0uM5mSB;UOK1d(5i3(U;okbPr9Wqg;GtY&@XHu?$cecJy+U<4(3 z3vu<7HeCZPK#*j`e+a)SlQU8?^c-a9{uHeZoffuO4egPbt6l|+xbz|8)zEBw8Ud9t$9PYM z5cHyKn+E+NROT&^oL7=D%Rr3jL&pOq4LC<1I%XNK53StNqHoskt1N7h-fjNr0|ut| z`RTQQX1*|VUwlhpb7AFPeTx(Ye*K~hHN2+z1U8MJ-7JHrn+`J*LgVOuFM6FJZ7^xW zD5gc=7p~Yz^vOdQBDF}dASa*|%j4lb;DaPk2AHp61uR}TbqH4cHZ9y zGjAaFkw4j|Pj~0v_H%dMLR0*EzkeS?9?{67CiQv!Z^f`pBkj$St(@22Vv;fqjyxpSR25^PuzM2`o8C-Mqr~?`-IdH1t^iw zGF0S4P6XHZ1;Z+^nFg|QY09wK^x=85pL#=RK2{alULraf@bqyyLM{IitnOEr%)uJ; z!X0R>z&5-{lwiIP>C(k_`ItA4rk^Cg$UGhi@>%ZPO8M$o+?CXo4eJiXuqBM9%H&_N z6^w{VM$XFQt4X3p{$)JYuZmG&Z6bLpRt%7myic8 zkfHC8#~o6N;Jmm&~1*wNS@4-q~@jCQytQ?&~$( zu05n>#}1^kJYouvk4-s0^a`6 z96KfwzUexlw3nw>B-&?}`zF~F(v69p2mQPL@Wrw$3FXFj6Mf5!6$SQk;X!}VL%#08 z-TYy1iXO%Vn^^osGclO~tg>9`c~W?ij7Hf{3QviyUV`V;1n^-3*#sir^BnlakPYad zyDFum^pcF^K~gr6a7%9t|AqRr&>0c5!IJDsDK$!=)@`+^iwYfucHUWx@clbv1CU{C zIn-L=W99OdMX#R+Uhx`vb>1FP*AfYo$3NOV_i{QBmWarbBIR3ero1uNg#}i9y(_Hl zOi3(BP+KJl2`Q1OJdN?J@K~nI%}81MW{98Ahu$6IF^Sd~%69Bg7nbDZm-50QqW7-G znpq0eyLwMq!&?S^j9?;vlDpo8N$#UP6a0PZl*RSN-Eo!DVsAz^J>3jM7yOHE#g5dJ zZO#b42xooVZl=xEA>LLMwadV<_^Mr9S5sV5h^0!+8c3c)J&aj5!YPb#Fi&rbJhvs? zibLMd65&*L-~tRo?%QHwC6=OMYgJmYUusdDH8l;gm{#BJ+fa+s$`E7HNhZQj?(QTo zsyZ=n?Z&tNN7#FSH*sxU!#1|0xeg%-@(^3HM)ZUddJQEeK!DJ}1TdJ6ZQOA0MY83h z<|?^Y+%edI4Vd10CqPJmgc2YLNeBt#jC5q)e~q1c-}`+3^L(F+Mw*#(&dg}$oU`{{ zdo4^D#t9J_>ihx^`irI)J@qfp6YF7Ey@1D7`U2(#TZ*sBu@oIQdeqM0R7!-=^!Pr$ zrxWloh&A*;rrnF}PBZq*KkcW~(#?I=(glk=p~sSe+765LFmm8taP6$z%HDA6(+yum1x| zJb9w=>$@^rhsBqbcDGBaNGy*nrH{!Imo6ma)an0$L3%6;oIX`HwQ>3hz#xC5KbFRp zCsrg0HJ1?$@)+v?!>l&f%4@4T!JM^Nl~N|MygMF;Z)<}o{hxE#B zpbfV;3$r$iuL!bE_7%aCS3W$93-}pri znC75zY!Fl~dpRi^VHGzUwl??*3YxxKgM1Cj`VN!G*U%UQ3iV%|8XKCi#$plyUowdg zBt3n=`tkyaByOUmc+e0Zm!6i^JXADgS9CU<(@AQMRY65i}8Fi087pn&=$&yPUEx zc-Rh;7*uiK3xitqM9UoZK%`g0N;%eg`^Iez!;tyb&3rP2}h+KgTIjb22@ptD}%PD z?%ykWkpH0YK4&!Np3Tf+j1uXtRD?gpAygutF|Gaq0GPx9WGOOYKlbc^K7%0~hdO@s z_(J9z5fB#61qG~4T`!+FF~9IrrP{a%#J-F)7)F#%h<9*>+Omvt{JSRJf1r9G-@8Aj zVY{+=Th;dF>w`}csf4CY`Y$EVt@A0pGw$@0)O2u#Cs49hT-5K%*j?ck)^=1JO3(P8*=d8T+U(WNl4LSI-&a!Ibsjdk~e9wsy2W0KZc zc$L$%ndMCjIPj+>?cAl=Ek~0GSx86+=@8l8CoV`WUPGOJq?}xEUn2N!u?KB3SR{nW zkB7bW7W}N%TW~x8_u))G>^+{FG;iYS6~T-k!0pk2nmh#F$xcsKhe=|a$UmaxH7X7c z4Xp_P)x7TgYx4O=q@14!Ger=3)uBsw>W2ueV8_FK*ORopfL9CMuyhx1LVP^P$?Dw1 zg19jyN8nyFYUEn2UYDV?c?=OHWT+CMp_zXO|i3Zw@LB<)lARuP;BMU!|$z z{0ld4k7LqIW~~{#6T*06G=KwsEAf@%8x+%C8$ZDp-cQ!ih7JO*A%w`gVF(`B$h`uS zN_>7|Q3fyrLqz`}U(L=z1UoM$%VZYp#&E#c?Sa);2Y6{E@CK!wUURlAt|$f(;iZ$P zk!EsB7B8B!aE9%@C>OO(jfe>iw>i6Ll8kX?)up*EU0OXD%?+7K((q6KYL24~8LG^r zyku9nrHELO0~{{&YMe>9DJRElFuPXp@7+9i_t{^~5EJxK8?w`E4?N?-cO+ZlKm8pU`{cIubI(!s`@qOJh=Gsj@6G z+dsvZe$jEug*+A`#6H22)hW%8i7-+o_&fWMJ}mKevU&2JE||seol76Zs{t-#rV~9! z&$&RS@f_Z}@>P7F&TK^TPg%?QuCk!4M@e#yoO8jR=Y+Y?t5?JaGa^r$XJ<+Kb`*r9 zLuWx?yo{&`jS73C2o~N>t^;0mPNLBMe-|ZHXyd=iLg_{Q-^cq3ZTq0@&f`SeX!X?q zp-ob?LO9s};Z;urJu@;L7A*1`-&#LoJI0BNq1j+@5wEnhQTnk+moA}iUq+DaA~IcE zh}7a0Uy+r^t4OrS#*0_;m~Am)H=0Hc!sF^@-N4_Zw03>TEIbvVn zCjQBR)PpHv5j_GbmUi)Gx>V#wXNed8^LZA1Zi}U3ZJ&~{4df#cJtCe#dCLM?VQGia zU+yLvi~2Atg0(7`jvwUMXu|SBK)r|H$w!RDiG1gT{3MI>X2HlyLeKJ#6w`kUUq~Ba<$5QwOz55w zC;uPbgojIrDZyj8R&dOD{O_WNo7D`eRo+=pz7;k@?*5+_P}W<+$X+3&Ei4`2frAzP z*C(tYIXyX*TyrWc)hXk_@-vZ4r0a{BSVJPYs>m^AnRMi0Ec9)4rSu}hgCEa;FscRx zii86EXi%L$vyB!CB%nZUZl+nsm&WoFZ4*mvAQ9bbUD_MW3^?2WC5ibzGgEozj!P_V zSOj|2stgtKC^ECv%BX@Q^pzH8$+m*ZiUO`8zXpoNh??JWsZbRlRUkYmGD-#EC%V>6 zY^Hn3-kv7}{iJ_BNVBab>vh(4-FBT^r`LJ>ifq*#aG7$*(nW5sVAs6m-&R-e)mMkP z3OT-=4_9?Ld-$;af#(sJHy^mTyVD+e_dD))^rXj~J5baU2*Xz%nW*<%=_>Vot9;9? zT&bUU#M2dQ7CrCWAwBeW++FXu>uC>ncK{E2x*Ya=pg(fhs49#-WQE@YJg>;2 z7Cao6;rbN+<7P)xFT4|uDhx2r4>350L$>V}!fUt4O(&Z(o2am0ve?O|)a8eUrWy35 zU<>@?QFX9pS|_skRq1tc<#6{qyM#5Y)Q1JpTj;{$qBDZc5y;g>zG{48g+`vOtQ&qGrAMArk!a)lzTg+)LDw2{?RB6gIl_4Q7 zSzs%6>C&7hw@{~tI5Z+YLWNAU%;1t}fwI`8i)&CID|RU<&#F^xW2#gU#i4MTS^g52 z3F^|qbqPXjF37<$t*Z;9R$>)8-haA4AL`@6`|v*h)di|a70AJy5#%|AJFC=Q|L=DW z{KvdIyL`Dw(EO4d0}P{>-@|J160}hJ+E4dG?Ms`09Lqsc_}ll@TpG8U!eg7&iG z3zoJa{>Hb#2EmOax^$^?#q;O8c3sf#@^%%}!*+S==X>LAJ82gVfHYfUJ7IU7OMJ0# z_k_fSheHSp!dij|T~1+=5|b#~cH8#<8Vj}q4u8NYx-6~UT8ZgCcOS=?YuDG-WVZy~3k zQe7Tf00u`WsuzVABUP>us>BGWWjjm43L~miT&1ekSYCt?=$1=qfw{aA)HAklI4<9M z3{_Y?R^h)B-W`UJmmWZzTr%@DMpzArwEvxCIaoK57*?B?mY0&9f+X&g3`RF2Y>XWI z4gG&3BcLGkp}4p(zc^D_O&pCTtvNN%H8&NB-g4Vov38GcXJ!+_$BRq;*+pzLWtdZQ zUGq|tv#^V=m<+l~`aC0(Z(fTv$V<~o%~_@U$Y>X1p3amGx+zUgijgs-kFDw_N79jr zE}%O`DF;DmL)>3+Rjl>ZZ#MWdbA%yh$2LkLjmK_h;B_D$E>+Mo z#9#dCn`=b$$D>&~1DBHq^+w3e3NWlciPXhhsDtc0lbs3%3gC?7G#By{6KS-Ph7FaV z!Vmi^ez8dh3&%OQzrwl*ZZ4o=l}^`4?(byPYv^}cy~$rJNu`_a(|I>J+V>>waqx}o z*^`R^M-3+L_C}+5sknAVvmq}h+jO4{bjdByf`~mm3l8#bbnP~V%)o)l0Vzm8Qs!(4 z-MkS{>Y;R=jAoJWk!1D^5CknFPOFE=sHo5KLC|{WO=Jcw2aV6nWF3Cf(=`1-=98Rc zh&3l=ry?b-H%atk=yVAf^h;5Cyn;-Z5Z`84xMRsWS&xnmOlT(nU)Y~~3LsxE2Wv0u zQC!B)#Hy2#hy2?Zk}zKJYAO12d}FR%Ul17p7MrJ=-FGW(BR_T;&|krSCZ_g5wA&&I zO=w5q5=kZhfS?vrFY+;+NygG;OiGR^-7F`|#fAB~aH!?vYl~7$@W{;vjgki)1UcfU zI>ZP**iJkcnEJTD@c=WvC6gYK$@a*AM0W1WUZuqb1^J%r!`J#JF4n$>WZ!tjUy@Rx zL#F;>a)tjU+pI^{wW~Q*ouiV|rD6b+lYlu~YMT(fHe!A3I@h?}ajjtosXsr(B|lY_ znmt=Ry@`7)%gw>yhz7FuNQKg~Pz^HB36!%`waB%*JBd$n(?_6TWOZOd?%M zwUUh+bh-^nq8C2TrP&glpPxPeZd>YW5J~6L2@)bQ!bFx`tnl#%|6nVUPxQJR5RU89 zhAll(=#1B0k?1|Q5KL9C`? z3`fpM9+R3nItTeFCfpB#`kNIV+yHTMQF4LWEWkKj)aE2pf{6ibnt|opI{sn3MU>t{ zVQsSs9}%_e(K&c_-d18e=ZBDJx3;rF@vhRYwg5gr(p4#A3#Jp`q(!O!Uvvad z#&UBQAbw^;SsiYpvKOM{`2WpXZ?dwmS==mx|rV* zMM9h)FYbrFv#XZm>*b0-%lbQ@p2iN=zQUd%X!8f`<3`n8J8h!LcbppCM78AtK4Ck8 z=nev7norPHU!Se@EzR`}Eg)sWv{iGj98^w7|W^;ZO zQ+KT4%mdk7J*e)&p%cojTc0#vwJ2$^YT>3$0Rdaq`FO2eJcPdEox%8JY~AW7>tH3m zjazr>xMtnC$cqt-H^RH})uf-iRQwI*Bl;})6T_9-eMfhZ&mM#-Vs`zb0_xv=Js_*=hTiiFzE^U z82M-7STXHK<*U7^opN5p!bo2ovqcxU)mJzXzxu79aNL#gg1)nVaf{c^b=w2>Y|39) zusDBF!Tf#ence83abfO02s{&VOsT3;n^T$?(kTAx@sqy{%Hxq|w(N#$(U~}q-scH( z^5MCoH;D69KJ^#441&m*+fT2oc~)>W=~DL9w37u_RA;lUT)Fyy1W8+N?XnIb39O$w zE?T9^&Q~F{i`zawJ6~RIj`dU0k-*sX%|>!p4|b};F*YKtVeYFolKd0kmieV#JA*jTdztW>4! zEOCe~K3x`@u1=1VhpS3=DlZe)ZzOv(^$F!%O-yj1pL|PjVraB7Av$&ICK+WVn{tDS zVz|)qy2NJr&icZ-GG!ikj*P{OA=gk;C9^HJ+-7&G$|57wFR#oPg?&SDJ z+X+P0Z?7At9}zX4OI*Ba-4YEGPZbo&1PY8ISQb--a!Ky0eTiq7s2}vt9ztC6k>OeS z_gvxGL;KF;FvU=sLjsHfG=*5k6F24Q)I;lv7BS@$^drV%?~ZhflBHhLh?hju5`Qf0 zM*M-;1Mvr#Z^g&y@}o#7ydx&7Z11w0G=T{?i|CL{O^h<3T+;x*aW9Z%Hx%LA z%W4aE%6HTzhL$UfqH}|A?!6??BJIw$N&QYWC{6+e9U@j{WOuB zk190USMDEBwkuG%YLsQjj}obPupJGQv@~ol+aYhRiT2J{=0+L)ykv-klV@f&NFSw5 z=Cn~MF{(JmH_ST*YGS^nJ42Mw)#^RR0VJ0kH|;L3;da(GmmZL}H^*+NRhEUCHh(4S z4~A-qS8@3Es=|WmY|fBvsA!QrOBCB)TL-XSiD7|33DpNU;w?E)w5_4BFx-oy-V)2k zjue(K@REcOM=s{OFV9RhF%_8lFVNHZkT%3J3L>jhlIJdtp3H<&M;$!b4DK2#(bM;8 z!8chp`SRksDNH0D(FJ-kUyfAB1^P+|(cR6vbf)|}riM5gFw{w8Z)4pYZR{*sGJ}+e z`iLv%SIw)M-!!aZrU}xf)h|i4guKi56Ol^#h&`UXCmQD%>Rak1U*j9QB~%$5n!M>N z87A^ynKqS&a9e7cW838inoD=qD9dY1t++Bz$WwNN?E`U8RCEGl>NI&pTA>FhsFd*z zBW#?+Co?QNo(nZqCN;=+?5x<^q6BPJWLNnNkuN~|-NccCckXA4h1Kf}$bH+*RVKw$ z`^aeu^j6X^Io7BR3Au@w$~U>_AQhmK(;SSdOLkjOEosq9}%9YwB^6;9~-Ebp$782!=8)GFAr-GiWcQ(n{$;pW_^*S zkp9S17oFZ#8L5EV6lAQ+^ zPoB=4W5!eSy9*9e&%yN-kY?89XTz?|Hf0sa$vkm=QA`|A9zAJ@UWdbU}g9=81z6%1e-kR?LS(EJ3C(+{X8{e8rWS3rg$c zWT7}eFFggMxl#1v-ik`Io8zyLR9nRlWqG}XkH*!CrkNr#-|{DPFl_JA%ox4WH+`yp z)^tYiu`G_h&qdP#20B15qizztjt(fN1Gp0U-boL=?AnZ{##RmP(|!rOx4_R2;lRvt zy|Ov$uKwChMt|~T3AnDy$p9Ted4lo=G9a1^;Nr;p9w+p&Szk}p`(`nEnptLhSMWXJ z`*yOw)QVvLKntk+pV4YQk$z2nA-hGqie|F(qapMK*@a1%PNy@7v=aIY-9g+%Po}3?TQUsq7j!qDK)x2)5-gzX z6+U4Tx}a^M9+$~zd(7-cBee6cAuJDcAQF_U8!*g|5qwHB_)6ANO(*OiBRZ;~jCO+r zvX(9M*;O*2V+(mM0@b58%Uf;cSL8jLl{bq3Tgw9kc?ciUfylrMc>0%h++;0C59?^_ z6s*b=NFg&7(wFXn`(N#`(5P2vt;ZiWwb9tQs7XXKYw`21U3CQnhrJ4kIN^T zN0{cG+jHth{sl8xxPy4;$il!Ysypiai<#4JD_FzM=F_W-;I~?78>^>B$;y~ym(;kD zK_!D~hPa*{M0)uB6-`$9lE8d2>-WD-#}SwM-xxB-x{S?k&f62V{j00vo2G1|TQAYL zJQ^9%N8LO2BX9Su12-j&tf3oQ>H22yQY_NXJidV;qA{eeHxWV^5hSRDEd2Rc-G!F? zOS?(X9ul+@!T`ejat=v*M#T5X_b;b_JJq2Z!Z1w&z#){54yL&OMy7bJ z4cQz;<+JEW75%v6qx}ALpI+G9s6UdjHM>Q7WMU)SC(yqinLm5@oP zWR%zG*mL2#SCvMj1*L~Er1YhL^SAs#vhA-~7dcpGkd16W{G!CQI)=(JLVmp=8q~ z*daO^e1{F+(s$D*T81{I^#u<=KN&v`N(U1q=h?iX>xVo|+IuBoM?#G9mGGGUa9E;4uH>o%75_!~|U-Aqd0&-}PDR+3W&s zVTzd&1TO@6xMZPJGRPNGIr^u~IYq4%q9#e%`Ii+xhWB!!y*q^`cq_XP7q5M{P+fjAIS!Lw81FD_!hmRn#@kn{* zaqAB?-!ZoCZjNR)R|gS0U5++aYobi>c+Zv7S56NZtNr+3*3O)5xh(}P)h#W1_ijH> zafB&9Y(CHilQ&gRpR`Qn>sWoqRND!OW$Gs)H&Li#2bQ)AmZ=h}-+1<|vSX0gs-z!? zS{06Og=NP`t5TrhvO1ATc>dR;uUrr7W&>Q3>m7KtbvGLsTUJ?FT2@(A8WR~A8xx`A zKkXIKwXUkNYh9$W<2aqiF7fhOsA!7R)N1E}uRtK6rt0I&n$QO*U#WTs7%h@b})NAG**!(}x0pKU!uTDJG+bqWa!n zb9{&`o;~f=zGSJ_nk8J5HP-)?T(vitI*x??*_n$NUUp%)#WTueTwl$L*a;aAHLtA+J9YQxP2 zCSOx#tWfGDj}usPmbxM+5h?s-*@kFyCPV+Sea7a2Coe5FH31W112!cX%gnijrXp>b zDTA@Rpp@OP1EX%nBqkzG8<(h*er#tqV&$R()G2K)Bkg5(-Y$JL;(R>F(-|v{Q%nup=QSzxj4|RepVe)+{vW z=$_m@Y~c8e&AJ3re9_u{hkdRTG-R8zw-+`QG?zDHpA5!+M@^2lT%8RSXuU=iA2K68 zLKBo6kh0!5*I3->RhyWbRZ&`IHr3=5Rx-xSlF~v`R;K>jO<=|CX4m`uEe3UnA%qDr z7DXUe+7KJ1&WKNox|rE$Y$`d`s%z2JuF*|l63>)ZL~=z5^C64I<+o^>lZwWtr4%iW z&;%#PnoDZUwdyM#=}R;6J}%Z4Yj+3Nr7@3V=dR3Oz)0V>%eE_=)n3*{zsytZRPUg@ z8|VichTq65F;r)pTWX(gBn}(zgzt}NNHQM?K0BspE>kwHz$bVlQ=-`eiH{D(a*fRZ zD2kK1J7(A=>p(cHG#S%!(%}_O)oRNM1UBB7^iYN$Pgk;;(4$H+MrEx&RJo0jGWK?M z_?nn*c6PbBSyAOlCF-KwtZ0UQLAJ0N>U5(_Tbxpa7#XTErsovGZmmqxg)t}K6-rZu zL)j%-lNytptIjJnW#wb9OtZSO0yNionv^`HNmB?l7>2*#hUac;*{t$Z(kmo9lfL_P z*uCH*Yv`aAIDH(!pe?cLDPK;WL!D|XartiLoQ=7d+?d{)Q9&nP1N4OBsxG zk)xg6%k+vrnzAc1tIo&$7V~;OnK=0eMyj&2bDVQy!}*ZM5x0|WW?j#D;z{0{a>lb| zYQ+~iW|Mbn{8lAp=EaRP_BRg6q}}rSC9aw^V%^fkOM?=bfS7;`-Os<$w`g#7w{Loyr5QVI3*==YtHYJv-YE`uv6{dV9 z$5fQLP1}&soKs$~y}Wo&!XajLT-H<3WCVJh4muqA*j!mrU-!+W(+#-iRd(*T zc9AI;>3iRF&bb`B(Ouzr)rMvo8#5eA(8iHenaQ)*5c z2M}o;4@o+xlYtLg{+w!d)79q144u#a#inFH6$f%}^l#uUXVI@YjE4OPBLo4!P5Lnu zvJAOgKDnFn2YIF}_b&4;@n(7xfPU{!px0zEnRP z5xWf_bR4fPWD1TP%RMfaA{I!7&L4mT0}^J7VN(n=>@bZCVx%k5^3w~_@)Mfko8q^V zf;X?pP^0lVbv#M?8R>9_IBGD9pG!2>DMDx#jCodfa@n$*90N?w(aZ<3bS+)+30(xP zr$sNxdndOaxxxKyro-Sid2)Ks(MulYQB_JhutkIb2z5M%OM;X2x;x{qMzrsYMuRocxkbW*B|3d@WCxQ1@Ugpe)a*iIA@vflZ zx@L1-u_9HyiaYY1-gEijzn2k&ijtG1v^;`Fl@_Kk1 z>goc65Z4OYN(W}dF>x8uTm9tvU_JF+o0RGs$mxT;X)(RVft%fsDYHHTSf!!KGObQ1 zSsm)HQIaL~fcn(?-lo0e9k9wUW2HTOhA&2@?P51;yKGK#SVam~k#a(_V>kL6J~lT` zFUvO@borHJoF0^x;<5(^3zX(I;=o_oMP@U4M{hctI@qqLH+0_4ZPr`lnF3G|XZ(+G zo?rp64OjwOIIsk!RSG_Qi4!2bLKNelwH72p32WhUCu1z8KM`I7cEx0`*D3_yNH|-b zTCOhU5X^8Eo!vP9&@{QtSv+n2szn=-geEA8$EQLrcDYkiV@X|^Fm?D@)J|Q*RBsy& z+*F1tsZ(v7)`;gHU3ng{3NfjI9bN+f-|WT_i?;)1JBEK3S+kek0s^eyH(j!A!qVFR5`B&J zw9WDwmB3alB8e=0#RmrO@+a^7an<$lsR!%!tz=?K>LQNGkJVR|l_>Wed9d%%(pR(n z={v#R3_o%evhwvlIZ7YPS2&g+(gIWTA(+fcb|_}EFo-v6Tkmi3hO!2 zKpR=0&Jaqavx&h4aa}`>$zaYfyJna{;+{#{U$~I75_1};-8r!C8`bHw{Sy~q=cJOY z`lL8le6a@F{X${fk(dApSLsiU{&p(TuET_k528tag z!!8P$`hO`QCDfp*QCEkTY}GNgQStO!`qVaBM!r^%qsVZWj%2M5;N`-N;nC^j0?Njt zGlXP9szO6EP?)A-Auke{44@7j3n0yKkfe@qy5uHO39IZfofbK5aY8CEZ~7KF<^ufK z9rnvQ{uam%!oftQe|ZJYX#9>+xT+Nh#7=YRcqpb=qgJ^7p&-JFIr@*NGprhRz>mGzrS)dr&*TG`SIBM*2UMKQ1(`|v@!cQ}4k0r#s4CK`Z%E1Q=_c7) zEWPd~Nw6ANeM0LPQ5 zlcC$VfZXuxPYwMIV|1P%!VL8()|O}NOWqd1=xa7)jpXvFaYcY$wkdK}^G9R@qhI`L z4czD{m2vr~J*FrmivxRDomR9yK3cDjk1O(1f(}Wb3(dxM5=Ik9P6>iD5=k?pcCf0X zOt*v6l3`zO)5~sDJ*A($n8WCAtvs0z9nUNgksIa`N4+e~ezU)@50c^1g}26QsAO(P9N(Ub4}D_N0$n=IkIiPIaxNy$UYc#_Qq zdCiaVs$5fglT4Tj1`yJ?>mI(p`O`u=<>JqLb?eqNaO0Uf-Ge17{Jaf3E2_y@}Aa->Gh zp+^E4X|_8(5`@T(ESfCGA0C}KaDZZ`SVn_;*?|0D_2-$bfo?^w}wcFtr#iqeuAn>1>|i zU3o-YP2ThU zVb~ADtEkk6I$*QPr($zUQcKeAih>qU#43)E5djc$b0WQjvB*vI=Z}a*2X0{j5ptyc z$dpyYb2T_S`r#~QQb%SXNb^3}LR{r=^nS4O9I;p0Qrtu)mcCs88P#jH_hoePHIPY& zsEi|(NZwhD@%k5;wHK{saq#?NHwx1^Y!qEGa)rYAMOl)Pm0ynbLYpTN;an0!p6-|A(?X8nC_ z4m|R4{A}AQGLl0Y!eicrR_SFKsr19t1-SJAr{!1KX3^NXfhL z-JSS*!i&<8IF5cs?YNG|Vrn;f1a(x-Mm?Yd9E&hJ3wfc};HUz`@*j#SBOrj#eZlrl+U?a|B*G zHc1^7C5tpimnI?g11nPU3)2hbLdQ(UECd-t7q}dAiZ(DZfZdE26677MdE^yK&1E37 z3#P!5Eme>&05T=xzgEVQ4@ER;0^o81G)+ctkOHuT-2h!@C>c+Z?{fT-zgX(|F^%R| zi7M6MMPYK=DsdcOO-OTdwoMXylf9zn>U-Zl>&$YQF?Y=u(HzXP2!r}XM}>=jR()ub z9Eci{Vha&PnztoXV|47~q6gfxGkv4Y>OtBt0M51kOfuk{>Td1Drc=AmApJLxE@D7# zJA^t9>L>ql**Wsg8f75q7D(*z%8+;be9mo_rv$}pS*cup_2i-Bhff@I{rb|Wrk1S7 zdB+!3(4JLPQ9M2m>GY!7+NF*1ZOtvW4=NAbsyUUpo4J%5+O$+29IQ#&sysnv{q>j( zOC#d+6Q67700uWts307!ClPdAqyT{m2aY9N8Z6xfpf->xbc}d_0$@i^T++-~CHjhg zIsJrxG6(3oF+ikclI~8#|B7fBmf)wvI~yS$3Nh~jHr4CA3ou8W0C0f7oo!vZQ z$$Z>D^z~NZ26`<{>D2q~gtGl#0O6Q#-?~=BdO`;5`L#tpW!$B?-~xL6b9L)=rS&fi1NR$6Z9#QwJ!PK3Yc~XO zpEin`sw#KvlI@Dz;a|l`3*Y`uE7=Xx28R!j2Z?{OZ4&Lch^hI-%S}y9%BCjVgJWL2 zVDw0>a^^_NUJ|%l4}xPJNB-*9@C~<>R=rqH19#Juy&S?*FZ9YGFEDnE@o!?9{6Xt2 z*MF%G;D({v9=%C3m|SoJy|ftE__&O;cqN^%v@fpq$P=Pd<%f=4klmYoW=ed5HXZ%Z zIFGN$Skc+2rLFVilfRrZIW99UJ6?GL;P{Jumm%14F3MxiJo%)#|K4&O*6PTwM2n&} zE}bu%bYa20l9J5q5{`^G@tR(tBmTYR)AI}OmzHJ;TRu5{l8zTGtT?&pqWs>atKXJn zl%y3aJ;(%d@y$s(5nE1S%XgQqd{?3swk$;krTbaYxyl{wmt+s-otwyYG}B_XFS$Z4 z{{0%H6g~LxOL$I90y^Iz%&F;ZTUV}c$1Skn3vja8l5MeN5!>Q_n)}<5pXM@t2haGN zm6LCs&Yo%6aZvfwrC-nde4)Cyvb?;KAqvNpixzGQ;YKYQwPe&{CUo;WFE6>*yaP3x zm7~v$I63+(v%Y@m*%LBvOpI=cPqnUDCJ>mK+K4YwUtZ#QZR0ckK& zwEms}aWCw+z2oXP#3X9^yY8DSGFv7D?qfSfi6XDxQr(e1eOOX|PpQq+BG-rECtI(v zS)s;|t+FXmV>b!Pmq{I;ibxD`g)>1HeOKfw#qTkbGx(AaE@;BA;>oy=p4I2)*ts|`qSlW9s?e!h~^c0<6P^2oE7D+Y-AoqA~tKyQRIiO)Px5xsJe}_pBCj38_;2xj!)&ukuPU6l& zn1D!BM5_>r_23&l6>k4Rut)s6Wf5z;iFCBIICya(%WKSzQ`&BlIWhFQi1tY#hY&J; zBPVajp>n4bB`?I0fwN4^=H8;?6Qvt6^sw&r>D~LkMc*e%OiNBmkR_Os3gH`i)NlS6 z=zgctf4Ods2;Q(twr1O==5TJYZKe(o?i`J)rYp$fAvT$^a&we9xtS)NX)!<3rFq-7 zJ?*lCp{<*%xI7|nCEZT9TYA$CE?LOF%|vQrR`>o^q5Z;aQ$Z0}3ic{2Bgjez%S$j7 zfSGh1{@0Rs$lB}VUsp)?dl-21_(GGtH>GWs`}ky=kiabi*Y!x6iV-UfWGoqwK2AmG z$H1icY}RQJLmbWygrS8N~0G4O+11aU-AuV{s z+rgk@NoHv&9%(9yfy*n1o|eP^;YR{7U8^L*vX~5dIoIQ~l58ekB0Nem`uR6>que$H zNP!o&DYhxV54_-~@Cz}uyUc%iG;OzLkFsM61aL^heyD)V0{7Ksd;SgH1dv${)_c5& zP035pr=&36-cyr2irFWYWExPV9Z|FLkY|YAo6*zjETMIZ9#;WV4(`Adi{c z--X0JsK?^GfpNywK8I-QFu;(8VR_EM`WZh2`9n}aOkn~7W~+dsnw`HrK-slQqtPej zY8cPMKd0Br>wnHVd{~*At1r+XpQwb4fUt`bdDcsK_5YLI81CyA%VotGLGKM`?L6ut z*czC?x{&cD#?s7UZcAxcbDQiGB0&wcNm1q8^+P{x|1;|xsdPcIQm#3JEMD(YTUcA# zDBs)cyMDbd{Fu$WsT)-va2uF8FdXF00o7#_lOzb&0H_5v)2zGZDhg3w? z)>c;5a->D_=IIY_-aH-GhXXH5It^v9_ZUzN*^PSqH%H!+oZI@eRz%;Egj7b>bQS4I z221F>ohYEEgoBrd3>xMpI*5yW9}m)Z|NP%~upYErX32*O$nrBHfNn?}U5<2y1gOES zz;%k@I_xA%yw)sT>eY^zSuyyJX^B1qh$OYZGz1525-iunB$4BJ39jC$Q#g4JBwjzU zv|fUkmr(E&2VrZvd@=p-yogpxXc7qimk<>Sd*D}%Q_dtMFlC%Cg)1mHrA5y4*;DPkqP<-@NcgNSZy6X z3Cr~laHd#DUmlmPu_O209G|gt553I%2Arn}#zGFUJFShzS zlJ#Qga%`jPC8TvC+c94veR7=KpGfc1@qDB8b1_|SYZQvLqF4v=sVCBV*wSGAT=LHr zoX?Mz_se;n%*I7OKzwks`H)q}DX(_0Zs!ZxM`X3)p%NW~JNpoCA1V2>w&^VFUOAjj zpRU`KQ|Jq|FbVb9AhNtKxtDdP<<$9Iduk69A7zY%g$BgEKSc`G06I&k1A0hZ1t+cF zlw0t>1@Dsul5P7A7ao>lPSdqFZzZ#F)hco$_mzOty%$N?pLr1(SG{`j2VrRZ(V`(A zN^jV?Ii7{LUssuakT@;QBk#Db3>A^lU+igwRKSY$sp=KV%xIzGSevvVz@NJoElO3T ztCD2W_f?;hK^J?==E5B_VBS__#(dsv;0z_?%T`fERzYbwsI*HW5~;#JErKi4L~oBk z(kW6;mD0f~|K!hfI~Lkv`?y4>C&fg|BFked>-lNF7oOrws$5lm3bXPC+!e+%@*jxP zx7Q9R^O5#dt~IWrjx*BynDjt{Z-6XbkLR4zY^%wzEyQAv(mEDvvaas%tjG8PaQj?g6JFwn2r%eJF&Yu@W+WaW`a5234W{oNY^SR@^D#$9$%Vly+phT6MwfgjIWysE>;lxf( z?7rDvvr{R(RZ;+_u!h-0By4W1MxCHZO4Vg1RWVgb>Z(QZMbVMrLCURRsuYBFq&4cI z%);{0^3uk-24s;p6l?3`bq(6Y3Z?XLMM6PfZY%?}#GUL{v7c;Q$Zc2@8nG&CK^Bt8 zmrluKG6z9aWD}h%9~e-yZHrP`v!Xfdq~W#^Pvv`<;Epg5Pb1(np1&j2?;&P|pWc&8 zcRbuSdbv{Qh`?d=kgQ#{gBx{fT-CT!%bP!cxZoC!NJanUyK24PxLM00-8VAx{OC_~ zjcvBfHivhhxA~zk%>O2bc@M5f74fq)6MuWSLHsN`!SZB1iEK`!jt!+_Vd)H^Ljwan zJtyfs54(CE(cL?8I6vP-*qW3ydUPOtzk!NeM?}t^I9Nu-&xaGyZx60LujGg$aBhuH z9yd0+5bP^ha3W}5siT^ znBJmYpkc=dr3G6KpN0lCcplc@KYZBr@Zo#*j&3B zO2Q$cg@S@-&l(8pM=WpzBu=M5Eu*N*qfmCCv zk-l>zHZLJ}OHo{I`;GeJS$Vm|hki!%I>%52E!XT=byx}$ma--=CL=a|X=IQ(NWCmB zA~hm4N|%(*7-F+h^|H*gg2cj%qV#PBb7sD=405~1tc-%JtgOtFg%vrKx!={9bs0(X zXwS&aOw?w;`#uc~iVF8y5|@;vZGax~j>;3)$|{eYKXAF_BxbX@8K+kltBciV{RCpP z!{J8EX4dnuY+(lSUgc_CU`l*iLV7@QVn$*{P*ysAO}+(*RS{(wCLL2z1L0+5aZXL4 zx!jnQotsh0fCYkOKcn-Bay@{gfwmj0wM1h1k|c=UmP+{j4_R*v3O<+D&~5{^lK_6l z%K$Q`V}Qu^${NA)H^>SwzDQ`X8#S`~J`acuiuQ|l^`zo)ar6WEK-#mdeWWrcadkto zT%D4l(jfMqrd;p?SvK#D{0DKvj+~qZB|ML<_m8#CaXEo|lkBtJ1uXZVh#w~@OwLm! zcXXrvS`BAA2^}Vzvt(S*f~X8#Dzt-BHCnAMO_#yEy(rNcbUJwGa?|qUX0U^#<(4P` zUA7caoqz&{J4i6Qgg?AH)G7N49xh=;8=^RPIj^A3UF@sG+0zN3LnXu!)`3WpjF%h_ zxb3}*6YgTsF7IjEzmj*1xg-Qnd=!?~Vkpd5Op>3MfB)Hjt|R^-YplWSuHE``-n%#NTBzUb4Txd1 zi_K9?qe*nv8dvYl`h~kTlXlwf(s5acNIHW;3rovogw#m8h~6a=5RvTd2@Y8YOQrQN zOL`9`xa5>w4Dv%q+WR*M5{)D58Cd$T`hT%Sv19-=C|05?v|m18FdYC%iWPX+yB+=G zSB~fESgNHzz#9jtg-3qBDiIYC{|JY=GqD>`Y*bY4j6oNAR;YeU|Oyq1AblpirOoIMMPTk zC4ni-!>U34J>2>=UC}A{5lnRTWBMWKv5H&MaY5v(trNJuJjBg)4b58R8p{O{>2c^W z!d|OEwbLaoLg0Cc71WTOhp`q7M2PYDb-XXZjJA;NSU_?uo&Pi!UVSZlV#}eGWn6~` zJSf=-@tN`R`1p*p1Z9T@^8Q!GY+1ET2GXR}wd>jTw)%b)NyC^p<7ATI`*bEJv3a|o1t0M!vfI{dm zv3)@o{QJ`w$*Q_F`y&P4c({lZI%NV&Vl=uMwMJd0PFU%Jm7@KXb?t{>>Njf1B7_qB zfC(OzOO|NK;=hSMrWuX=R|M!|()fU6Nt^B5Boo{mcfu~P<&pO#q`)?nB|R@rqwnT} z@>fi{=iR$Qy30#!575m_eMAN-Ed#}dVnay@a>$?|9D%9-cDfketvb33NrKDKJp_?H zzmd)0*$oj-2^+NGGr61f!Vy;bm5RJ1CnYcfNRPWKa0^L?Z=@n6JwWaV7zuiPcX_IH}UZON+LRO_5sMlq&wZg39#@y4S=i0 zg#^;+H-9HR3}jx`U7V;h0pulM#IvH6bIWI^HkGqe$=7!!LPEw!GMN9H4DRVB z_9KI(?QY^>aGqh1=|=3~7m-7e%pR{`M8j-Vh>2l6k;AXuk>3%^LV4N&zseyKPJFi> zRJ3hzZLw`}uhtXhNZYHnS1XBRKwH1PE?H$|#xj91wR2~sxBXYAz zuY(X&1i2$3D~(`87(-Udp*k}b(B9-)}y#>O0yJzIx5G8eo zH}De)Of(jp5u-V)$3O+u3+g;F@Hq&wbgqJrL0ICG9Xe|n5@fN&z^jei4fpeksGcQm z;)l{;%U#}qwaqA*TA-H&j#^H;wGJy^yU+7jIzJ)E#aLC$JBn-{^53(znWd!nSkYwq zf$u!{jD6?rSso-bc$e}da)T}ufobDk2QMH&svkYa zMyn7Z0I_MD&3@+$z3gcX>0WW-huXa*7lXk&OZZ2uH2d@akFocFi{fhAhgZYQZZ^gk zmm#pj&Zw~)V=S>p(b!F5Lu1E=Ac7#hvvgP%SlFfa-ocK&ml!ogi6$l*O;6OACzdnI zS$zK2pn2Z+`G4Q{`+ctLPC4hynRd#3U-xwpZp$Yq-~GbuM8P%;0rP%o;85%dPK|2< z9r3O-A%yrzFUuBRytGiSmEBQc>NZ$12w>1^sjY3k9RFF$B~jY6O%1Xz@G=o4tQoPLH-Xdc zq~s>&8x-On9iN#UBYY;mxova^KXH;i;yp1XCL$@0_X(}4ZYnLTG>PSZ{GR`Smsv5~ zr=br9Rf*nLdyj1AymtC+i_m9h>4mT8>vYC3x|AP2Au4pXm>e0O9L0P2)iyU5RWw<| zs=Ggy$V|!W$ck0(kdb0_WKO7`{6reLjoWN1R7Jk5hSij+7iashS zlHcUrv~Pb+6@q}9(A@Mcl-=>cBzEm!GDED2Dhl1Ig-v)EjASyot23*I9G|n@mmE2R znA6l$KVJk24xlw|K8!8XHkLH8RX+5L?OTSPA*Yn->9uu69-y9@_67zDCJ9MN2>5_}Qf79dn2ecxmbN=8P)}my7``0ohB1rDFs8fU}aav$ITQqfkjw zn5)38nGIlu;^Pw%;>8deT}BNIXu{3r>}-osC?^I6EMbYykGkL5gUg9G$HgXqI}66c zv@lyAp#&LXjoI-z(0(%K0RJxM>5#T^xpC%LJ!U7}DI;v22uDm|^hR?$ED{!TE>f1F z1~(-WmuHB}iQ)CJu`yzVEu)AgF)>C~(OiK( zH!4c6j}oG6*#$J7i8AKs3;2TE+yZ1NB=OAmxJX3?eI7<~F)w@XYwkcuHrm7XSuZ&Vsio+*lA* z%oi6F6eF{oJ%Z`HU&;Y0q#+vm&X%q5QQHJ!4umOxEiK>|ei#$vDh9Y{ftKUK7zlE4}-D2Hvcv!eBv|4sqXm#)fLSvgO2&<(1!H|n@f@QKt z4e1$~7_>jVPn5Q)f;|7RKjjrns!!H^Dh2+omWnTA9r0;Hb7xPy_sTz-HcNkP%FMngI{ijvH+8SzQ9&w}OCV%MdFWa>>x z-8%M$su;&43xL`Dg`0QDtiQ#lyU5^1A{MILzQ4cY5`VI=tRw>-S$bob5n6dhLu!fv)HW)Ool9y=N>pliYIJHOkhLfz{!H4DoH}5cRJ2dmFs`t+ zu&xlReN=5%>n@jm(lWDs(a{aqZD)zkNyv$p6AlX-<~!C?Wz`mO#_p-H0q-gr+Vwdl zt3}eICNv2H5}7s?0#efCZ1O7!QTNy3iaWyqhQ8)xztQZUwgqs8fM?JtJ($U4Gs`pb zjm4QoPGq38A55Yw8ED%tC&-9)GA5+QCu%d<^m1c8!z0m{%(NO~x`a zo|2}1^H_k=TH%bSVLtEAYA9`ga)a$h-c86!%t|&p!PT4rS926QiC=cI=@;$&tIo+n%Q;&>mXaW7*rI zy@hBz4;y6uhAF@Gry#F*A~|qifN88T<&=y2%gYX&(Vh(1=TR=?1^Z=zAi5VV?>;D$ zuBHcf+W)SGI1SGJMEB8fkvcex96IE#*+<7{zDHEJD@27lEy}JA$-+Ikd-n-MQsf)k z{W^uJP4TX;bgXqT$>->0a`}a| zePdUl7W=h7Xs}RqM}SWF`{op z^4`ii)#YznA3V}N@_ex1TOqJ6b8lT`ZNEmNKK2ME*e_C1_AzoM6X`6O zm4_Z>-M7n#;twq`Bc63AFdV5sUoHli z(Ey~Q2U#*gm`cYEqW$~#r^`qrok>2OCH$65sB`tfr|UBp4j_|y3-z3)^~K7cu%1F>p))fT1pfmLYP-DB`aKW7V}G%#fGiG2C{-V zi#fw<%>>aYlb>~QNaqC~kOShoo5^d~ClEPT*os)!#o8q~%Su)VQmE|#htq$p`7D^1 z&`DwU$uqI%`17Z8N={+}(l5nC`86+uykN`(fw=oR;#q>p>L=wxkYV+3}*Up#a&S9Y_LuG?BnmL?Zyna|hEyX%4yuY8!V^prJ6Z zE+&3ZjlHOq0}}9g@=svGMdAl7`h({M5~{R~`;c}}YMZ0A?UdfY%zGz3Z{V{Nhj3=* zhg5|0EhWLALXE^Tq8R1;pMgv9PA9gvB&PTa}!0kDY%!Pa``Iq#% zw7k4bWy(lQ#YC)x&IB5@IF{}KPM%uY+W`fFC1Pzz^Og4YzG>|T$VfT9ZRCM=4LNCj zHi+9~++^C4U3}M(4z8#6H%2~Pu+-77(Z4yk6%Lmr+X!S#z?AnEX^nTX{UQCv1zw51 z_LcUlyla(Lgh_Szdy03LwmL0sW2Y@4@R-WZLUZkvWwmGydVpr52r`vTP=KhJ! z=7K%_z5KivoOK)tv9RfMFe1)gRusRxC1F$2CW8}P$Mcn>)eLOgTd-aQsi?bjhYR|2 z+u03ALDVze5s>?>2Ua#N&O1U99J9T>GPd#CyiyXp#UnIfam-5Zts9)+%Nf66^|qx! zA2^YyDNLMSlCO`}$K-2)Vr%4-@()^;9sngW67AY>+~<6Z(;Aw{BsMlDOE0N2vl_)U zB=LOS@rGRokcN&waJ1!Y`KL}a@>|AIYpQF|HYC->L8&(CTgH}#KzGdXTH~n!{yUKd zpY?LAXsv3lZMeM5@%N|1{stLb7k<}qk9l9_KBLNd4fZ=C0_E@_VTGk$rJlv^`CFVO z`7)LB^WLAKoe}+h;C$h>Z`78Et)U)HXT6wHd|8Ww0pk z65Aaz)mVQAitn(mEPRT&P6wI!_z$$-sj`2jFJ?!J;QO3>kvLu;pFvNn>kbqNL%CCn zvNyUdk8@piDdB)DSJ!?t@093)+2rBC{VSJ-xPSa{#rD$}!YEFawH_16`~LLRHlq3J;DOI8gbd}5 z;+WcIZBy2srUI;eSib4*MGzAF{5@g!?2Zj>77iWCFFJsbdF6TA1TLdG4UM_vtgK9{ zPN@{2UKU){jlvmcDJ9_Az~#4GT{X<39$~=2r9igH=`81!V$#RS6pT72GT?9-Kp0!jKrqyLDFHaT>12N2&tX+v4zxs1peo-)K;{s#9__3b z{Bk~;-|k4iR&e9q3!6D-VD8U9{ZM%I^ZPMlfpkpfCU0LhZmh?N+ut{R^6Txkxh?|w z*RMIhIWt0B_{QZQ7Ikx24Z=Ws(cmjo{A-(-to%4o|G`S_@^ZIBz5-bGdw9&8LwjlI zCi3x8n6bBzQP)YBpt0AJR@=}w$w=*~`toBiEKY8GL^$%Ewmz{gwpOUks>!agsL0i> zDO~cwwDyBq$%^N0ziFR9{aMpS!-fr7+Y{ybG`HmS&|GAt2k4%Iw!7=M@H3*XofkE6 z3aQ5(WnF!8Jr4`!bfqRme>(NF8JamEtZ9eQ$49Ffpr1ZM3FA3ks>~=Y%P7kOsRfU8 z$*J^_QnP#momoxaBVHFi$*Dgn*gBl;Lb&V8u1%e?WcIY_=jYrMG#mPTeeTQaV(-K1 zpMZgnk(7UTE`8MZ?4y;BI(3gUUu%A|-tJtOXuq{%BxfBeaJUoko~~=r0zMl_h{Q5RZ!FJ=zRzoee%N( zPekc;Jx8w70#ZP))2{$^#P6tzQTrzg`8yk9Yx3b@6(xIL|`(=q!`i+2EmY& zY)IlgQUk-i6IEM0Vj`BIFC~YQZrmlqNS<##e zijUmzKSm`jJ$?CN>o-leO_`2}D>fL#odpNp+QXkICB0k8nD>bAF42I3EYX}^RZ?54 zJ+<@1j&{gSts*fi$Okm$Pp6hiBg)4DU_lk(s|Sj7$`lMeqv(g)kZ}D9Fam@JhpqS3 zh8e@N!-02fFb7-vlLOC(VA9u}7r5mf9+fJQ6jlVVzSHT)#%jC9VtA|J1t~UI` zRu6&drA#^Pa@XZZcd8Bl<+QKKX}5Y{$MdwOcFAc=WgU!zAJQvuF`+kqlis9NZ~&}< z%Vi>ZV2$`b=%BKQh6(%STG%gqWrZ=lQj9zje;f>KUtp-3L+)2q8qmB*KiST4pU2K7-MD54`My$OH^E7lCr--x$06?Z9 z&37l@P|~S1_u*g?n9tSZfll)sc(w);@4+ODCyRArmrUD!Sxp~<6j^hB8uk-ckjH@Y z4eDfY1X(R$@rRzoMm3NHUG~>>P$5&3SJ9Z-BOt90>4QIw^eq`H)so(QaVIjYuv<*>vJ%o4PO?Y?g z*zB>qN7QDY@elVN^ATHv(*|wT8W5$VhhtAKq(n!j#qeE=SWPLGGNMI8Zdy*RR_mX~*cNM~-=m2mKQ0+iSF4r#~-tQ{OPBJA9H2Jr6`U z1e@UU2<+@2f%bRg&|nTg1bgzB#j<5TkROsg*M%)Wj6lp5djqjI5J>%g&#(h4)CznoZp1{9|r$uDqn}9IP{{HLclK`p9`weAo^( z8IPTRAbwSS?+^0wnd3p8yG0`JG~hipYst$9DpKS7d47B^TUpWOj{LM2W5nPjEj}&Y zkPwe^l()3)K3;JKPH!ZarAe)27;SW7UJ03HL@B}IHOblT2pMI%WP%J6Jg=G#>GRIH zT!B}_R<9^(w|?~K^$5K5*9S)KiQdy$uy{Uu(y zR9&66&%fG9<39Iu#Hl4S?*HQQ^U}(r^G5&T7~QQa7!#cqk{A8UXmDRa;fgn#$y_K@ z(s1s%`rtc1JI3S(r^Q5*-*i8};#Ch-^^bIGf z&HI4ffQnz>zkXum9$ZVOxzcw=QhUrx5m1G?%6}`!NOA}x^o6oY(f`YTO=mrvu7Rt7 zo02+Ksih9;x(d|mI!%INyc%&Xk2y)hw$<0SiG;J|g1^_Je#b5Wh*jIZRcg&e#s8h{ z2bb|^Ynu~M$mCfd2;&`Qlo zQ-e-AU?(4f#Ua`R$)45t4edTMT;#xu$-t_POT==CblCe@UGaud8i zvyKDk%}>|+0J_|75lyw~*yOZTt89a81050M6fF&u1|2(^c5Br!r&UL>XSHphZIB}! zPKEp6vO zhgbd$x}}0LrimHep2@Bug&{@3Wyu*S_=J`ESk@ZoOUcwN2=N7dRMvOl2yfhtyq)*i zC%e{DrPwt}NhX-MrX!xmS8Pp4l0Pcz0_DB;zZnB@+&9=U@4q)f>{_5qFvXh^Oe=PI zu54O!X)5VGoP0E$uId_Vo!n1P?yC}w@FKsdElDm+E=*C;0YFW<&fhGMesSru8J#emS8!Tlt>8&d3XY?4CSrcC#R-m_l*rVb{6;`J@&i1$}=l%XU4YY7i1Qi+VhhhsjS1Pg6nQ);;#dA z_wjtQDhRLvL+P9SYqfWfQOr_`qq{`JUG}UGw%_Zl)%FE0% zm*!i_Q>(#-2+)N+KB;h-OosafLpu%qt6OS7_PijN5b{o4=(X+9YumG(_I7DqShv~( zv?rVCE%0<%SQz;Jzm`}HqeluLNV_^XvIVj>@Q~sV&s>#zbq-*Fm+yaeS!P9rwzFfg z`dJ5#C$|aCRt2j`G|3(tr6zR4vkr1l2RZ;9d4}O*gJciiY>)lU%4YjJotAvA1}5r$ zwMVIat-Cw5_gn2p0PCp{NhPV`s_<|Qtg?_U^^<;d=6O1l$FyqZ;{N@}U0sz>`1B#X zFhfX>Aq70CA=O+Z`ow`%W+Vq3ZZ56-lV(EGfmRO1%3Klri1G2-00QmFN+B0xE>Cir zM~s>{9sTYkF&UA5F#J~Gu$BKgEbvuXwjQvmJ>}_BTMu+6*nopqn$4Lea6Y<`2$BxJ z8>DeAlXT3Sut7{h=V<18lT6$c^jMKH;ALs|DH649oN>@Lv5a!*utlQ+0)ETy5H6 zHweRXtNqX5deZ+TgMXjBS*hVNl#Z!YGF_i5LC38s|v z)R_47F>aA=UL#jem^pXy^kHsP5imJyV)FY&m2u@}!)87pB03;N45M~o^rh}^yKs5g zPUV|i5?IHROtz)2x+PmoFFZ~D%q(SEvargxvjl{x=&EmD77MOtd=Y&C#!Apcv~uLF z_dql;;IvRPZ)oWT-u4H(W!nySh>1lycg|pTBvozoRN`j6pJ37CQl1)s4nI0 zYr4!|xL`0|5bqlA20%Xx3Q{ENz!h>jvHmnD+2B~ zXXU?T%$>3wu9>uiCT}uQh&de}5b16-I(O(TVwPlvv`gkVGxt}FNm**E|7|mW}kx1xyubs3w(V2d|HFg?GXQ1chGgFHWi3EW*nVqRJqJ5 zD%m39^{db`{wLewKjROdC_PXYT)v=D{Gf5-apSLO!Hop6C=>ZhC!(U8Md`gF0Q2Mn zz0F2`l?0ZK0Qz29D4&)P?mJbWGg)Gg?lAj{8}jz@2roudYR49})POgYPcF!B_P#yw zu6I){fX-`ktVg;%$G3>`)A~;vY8t+)Yx!kQXl3Z(hHH&qHZ(L`PTliGedBj^d+IMY zd|TfhotsfuMs8^m?u}U9`N-L>iKC@-N2+ZU*hqG$Tqh3m8NzFNo>C}ii;NP-liQ4M z{EFRK9zO7Ky)8Bez)?osj5Yz@i}hf(SZ|aBklwhdnya|ew;wbhAf$x=Y)+eDTT?wR z3~Mbzhc=v^C|d=6lBIWO3E82thIMV_!c&S9AU*)Lzl`D(Wkonws7#6m_#iQ#iA*Uo zDYK%p@)=VI8)N%`>&A4T_cZV+DH&`xft>uMjk8NOF@~g+{47=z*V9Fj4nzfS#JKeN z$IxpKmQwl5Bt|o!r(WSqU;CU3C=9I;G4R+999_y!qWFRu!ZC zaJl?`ilGYs2)X=z;M*i)-sfP=Ga4aMi+?gB9)475SOazi2pA*kot`G6LvSvsMpgF@ z`pMK@17!+5gF%HK17wrr^8_g*&Jj7})B-Z&5*Xy-@q(Pl_l{Vv3ich~ILC?=;RCu;|@0jA=(QoIOAm|vJ> z$rTHNn5c-*q!78zihi4S)EyAzy?yrA)$b9=SOW$u_fOBf>|Ap(-!O~YSJ%)ECeI!{dzKX>=?lcD0LHA>!_KDB<9!GS z58t`7IJ`>ChhjjkS%wcO6a@h|0DfblqLNXe1Vtacn=kGHNuA5#8Y=X-H*wwf#;0N5 zzJ}*_#UkRapaS}adF)(ecc#CI$jO`fWLXR;S#rIfS2;8mRhA3tGkpi)>z~)S&+{5% zcp`Go%ManVJ}-Y)8Sc78yo&PsC=~UyHx6*Lj7x|17v4ZT#0D^S4pjisWdwpsB?GCt zAJtU(QN_cHhgj1CjGo<#1{Gw$(z^e84McK$y7%_Pa=NiwQcQj`($dp=4FWzZ-6(YD zmEWFpqYCQ)aN3;hetzCwUXp&iavXE?ATY@X4!%F*tG;PZE|USDHC*0Lww05dQtRM) z^1*@2mblww#3jvF|8^l)tZBH4ClyW6je%uCS@6#6jeI!uD`xlCnoAI$h%}Yu`Hf9l zXZEklNcobYDX4gp5Hh%w-Ct3HcG7O5i?emv0&aECTKDaOrk|t2Z~IpLDqi047PB}m16jnzzB8x&_UtU&QkeC;3 z786X-CVz|Sql)0FL)udZ_nmKRiSe%!wz)C5S^CoO2y+PU8xj#5mK(b#O8m;NB4CA< zG>+z?b_68(@+kIjC zt9x{1{T@0`WV&<#_S10>RkkW+*RR%8Zph@xL*zD7KVha+iFtl)f^9D3?*?X!6Q3CE4sSnm93W)M){^%gW{5 zXRjad_+X`<*Xmdi%(jZhv>(D#t?zMPExs^QaF$f;%*Bglh|aW^a>n^Z9fGq`Vmr=X zfcHUaAXRN1=bBHiJ-zPq$ET0LlD+!OsUOFZVF_oJ5fxP-U}P)VN?p#lo!~yjOAR@}bg8mmFZbL zUVa1750{CqvhuS<@QuyC{8@F#=jJO*KR^7`^|WU8EYWM_FXgE1A6z?89Ha_Hs<%~g zbnGcI;4~UReNQ`;st+A-6jIAyPGvNT1V=^B0p;HtxIdpV5THTW{b&v>$O<%33jZ*D zprBEt^hA@QnE1u_Y(+_2fJpXda(=;xv!2W%A>K2E;*(p-vWjGXkv77exwCuUgMDwoqB@E>v!VGP|qt$=_K9FeZHm~JY$MJE^xI$QUUCf}%>t00UeQ)wF_SlkBU{8qtPlnn9 zsUhWJ1#wr_wI-no zq?dIv+p+kQe;(wIW{Ngm`3-^E#CvQ7Uf}-yT}Gp%cARBT7nL5DXf=Ca_<{S3RmIlS zCWn=Y71*UxbnkKr!sY3yP`M}+CCz&>ckv{htwbT%FW*x--H0Tz8#L$h4!!aeZEKL!(xzu{}XVwvqYg=^1ebL~K>W zTWOnS4d&+4sw*sJC$DqFflht*ytbk=qgWuXoTU!zs*O7ljL(rN-!9Pxhb2b{wC@tq zmp#{BaS7pwh$h1Wjei?9oubU@Bif3R47lIbXJIv5wc$n1n@iy{OhV4rmyp-lrd`=} zr6QeVU5eu_W+_V+GefBbrX$1!4rfQvZOjh#V|~-1-!4XeZV=CZpd7Vn?K|W4uKP*6 z-u=#L*_!Tm&JCd_6nEK0FF#X@e`V#kgneXaA$b{wbbHC2yw&LqGzumJnn-JuRW0?> z)duf6x@Xr>0r2o)2#7i0p1w^8V-u2+6A(JkugS=qXv@1Gl1FqH64wRqIwB`_?yQIJ z{g{sSWb}sEcs<1G$Qd07?#2JWNOL~^*>%Tt2gMV-J@o)aPe)qxdmc(t9 zA~~m)hNp8WX{o6Q$1>aOm_%q?B=FPNgv6}uysN+E7K#bw?~!1WHajajTe!~VSQ6qg z#CAIT33-Rf%FNEp=D%jMvl0?Ssn1cl8Y(6sH8C-spTuhBp(42u;6z0hYCuV1h#`Me5I3~-OWy<2e!qF1r z;nGx5o;zjPmbIP_WnnMrzDCVProAQWxLI^ohD!PJs6vXli%_{S4}Lp@dfdaM*OEWJ zB+*An?k+O?Jg8wHLfi<`Oi$1O*=tTbc4ptRzRGk=oIqo?@i)Up!H;t}hx8+CF7nGaQEdo_5lfwfOw(zSwa?1S09aWKg z&T5J8hsxr=51C7FZd^G-`FnEUnlqOk3vUna;TInWY2x#AI7qzSQ06RS_U5-#?B^{O zLn`Q!MddDpFk;tm+jgboP13p1A#*pm3F|hx#%|?<12VG%MLI%Bhx;>DCnYWzab(SF zncZ!>OAhddcZGY_iVg0CA5GEPJjq|2o2Q2x#>@6@o^9>zt*!X;bQ3|bY31~WZH5Ga z8rckQOHfg?3MEAslqJ^lM-Jqc?GlRyGX7f^M=s=NFE81(Rn(NLHtr3+^u3n6b@O*( zfAMJ0#%7^uW6@$4#3Eb8Er{x(mT$?*;ELeBR?D~F5?4?uvkq1lPV+@qW7iCDZyCXM z&XWGTW*5TCC0Ag5U)HH?ja`3n57b1d>x>3XFE`0twr+XekJc81T@E@1t6w30`CezYOESE;Fuu!J)6s+O7x}Sju0ET4qV(z^mSEN zDocj};`%@Je^L9p&Ws=Tys~m#9kbQXtLX$z#XYdw!PFM7>q{oV6{0zz`ChVsOk=Xn z>beHd_e&t;h7;v`VsV&^RjccCdA)n>#jb5+cDz7eVG(~6C(c%WK%M>GN7$@0Or?l61Dq7vXt&6#J3bI* zD*=tiW$n@v^)G7DLy6eHyw;%rM{K~S3WTkjs5=Op`;(v(1hJldJI4ays}pgkjcVb4 zy#AtG!mBz|a1j`7dJ)b#2#~Igu0dQ^<+ZSa{5T#1mqe=wv^;IUhS%HGz)%b7_t;Q_6ue!g>4#Z3{prwWXP znWgXxNS#KL!JLxel$ny0oy1c$n~)F-MI!yO)KKQms*%U&%RH^5J7MU#MkC2<2p`>! zE2y~f%|$W8E7!L)NafjhH0)x5NoFxxng!_a%jA+AFK-XFYqCuZ@JOXIgR$`IU{iB5 z0*2g|2GAhKHy;sJ?F2aZ)?ai^j|bQu+8#0i0nyvHX{no1HlBkL6aGVnxUnrw`BhaS zfYuKm4|oD$T(b3FIw#~00yeuZ>0=;na^X(SbiH#YWJnR$&Pp9Xe7GX+;yKRb8EUZz zpyJi*g0_2#U43mgn8nMz-kYMOQ*p-zlK1XhYdH(HcZ5U|5bJ(JhN`L#mjgxf$Ar({ z5uWvbhGK(asnh21)L#`C7aZl!LvHHt>a8MZ+J?|dMCR-vt3f-kJ5exPr9JE4y7BQ} z@U6jAZRtTas_p$EfEnQ=R=0|Ls>aVseq~Uo&o<4U(-{Lq!{t((LK&!Ezk*ln|q z&?&91cBHpXSSY!IwH|-}{ku?Rl84vwcx7ori`csFc>ACHgA?SO4lDbQw?E+jJdTyt zfA$=A^V}!;v{r;3=V3JO+{fL}Nfw6}U%iPF4hd=vn?3EY;kwyeZ5@oQW3LW@;9&oh zwUS^A)pFJh8R4>xtoQ+MgeX!f?c${UwgZg3`U76AZCV6&T+?+~K(!&4iug-r1H^~t zvc8eqg3Cn+M7(O-V%q`?a+G}YZMST<eKbYMH`QJ@9{KFOM8x*_a20e2yEhDGl@)BCf%YTUmV{v&=Rc^J@1oBqU1|N5CPmtfZEF2p077vizC_p1O zgF1UA8sF6<;5$s2R(~zhgx?<81ah6n#hDC8&l<9lj`@jBIV`%Ae^BgqOO=`(UzgP_ zT{pm)Q9r_|ARoZaXEL(Ii`gEj<^x8()g|xr+k+lz6zXlQn>SQuU_Y$ah?K$A3 z2C7M`44I&$B z>{hfO5=$Oa!|gvur@5iGW&ju@v1&lX4yn=eBlPrZ^@fH<-ul0VMwZ>>bF{+vb8W+WtAI zKMo6U?Lww?;mk5{I^58&QMcUB~-ZgaMe$7Wvh^x0u{ zvrpUJZ1EaMOB%9jDjNCD;cR0~kWZF)4a6oiSdw782=)`8fuXVP3@Wd!tthV%;g_u~ z5B3wKfnD3UTS=dUeJc!*Rx@NA90&L4?>zmTHjkj=LdAi$)lArwgpVd^Z4YsKPRXN@ zQ)p4q%rv0Gbs?9?^zVtw_n5X^A}&2}Cexi6Co&x`RJ+xcJM6w^jnK7}UE{uG?b_X2 zj)>N!?2+Aj4uk*S0T`=8^dO})2B70UWD!*go&B(P_mRWyyVr=%yx7Ro@n_C!0oghP z*OZM!%K|mPnk$88{ZOL&nzg&#kBFUKY@w@p*;?7Q9p1La z#@JZf>LpoAb1}hml(Vi~BWEQ`Sh^eIlD%{_xywtdB}QVU)#nn=>Q9S^fg z3uM6=zQOG6KacV@#%Gd9U&bK*Lnwr`=vz}-6Ly9M1_t@ZHpJBH>s9n%r#)Ah*HnAr z99`g^FQ7es#H0uKWdy(+sR|EEjgJ!D{{pz?>c6y8yVAJY_QSQe{-B%Z)d-fL%B6wY zu<#%_8Tz`+1no~n2mB~{=m7o5ooKoJDHs;1$NF%;n5gBeF7MePgw_OChg7RVLZZWc z&>{odrXh+iFQ4py^iXQHkY8lT$P+W)szY!X8?Va9t}uSG_2fnEpEvG(eMYD&Z_01Z zYsqgbtf@&YOD>HrQsJBnV&Y7p{BU|B3IO4>(ma!xlUrqki<}|5eP?_xwr@6!0kU|k z8+_>s+Do8zgQ)!yidK9JM6g)$@l-LoIi|Hut7#ZVS5dc+$sr!KMVu6Xf{Y0x#yZq+*4I-YXVB1K0x(N@r(Xk*}?#FA!rO+NL zrwqoKyh?xEPhSzuK>^tT{G`EyCV3aTOqyWGTA8 z6_C{14w_B3v-r`2tYkECeaTuQRdZA0w=bFlGL{g4c9mqz!EdjBzJK-jY!Tl10RW`p zb@3<_rF4g>@m}5OLjRNQvjeNgLr`UdoUYgNbO39;g0Qw|`tk>pgqV<^`0!}e+7IZV zu;*{%h0;SGieUx8=BQHDN4KL;#|kYe&nGWmgu;1oMNUb+>d-}Up_u&6li$gq@O7Vx z#WCgj{BYI92?gjA%eBN6<6mb<0pC1=*I2YRft`SV;S2*YtpCs7OPzt8136NQ5H){V zE7-OSg*X4?LmlQw)k+MldqenoxM)jw2sA)vH*x$>^)oxnA+a5M1X^vifP+KkjDO}j z5IQ^XQ)6iAPikQ$C0oN2-wjHV{?Dmk5?ILBB z+si_l1hSrODlKagZP8T4MJ6Of39f8pLUy4@!j;__h9f=smu@*5nfPLB2#OiWdWB-E zD;w3FHbZ&!$l)&q;=mqk4)rP#n@gHY5Awu`y?S`oaRL2iB29 zFi+%X<>ZK@nYA595Z_X=mg&6VOlNV^+2Wg*=BB2A{4?39zk_Wv`@to06wJ&fgdNkK zHXkm@kerGDmb>JhqcojeKtE-kO>*NBvl24nGLo|#$&b>@vefod#v9`wvQvpxXEM1+ zzgjq-vHj{`$V|lt4b*H$x%jq@}WbFYjlI<-U0$Dx< zFYi%$fnEY(lY0gSiYN%w?@~(PHgFocG2>aOx8%%8J*C$ec+As;j3nyVWyd_RikwYh z>rFpJ#K3%Mvs`PF!HIa=0BQ!1KnoEnQ#{~AuA~p>|GPUp@~xr;k5 zhkq7_a0Q-x3TAUH85j3i*cHEvHXl0Lrn0H&+csZS=kX=ncJjJA>9d}^dg5;DgMx>k z(Hla8Fyk0ZYyK|$bJvfjNw4+fH6+>IZQrsd6C#PO(;b>ea=5a_&spj2Y!}LXhgr_d zLv#`d#Hi@|9{AY40f0=bqdX5uo0;n-(>F!PHH~tH`Pan$bgR7WJ5l3z7E^SG79z+b zJ#VZX{FnIGUj)ot19)6lhiyyA>&WB&{kNgN@fyD_f$Zim9)8txCRK?Y=zd;pr8*w$ z=ngAqQ5U2neLAz4<4{R=swJ=Sn4rDkHvDh#{@>({cG8bWyXE8u$#0Cgo@FstsS9;D z4niZ1-`*B(vynPxpvR`nY^N_#Z?1_t@`!hK+VUYCArcnwtpkrpuS#OaqqllxO~1$D zUw;$!C>fX`UzK;rCTF|fLVA#$ux70L<;DNy#Ef3(J2Hv$3k>uV-e&y*D{DpTPGwzX zWv%cVTU!|jS<78rJIMl_R7XBi(}T7;d3nb3>*LN9e&t1?P2>a z55gWM${NJ+Yl!kNVJDDv7-0b?g&{lEhlk)tSzrXSr|Mz_Fv;#R5^Ul#{e^ zlw~!`H?IByR|QB>OkQ;4^{L!05~}m~hNU57w+>|Y|Bo-*uTwY#X96UOZx_t^`{UMu zWCI@;=)3jD78f{|q}RD0{;K%m-2RZ@6N1kYCWUPY`XF~J?>#GVy*LAas~&Wc7A*52 z^FCai)3j1({FKRHH3cnaq4#PA3pI>>qV10x{!@Cm=lYg;$IFkM67kh@m5Mn*XonLcgkzjkDUA%hD zVv)Yvl|`MeJ}#%Bi&%I zG>SGr7_4=+pLxv*S_6OLdRj;8U?y4u>n#jFw=k}GLo6xU-&U}CQPM0 z>8PdDnWvlSIGE_YL`@7#MMJQ-UXV&3bnTUZ9NmImbQCJF8esiFbOlb?5wv9|VduK3 z1KS+n$5IcqvQn*C`753rKmrqWQ0^f^bWj_yb!^Zfd8!Vn!xJK6VjzAAhEXt7k$Ro< zx{is-ODHPVy6B3F5@PZM%}Q7-K}c~(DVK3biK+~i`s%Wac`{E9dqZIjm|p93GPwlt zL>L3P!IG0*BN?)!A2cbg`Hb}=w(Eu*JoP6__F>9T3R!8pGX+)aNh^}wz^fS}n?g3o z`)XOT0X6_K$bojR7b1^r6Og%(i(^79A+Sm6*^tn<@EDoS&Jr4s?pYq_)ai;5Xmnn2 zLWvykm!Btgx^`O1E7My;tDNLvrUj354>H6ZC)0!AamD}cC1|$5R3ZCO@be9#^6WK+ zvzqL)&H!U`ngM4gPMmlfqKN-LevnB{HF`8IeYO8ygljt;2A|J@v$w%qD5$af_U+pf zfBxA=hw?OOvz)CrcXNkz&-ebXT@xowyoD5@Ve&Ocd;eKwYs8VwplX>7puq{HCT$+> zu*PtZ*rx!+{2Vu)HW2Jwn#5UHJHgV~OEyPEtf};L0*K`^2KQ{?!tNq*W^&=(HDpkO z=e1NxL!e^EY0?JbInfyE;Ti@KT|NrFXW?X6n0sL}g7FAKnLS9y1L^ATFG(E^c%Y`K z7v95mG7cuH5t8dY`B}TfG)XLH0C5>)J>!!yl4De}cE-4lrd%6&Wg{QMZft`YiQ`Ad zoW8nKgd}fDqB#{hF$POFO>8TbGjAx^ zB%suvsUJf>8oeDf74u1??z!Pl=3Kj{-h)>T&YS1PzdF5UyWUyVC8cmdm?sQFOvJL* zA*CZDCT{^fjEf_{#b?xm+3@g$m>5hL!RV%`)6ahVkEJe)_4Wz!P7*gKG@2$1J*OeYgXp0;Q!lv_XR9*Y+GGJ8=3Vj z2I74mi&y(G8V~)TQH!Xqh`yylMJqrPHwU9{uP7C&L7Kuq9I4+u%0@!38Qo}C-r$u^)Df^ zYJ}ASLh5qpBPkWK;;)4Z2r4MoL+Q(o4z`6ce)0aHzC7_%@9;0Jg(q;Sb<}Ly!uTfa z3;{ZbVRK{53F!u_o$XJ@n7pFIBEG07D=$y9z9ijGPd8`h%P#x-L7RkykaEnSavui4fYcrgx(`%w~1L0lW=_oPm$#0K6CQ2<# zcDPV@i0ozV<`7Wtb-HroH#iom=wDj|TIqu>Bp`@Z`$HZu5>!HGyi@>51^Pms6)LR| zsS6~5%2_%ZNb=bZ-7|~BZ1oy7LTGwGd;H0*d;5q=Rc?-`2;x6tgZ1$-m^X_{ zsBSn#4E$KCyHCU=VqTKo9L>*RgCc^0&Eh_)x;5hQM=H8>B*;@%{vW#D10ag4Z5sw< zcGpcF+p-3B*%?jj-H2Ud?_IHCK|rNT?;REvmbS3;4uT4(s9?i_(ZqsX)WpQZ5>2AU z_!#4vIp@Bw`?_eLip-I3kt1B+3NJIXV%O7Ezp^y5 zWBn*ZYq3v3jx#qvJ_|_~kDh3#r{J963=*aYHOVrP8R#l)$`b>!z)F(WNQ4y>Cd@vul}YL+oiUJbO3=>=<{-#^Peo zH)uI<$lElEw>FZFwm7`CF|&oyx{Q~#S7YfBkeMEGD};5^-#RU9p)6TNVWWK;LfY$ zt>!DLdD)-cxoBqKR5gNgV(Jneh+ngx?7w&V-i9ZxzsAT~FmRnZv+N*HTyI~#{fabe zuHGfcpBO^3h(f&gI6d*xI|V7}mbfDyX3;eM*t|mC_U?&h^c~8apgj%N0hc{4IGsip zKg){rlD`I6;cPRNcHXyf!L-T)*t_5mS{+EgMZ(W+ax?4+O(h0coWnMi(YzGDNCRdue3FKaJw1HfAk!_Jn6lWe0D=F?q-M!N?R751x z$!9yr@Cu?mhz!` zQ_Tz9^2IZ7%R3*3A0D-dL8GZN$__5(UcCJpcev#q?(lgHh#*}>f~wEt7#+-*Htqjm z6ux}`&~`tvPm`OgFOABx#*m>e!nkh#x1rF%Nd0ZDOqOjum2ltLiYCaGOcJ$9{#(Ts zvKd_(^nf>$Jk8HPGq}IDFkH5xlKOc!C{C5{rnk!RfZ#1B6`nHk#u-fOmE;!{IYs>; z=GIWlF7C(xn}Qf`!!!9Ak!5<(#$!LC zTDDEw9U(?ElF-`z%SL*OmYV1h=aUOOOersI)qo+?PFzb*Efl zEjcL$d5|kAMbK%JsHh7+&Lq=+IwRjpO@EN^u5HsT=qG0}j`_?1tR`SK6tzVt3ccmM5co6Fow>ZLm$!5iE}PKW=Zd-zyK3&sed`_ZzFmT5Q)Ao6;XJ8@QIao7}12p%J~Mo zu|?qIe1xazpIP2$Q6zr}`-L=7^lt$43DbzlshzX``=>a{0SU=VVto11+#jebXjmYM zUM}CJ!C;7@i}a3Y(Y=z)({S)5zLQS)Aa8pZ&!e612aQ{@NZ!#({gnh@tPTzFleDaw zQ9E88799_2V?MMqCj*nOQoKbfL4bbB8#BEEQl-ID+;lzzW5j zcgC+WvTnbssjRB5mQ4>v^YYipP9HX8Gwr3Oy@s5)KMW^ZP>_NeJJ@-gg{k`C>e>+iu71e_ZvYbDd}Dw$lt*(9*W&@JD6>|t_2#} zD$2(68~6Cnml^AJGj;cR4g8RglZ-C`(MJFJ#K-1n})As11 z29J1yQfS~YI61>NNce`12C&n27Pj(6z7;Z;6yC*GIt~A8+waO05b~z5LKY4wGa@1@ zOzj=z?~4qL6sc$V&OH$TZ4us4-2vNQfDtT3Vcjib7pKtmu zT?IBR{$I$%7vqU5aFP&kP1}9?%=*jz#BEb^%^61oI|m(gKIYb#e&q1En@4uuBlbsr zJWrN<|HG5sPn+*I+=qAaUv;rHX%kqB>Qdkcg^+5_Szd;CTk+*%D|%szx^^^_LY|O8oN;Cu+nQ; z5xXUKPIJgXnN8caKIKPuerp#mTdAd;i@)-^RKy<7z13WNP-gOi+SZ?srwkrEZc4v? zf+0#Dkq})RUKC!KQIuSONRS~sDJ(8DH!wFaTUM;ikIP`A4FQQE zA%SUu`e1MuM8!wN%2F!zmAh3LnJFn5+|``hCyMT6>`tkQ-xqy)+g_(aUAb?Kx53*G z?57QqB_P929h&5o5D^B1xGq^2l!~fSvoo^|Iq9YQ_h*5C5HiMTDgf<~JaH%WN$HW} zC(mR)iMtlt;(gEVut)jE;Kc1oA-Yvzv9e?_b!fDi*{<+)poZN3bnQ0_F3=p}L;n*% z4=$HM6s513S!?Kn@S9#kV~4oeZe8uQZ2RV|n>Jg0nRPbj%Y>al?!KO2c5KG&lX)e3 zrH2^9jJmIqiV_cREcOVrbM~GQw+JNO;^NqaS+*zE%RW2;N47i*ZcUOQ*#;RG$%)X| zRUJvHjVp1>NzB$7q8J5jAI3#r@{?;G#! zsSDU1=HL|taY6H*$R^Qx>AelUg)?q%xf%tGSccx9_SO6OsiKULnUQJ18G-shT}W|Y zdX!ccmyi$Qp-}EKn`1W7EG#Q5HD0UL>ci7R!^0xNqJkqbBK3*dgm^

      zA)4ApBHI0o=#zcPGS z;Z&!ro%w+kGBS6KGCVvbHIxgznSHPNtSni2yrej@II|?(+Ig1ml-NnKwsp?RQ^}|F zO}gZTzErxxGax!XBe5dpTEex+YhsT70Ytaq)>Q!VItrMO57SX_GJ&RFEXQ;dM}pfG z%CwLi`bm)1A@Wn5V`+F!62yc`u*X{|xAnJ@ft#TAO8dxuN%m!a+1X@J=KkBMxAk|B z4J=Lf$f9FIV`YFDu2ddRJCS-E*~8M4S`u4+j2P+A0(Gu7q4udQ#fn z^u1|&(+vJuc&TN$IOfr2^-D&yG(}gH)xhW z1L^au(#*n~q+;2Gc9}9_;exFT(~!+7W-QG~8+dWkofw3VW)O=Xe8sm7IW}L0H4P~n zhbobRk`&9Pk?G3V@~Ena-FRLs@H!=()}Kx}4Jab)24o^C4V8IW1(^j=xuMx9kf2UU z!=~BkIq6v$I7M?iv$9Uv8}otWv+2}k8?{3C82S@sR zM>JQ-kfTR~8^ex8Wa;$!thDBWvn6LL$Vdmm&LlQdgI4yf z(Y|p3)=_SeTXfrGyp6wd)9iuE=jayd795MXCW9vxY;I+bPyKeT@W$=+QH0jvjq?*7N7BtP1uUhKU2ONN>MIOxt0$MRYHGsf88a>kP!SoAn0w;bdwSIKH&eZG5rSRI(%=iaN$FRYKKv!9f7%q7{0*GQM%&{vh!d@VV zfPI*uB6wDn;`W|UNT_mMf#qd-8TLXi>r&5rp$as=jAj*)>4}|Z^ry}IR|v<(n+<1OR4D61r~_$K1@K4claWM_vn`DTi;Z|G_zd%>R1miu|hQ@}*$BTX^tN3{Q*2+i8MoIJCn)-T9+yPTxUvsxvq{HDiA^NnC^nE~-7`%bt?wo1x zU9tnAP5RJ8DzA7 z&bYa>r;7G`JeTy(VILZ zF(rjSW!xvizH`Ir&!d8=|gyfYv4Y};Bl%7xBm^uJ|jQY@+M|JV$E zSU}!Ivmkmn5$P@@7QOW?CQuUMQAXp8Uy9$Ok+FlidCPV?2I&qRmL|J@W^61PVTkxB zS2Q4!d){-KC#WaPT|2{@6Qah*`6x-rnqynf1!Ls-r|=H`+y!!scE-yU6=pl+!aE!0 zBgwgvW5-I)$>_o`CHYalb>~hbU$%Bwh(cOka+0iJv3~&Q4m~7}a0Hn3!S+}n7NVj1 zP|kMmFGrT-dZlk{sGqmWyOSoEY?%&Tg;K#>1)I&A!<|`5w%li5$@?RXsLxiNgVvGl zh?Qs?bVrY=5Kn3|Lz^cd6cLAFV*edWLM6n03h)!fl&Y`;Y(xjTQRO;n&bGghtRv=b z@COc5wb{dyqwM$;bOUQ3f~XTMfbz(_ zHHg|su{o=_<1bbL#Yt(cC&NQp^RGHbcJBJ3KYBZGh+8aL>bGSRhqd!P+%jF^W$ZVE zD&n}5gao~o|44%r=!JV1pWGrI0l5SWCGGOm1eT`Pjj|DH>b1|19wd{O`U?nUwVHi@y z)32?C$v{5(skX1+JHB!ys{o1rKR-fd#h&l}P2?)mXkIQC21wdvP`b+7B!?FNAe{JF?#Q4#O=aIHBWfx#3o2xvRn$>*WhQ&2 zopiy;6;~rzc-TiW@eyIVF!j<6r!OC?I&!3#BNOg2{4N@=-0I`x6vD!LZObIYgn_nc z!RDrG_b*jmtmYs{V8vwS7p4`eJMR+>H^nP&N@&*sjF)$)vy+N$l+uWPj8H3?v+BZa z4yncBlV?KrRHy(3dSi)OQ?u&!R~K#-7U&Yd`t)Ns56FT{Ia&gQYd_{pMcvu+IE7QU z)?b>NgOuA-2dc{(kE@8YJ9U;W+hDhJ+4>WgS#nBRlee#;jD-?yZ-!iwkblX!_R-Q6 zPU~0U?0z24L~dBCU5Cd`#3Z4I@S^i^vpkD&2I7n8pGUy~+_75B*mRdJtXR|t8Vsu( z(scl_R-0x?wuw1h6SFn$B26TJR6-5|)lBDh&Y>IBAtx9Z_i-e>zW9R`Zko!OYxdI) zPga|Cq!}&2d%k?l(XXSq#FCWK5*6Int+nl~l5IP7IYx3WN0aNDQP#Fv(r_rq z9qG5X+RK@Xlj;Tz>;wsl0|gU$W%lCGi9w$dKu4rFBVif-@D0^zDPJ=t zk~fUvH8JxUcAs`tQ`yidl)=ETN92eB=t;n}pAn4B1Ro|NKp)_*+L^H<%Y}U-3}6&L z4BGwE+_!3z^%0Ho>WQ^WVnrVUM~4CpUL~SA0-4jf#}A%Wx13zNG$u)07UMvbLUo)9 zyeI(3hcZRw)y6&Qn_t<@bqH{D_2Hlv+JgxV@Q(FXw=a@x-M;T=G&hJJ5dKy6R}o)X zQyK5eBxNNVjjGFMPG3HI+<9Xz`&t-|y-_Rv7$d@=Ac*+-a?_cXGskys$Ysd@;Wa}P z62%Y5aQ&k5aL)W~x?o4`iRBbr(|4lrGS<3xS}$tXX~pbtou3sco_UxoVZvI!TsoT* zuGeDRE9;zL$JDm`W0JvocCDyZvP1J_gZ)|-L_>?>7KJTlM}d{&10JT`@h?-RxLX8k zruez&=J~I0H696c+s#72WedYwN_nGLw`jjetwuN|t#ICwyID*|l>k!RSF~7;lBeHX zd{oB$3~68-Sjk=E{d>qNED{-Udk%R=dk2Sz7W>OB3udS6=zWGBV_xqVcC8<* z9c&&Fu}ECIj1dM%<6%r-E9C$F4knU&M1E!pE@oZ1q9Sua1MC0CmIuR*vW0FtGIyvI z2#$JWDn&B|I~N~;#2osZxf-$J~mrP)e6d$QNriN=;t-RK>c|lZSSV9a( zZRtD4Da6TVYo~RDvCGUy;F=s|E>>4wx({fiAE8RIk!fyn+X!sKCZU3XoIM_5E5T;eMy=TI+iZUF7d+?3K36U!tN=n4u|ZS^*^ud;pg2Qx`7A!i8Tx{9)W zc{PZZOD>;Szig@9hGiUe#>GZV(OGi5vHUcRsGuYj#i1kh@@XT&03p70<3(Uzwvaze_H{=Wzhv$c~?fVDIX*X%;X0YF$Zf_<> zHDHe_%1_aln#mbyQ2_)`+mOo$LDh)7P&Mr*iHwem1_;SVD2fl$hQxx?l}L1tPrL%QHGrOTs8Svl9!W- z6hN|)pLRlc#Dt~fM;1b=Tw)Zt+YOm%cx5}Krx4?M3xxZAVBG!5b2OvqS2jaW0+iWZ z+p0}>m18!n8_U9rxu5iq+}sl%UCJE^D0N(^It$(_ok5qO%aFZly7UL>p&~YO0X$+F z*#hUy#!uDsxlxV+;Qp4om#D?aKd~oLBN6$pPFQKsFF-jotZ)#6zB)l&wvVJwC}QGdd|e zE=HD^`1v3@QEig<5!W4zb=PCvHRmT_-JB$&HbY$3@b|i72Z^Z|Kev7L9`U{pemb;h z?&#l|x4===)#PvTR}LFS8j*UvhOQC(p_Pr#o!Kv6feac{Xfm!AWEmXpNu6XkFh!g2tgVdrrJGvTcj2(+FaXXR4nBRz$VN#fg>o^*S z41V8E(sgAZDS7moEPwsz0txvH!Tl~TdS_rV=kX)piX@MKps>(me(|G65F=+Elf}eB zvHwA{iQ^9{&unX4zi!*M_3Ik9ojudocou09u_?;4+Zxub+vd1VEIlihcI-}uI{Y|j z_&k39=i?{u{}ff?kt~p+>^lyc@sBar(VVO#BY;Qh1v4=cAhcc>s*l86FESDzl#`Jk zYDbr{7o4>tv0T*e!`fJ@CrEG=UE!0$3|1b=DYVgM9qV;Ungxit6U_oUj#)Io?oRLx zWZ@%Dfjk1OFBWp>=G{`#%dtSO7-)-%+(JN`-b!I_lZnLPFxe*ZNzOnT+cM|bWD>{w z30OM|geBNk+<{mp2sCvw{;F8qLFYmgT9`qw=86*XC+lhHL;AHElt70jfh2xCCzwkv z&OJ6FXOV2)a7Q#7y;bO{WaG)ci8pTCL(=D6XQf9s+#ZGVBpXp^XEG{ z>K8UR0V>oRw$p&xjlC5oH=91-k$UH>FwK3S!i?pM_Idgr^n>A z^R|u%U8+61&I%cHtM+>7H+gwk$HsbjZPI(~wcgk?_txxIx|*)G`cM*UwDQ`kKe>1B zsis@E?%X+Z)@qqySkb&=lbd(e)V35KJX3RhtxW%XHaKerKEI=9uQ#9ZDBdaCNdBV) zjrah3L~ii`uqN~I`DZGYv-}D&v9D%5wOk?M3x1|Q+enT>iRULpnc}961Ux+$AxBBZ z&zUox6AGn*AFqJkn=kLpD}Y<|WBEeq<~*Q%XZ{Fb7r94x_y=&pV8MzB4DgKdRO5xWVQf#?pGMMI zH#3EU$o74&zfylnuV=|}emXf|>i>*5AAWl2+?%wNV^#`>EShfr-Enlq-oYvGT-$c`PZ?V>8S3s@SQX~#TVl&hhI~OhK_C+My3gU$y~t(Q%;uL zjC>asgcCs+=*A)D6hfNX7h8!^iZ4w;q`T?Upm#6L^)F4k@H^^d*S3Yw0X*PQ;qKz+ z;pST7S9hSIrj9LGsf-R577If*JHU_ija6@4YTU9iL#x%&I+^na$lsxA2ogRHfESw`@s>+sYLz zgpND{z7UO1%}V0JuhThBbX4B~bcl6sT(ftC3S#o{arSkF7QqK{ z6Bl-a$w*Gm&Qxa^l4HT0zJSbvm?SZKO@>-WWp1j>1Nj_|xY08qo4rB09>fLwMD?hT zu#C3RHes1KC2jmNei`{^DweY^Awwv(Cr9ONy+mA3Q8LY;a-?Fpk-frHtDERHY$9^9 zBgz!&Y&9M1R3E__j(JW$eMmKA2(-<(=_78_8v%k^HN7Ten(1;5S9R!n+NeB1(8( zmHaAxh89AhGr)ULMqj^yqiV=oni)j>x4)Tv;1_H2lB_wP9{VEv z-IotYFWE1#`RDX1MSae3*QRk9wi#O|)1HCUBAA-JIgZ>YZh=)eS&2bU#mTFB)xpzg zmqM~vq*IHOSrySgq0c+}LK7XTqsu3*q+LTR`U2OGL-t#Nhdh(^7VaPq9qq<_bVM(L zPNWaK9cVq^c>4~ZZMhCzqq{bY4IH~jiF1BTgAp4C7q(i6gMi8ad0GFI! z0MGzll^u_fNcK55_fy)#iGHF6kah*|#1O3IhLMjKkS`Jl457YJ&t{Od*U1+z$;UD@ zkyhv#fYwS4d7K_jbKh~~Z2M>>$pv>s1X3m@vW@emS4>uq8t1uoIv5yc0D_%Ozg8h> zc_@Btoyo4b|HSiW^@Drm4L3MYeoe$<8%gp-zO48wCR^fd>JjwpcQM1lMl$(W*DwwL zQb}xFh_!QG- zC0Ub6rXg~$0_1Gu3j`+CWOD65xphJyE#X#?i2@(^Z)pQ2t%gG6sL9*xFp4NBV!^UU zd^B)}h@sb=8k0YgrrwQ_n_7_!@D9Ex|10t`Cr$Y?8;R9#U6Cg|RK9rKy2XIt{vus` zc3lfgc1s|sHO7&6Z6qPf$$=&C^^YQP_2(N;pFApSOYGA+>(a0jR4%v-vReOo+7EPu z`-G6y_P*;p7l)&5eR+qzIJ*2CfUdWK9u+K4x9yAt<|DM)7MYfDcdo2WbknHu#qM8w%quG z)6XorI{(J{`)&{2AH-ZtER}Wg$g_zRfvFw|kx9yPg2wx1 zW6}~6Qxnv&F|qx$W}0;9P6_&H%YxK zD{6aUWcbF4n2aP@(bo{k?w#AX6lcHY%C=jcGLJjogg;O}_@v@P z^kINJoWx!aBALi}UJ72X@L5RCi-9^~c7 zYTv+;liti#w8F!o8$^c3&>r5Pf0NR6@j{TDFdXh)VG(~i1VjCUY-V&;RCbI^e|_#x z6Ik@2{K0^td_%gZ+HC`spikR!h^W&s=7+8febz*_!tZG-2jayNf41b^*?+QV;Hdjk z1Dx*_1ejk+d=STbDfK}FO6sWb*MuO%D}5lADM^)PfQHSJ=NE&93?b(KF`ocHv8X5o z@T0(XcO(Q~&=vA?&}0k&Ju|9%PvE4x`}z83yhMT_?-iUXo$T54j#_(pHEq z){0Jrx?JncC!#u)?5x2of)AD;Z)7EY;tz=&m|saSgG3Le!=2XtQ>6{_34im0PF?Qi z6ILH85mpE*tf)7n%27!JZODr%)#v3}11D?*eTHlMiqAAh#p_inCvkwmM~~9jNTNpr zG968d<$Mo(we<*=19t+JKsYyWzQ(TD*iO0CAtT$7YyT`=WBN=Q#*AQnyk%o?Ux~O%Kc+au zH``Y&7+WM`G-Qm1TP(C9+Qm`hC=KGAyLV?7BQAjz!7bUby<-^CtkRKOCI*Zid233&AOfa?zja72g$abf2%fH$yI-X2Bu zHj>xo`Zn<)BflwypWxU=Y?FT~6^sxG!kIN8ijDJb!hB~rZ)^jFiZ~-Y{qM?8EwIji zw-W{QW(1i(w2^GWyoO_@zxrec^fC4&ZL!gHgTLJMR?jYo`!)ejGD9vRCetll|k zJ~fk3vw7>+x~jK2|3D`1;G&xRNiPqw$&)Po0=X|yYZ4}J>NjHQys5LN%=u=B)tT1D z-MQ-X&9-!Q6S%U+b^f=N(b-qO8~Z{HU(ho2&yIkg1O4&6=r(v}lFwzLRC+g&i)Q&x za&kr^tn2t)NpH~$@V#6hKBkY5+IX5VAt%9yo@T_A{Y{pyhQbEq5`T=~8}RwpVbRu+ z2E|!a&@Q8`$`_L6mrSjsc^LCTlIu2OBBS`RhT^s8d!g?t-`zDtGUEpZo}xa=B}uN! zxhc}PsCWo=he@`JNe-)pPb5L{y5c0342fXI33g9G_}rSw6sKkwN>qGrX%@6&+3ARO z-;t0np5FqmLbrFj=m=;c1u`uuVFiwA{*QLJq~1N2+%jUbtaNN9k>(>&;Af`GHj>h=EHA+K!nD_wMvZZ`bEdsvYt zGnq-(7d-so`t=_kF1S8%<$70pKUQGA4@nP>N(@1WM<}M7;^~5AR6WA_@Q(GBtJJg$ z`Uzd8o|u2#jf?k8baz)Fo7Due*2Vl1V#0HJvo5hVu7P|CQe##{Rh@`h7#rQ;dF8Q8uc2wIP=ADF1$crQIMaXU!l*BkS)6i>Cc~`cdabD zbdmc|SP-rc2oIO($TsCf)PXwj*IDNzye+(z+=hL9(HmZuK$|vu(yDl*xOvkQ0=FY5 z&?<-*FVBgrmP|49F_8Yej?M~ z%J_dt6_3D`=+HhXEP;2HwVB8Y2^qVK44h8j{09ifrB}=ik{7Gf43v#KT*P(6mlc0wv_gU=$@bQU|oAHvEjuXaV8CLEFG- z#1Y?H(|*uX{`S^f{}u#~FY(5WCdo?pGW!9rGo03|g+-JQ0uRO_OfUuYNh-#}fn*Q| zn$}(n=|7N8d_-rf=^5x(YVmy3Iaqo`hJ&b0lo;zCgJuGeN*nqPB|ecH7vQR~eWNlT1*rDdJmYo5Noo`HEmC9y0tDk67f z1Y)ELF;GoA>c*I5p}ajFcE45n68s^prcOi>vZkIv?XMG!EPG?xrKD&vV-1lhFw ztu`h~1&rZqY3=FiuPe{Xh*{Gq()E`5y<|r9t+g01=4i$}?)L$R)K@}B%%fu{yOis@ z35n73)gVgi;x*_YV#9wU5XeWrW1O@X`p1$Rr)ZbHCppSqzKML`5o)C6A<$$eC#|cI z4mDUlY?yTJM%Y6$d(Q8?_t);HWv17F6h;|hvbC%(12k@G10?AYBEkVP*%=sxsB*M9 zF&W6>#7UOJvtSWvDp1~AesKoia0aBF8uZe87oj^t=Jx>?59Au@tPe}*f;LNjE5!*Xt{Cm+qo(^ZW15Mi)XCJGk=PTjOYWh8yTERBY^C?=t=YN2Ha57 zd^~4Uscs@iH+bP)nnt&&XaKwoi%B4hyj3&{BVj*4GnUqeNZd%5#lNzC2kf(5{9OEE zH&wdGPR^^GJW(~lZ_1{5te=a~{(!$MHV>k#@C5Fz%qcJ6T3*zN#D6N#!jrL^$%wI} z59@bulMyxe$JnEWTb~|+A07iS%k8x1+*eeX?J{~$0-yfkd`xuh7ui!kP5oEuTEDa@_1t-K;=$F5H z|9C@ny#+@!fYp=!`nnw~tszT`PM;x~BV-&I2VYW@FhQ7ri;@M-taQ?4AURH17GEHB zSOYb3Q2R(`(qXv!!}Ns@nBNQUTlalU&)C3*sHRf@ zBf>%0hYT-eyE`FcP~tEG%ZYnnNSfP_}v#m8>LmRL)-%27it2F}N z7ooL33@x%vJ6S74{EFlu5UVz(c@h^2bqYgBZiIDYZgE_(8sPZi;w&)pX&D+;KksH@u2-haq3f&MV1d{xfrXGd_AOk0y zI)c-<5aMsq_k;68XVr+~!{Oja#Z!hHWHfNiHjr7>$}gg_JU6=!J&-V5PWfC;<)NZ?~>U5ktZ>u{{U2`DK`aoKZcbZGB zU~84;;_cz0lkuZk$a*=@(YBb7cfus4n{JnnTj$0uY2Gzy2Wok&e4wTpyn z|4Fo)4>wT2Vk?+khG<;|{+WdHAeP&9KbHR{I37(Y{WvUqK&5~tmV>4pZphHwc z)KmQWP7)4LJ{`B3`s-rSVhnNC@djf8gj-rb%8jg3ERTwTS~ZrFJ(|CkOruvZlMTlV z36SLHW#^}J-;?jfef_-z75M+pCErO3uv!{-p7^I_>u@C2e;>(*qr~!Du^KE#uhNM8 za0wEr&EMNFL%W(D@<3mI2dptcI!+fLb14*7grPe&gF0cbQnc|KE9yjq3F=0_03OkUI8_fU_5g9>tB8ddl-Pwg;!D{f= zFj+YndHHZtpf|n^h+7-8C-O47)JEc~)BIt&jdRmW2hvNiyRtnhL#$1FyPTmvwCR=P zhYmf?04It$bT~lD9bL0kAMHUm3cQt`ca*lh?;|d6uj|m8c$2)cIJ+ixkM%%uNl7>I z{D+mT#kCpU5l<@r1*yS%`4S4hz!>AXwFRovG>JY^dd!;?0>XOdWIE+rYW_O;r4^Bl zA=9UjH7So%Zf8E;CmSUdz9o;ak;xJp@y1#uKNaJ)SAPv0k>*1c2kFOGK4n)gcAGj* z1tpG+^b3*%$9Dg3iS#~Ol3b!MDZ$^z{i*am=|7E3R%7u-P;_p8?Dk-F3wPz+L70Dq zN<`;tVLCp16nuY?=mB$Tl7USBUoo}p%IBIGC9J$9$&m003;a^xmnj+jQ~IkOyt?F9 zJ|#WnCtfnP-3?xT!`j5qj02TP)3Ar)z3@r^XcXv|@2K}d?ne+QWk-md9T z7c(;YS}cl<1~huGwEbn<3nhkNLm7Ukge1|SN^n$sn0XYWe7Nx1q|Q1gEnGOMbNxxz z7Cr%KxB+c}TxZ4;W&-K4 z6m7f(&Bxy=@Kp3B+M#6WM3AH`MASwP+Urk{54 zes}>UztKfxKRsmi2Qt{ncMMiupTw`QvG~)5PXd2k`>r7Rg0$1aptrO|=8&z)SPL5Y z7UBr+$daSJ$|HzJmjXM5oi|^&=XonK95R&nSR^a}u16lj`mmP?cxnjiEXBV-=%_V*I>?fabSQ41!Dx+`70EkGp;?DBc^ai;h zSVJ1+2JM^@OnGa-eo)R^BNUC626U>w(cgqA!W8CO$72sj8#C!Y?R0lVE?Y%(0 zp17LdAnQyk$XawtN=!SI0TrG(9!Y{U$O_1c@V)ypkHs9ej;{`{@+pu(vsDO#JJP9g zLxQUZjiats4$g@S4sSiY^?Ks5BXCuYvm!%mX%TIv<{?8id@&2Kb;>dqt~@;OTn%W= z81$Ccj&Yf|dMSqm8s_I$=W#>(s~!hEbh!iZh%6UjX5z}D>%LC3PEJE=r25MfjpsAC zV|-KEzUX~{<#?g_&C1u`J$U`wlWO>6m$L+8N| zML1^GNC!mX6e`*b9v2-shrmU*qpd%)oeQ_Gp6@?fExvL6(RR0h$NaCi4XoQD3Y+Z4 z%LefEPpdSDpi2kA=KT)4Xad>yEDU%0(220x=zT)BM+vWWL|SlO3^AKzl?cicLOU~|NTN_@VC!eYW z3%Kwg+_O#2{a3UHf<5#Q;T9zU9QYuvcG zbH|UnHTN;cH$fvB4R3-GNt?Q~#LPs4Hr-m7$``|?RtCEku2C=B8RI94Ye9sUibLxY z^emHd>@gC34$#{*9ota!t^SgXYTsO;M(wg2@PfY3qjt0lBi_* zd&KE6Nn?}AdkQvTCOR)OORv)B<`(*}d{y{fL=L7zCp+8iVeh^p8~F;nL!) zQ}mKT*RM9-X>4uW@Tb>ZnSLBuGYpU&(^cUorT$Ygn_lAeY+Q7#p4CUkYExNqMTi72 zce-9x=4x;$$<4_OsSKqiHX89dCs+80(fvv@0jv20=qfcmW8U9!a8O5@NNS(A=KH1cVlP zfcUahM8Fvh+?VKa99t?0E(kAXL2pr9P*B2|uJb*VNWif}fH9AyWs>0V@L;YTsX%pR zSh0i^IaewqP=B%m+h`$2Mkg!vi6jAR%hOoJ!Dt60Hd2=)x)B#o2a9e)$FpZ7P{=dM zk(M!0^LN1rv0$NCp#JX~5WS*C8_8R9laXwd^X+tm(sj%RuV_{q9-b7gc5^ctK@dOj zl=JV4NI%(JGAtBN`Xm*ZR7CpUBE#6Lq~GD+$;4AKV{M(WPF+xtq%Gj~MnBu&s`6V) zzle5XwZ2J?!6CA!$iSq~O`CEysUrfD!O9XA8Mg&I34RkJ$J?rG^Tt}ErfU>X<1a@3gQ}xvwsvF){?VH#b zjjwOAQEWFa^RYKZJ=9zZ&3JB$oGs&^ddk zfm+Ki#L`_XN6%mwv3w0=^?y8(bYpiAE(C(_R!8R{cF-+Ta`0g8sv56_ZD0`g7f_2XS>Rrv;n&UcNv`a1iqR6 z?SSL7o6N_!JAAhoC`ilX>hg-}BkN>j$M?#4@Y~7BXg~#}GKFd=woC~03fz_9v^S8b z2EL^>7wKr3Pj+Q^l{zakB`piv7S%};4S2@0scx2Z*#YXlYg>zdGXk=WH z-GahgWm^Ka?%JUC@X9F-;9{~Ezw#)M?O=>``q-{57v=NbPL1@Tc*q*4Capa`gD2hW&<%t_^Mt%M6Za z)yGro0d%E5kcxw8sTCvuKJp5U-cjHI1TSr60&*%ME6{wTW@K{;XMm+XW)yYgsCPkf zesVz)gp*RCD2?3zk3U7gow-B0HggqCffwv6WQM57v1cuZg;chdi>(u$Lyhk!s{d9;6?zd9y1Nd$Yx;Wao` zjnto%h*axjNs=goE$$Qe3}!a%x|Z{|FI&~*FVp7c>GIVPkveS@XYU`ls={7IyEYSM zHtAu=OfjgVJ>0Y|>P=g+%eHZwDpm&hZ}PJ*UDf0#bGvaj^uBt3U0P->w`td!pq24! zwL9!H*UA)j_J)R?O={$dAsbZT{5tp9!Ec-0H#s?M+3x77UB2H@=3i1BwMSi6o>_o6 z*mz?7Z?dw2IAT;*YNfCv+sQ|Ji*oA2YoKb@*6`At|Kt~w-RrJx4PwW?=fK}ZM8*n>^i^Sn&@V*ZFO+Z~q+-J?AWOQM-nSW)`xEy$ zhJr|R|ACwBiYDL zBf-(ck1r+Lde?)Ua|{gRy)v+ znUV3A0RtNL1D9V}ZLC(eWNco`nG)LjEBC-RxzHz@&4}6sW>7fmB`cRvGfwe9m&R0* z2^ZiagojZNGEjylu!^HQU36L(j()Y4E~EdZhgI}EnFGN1IYVuF92+a8-NRdG_ZpMwxMoLO!Xj1%zxX2dW$h}p3L#B9; zo}XsO&y<~qk5^hxdZ}+-42ikH8IqaoJcwd+@9Pd3LL25NS<}^Y$MlEN%PZ11gmc@P zv-E@qw8nZ_g;a+-dM1HHbx7m4}jfjo6`o>nq%9}vYmZy z@~)PzJbyG}e{EKy^&Ngp=Ar1rzI(0dK=Orq{f;`vYHR8X|3_{}kReb#mu^vdl?K&l z_iGPi9VpwImX?;9mIiV4K~^sHtFoOu9NglU*EoVAOP87izP19ZgWEHbh}RCrw35HC zJgeJwY@OOJ*XJ!{S><#G&$oLp7$a56c(nk5cT;I1D;hp_qZQ&-!_nLpFd*Bs_Ezve2TP@ z=|B@r10uLDT|QkVbTO?_R+X1m0jUR8JUZ1UAi&2bpuFnKfM(~z>|y7%<#uXup5wb* zRf6>+lK~w5Q_{c9$-;j>$~^>)0nNaVF=7Pdr-0Wc5K9;u_f3= zBVtzs6r_vvp*QJ6laAOGjbe$45@U+dSV_^um~Nsb0o1I4HR^rWz!=Z@<(~h2p8tKW z<7TbB_Ue6o>-*lXW5{{HaFAa2Ejk z-y}#pgn^%9GI%K>&Yn%&c8bqCS$3lOsI+F`+@iTE`aV3TL4Ql%CTjPnkA_;b5``xj zr~)a^{v0s}v)Gd+90&U#;#LSCWw?XRT8|v<*TvzH{>&FxR02$c!A#uovjt@?bUC@^*#`aq*U3=of zrb{ZTqf9RL8~y4ZGKzPf1scO$`E^uEk^)yJBj|X#j+g(6?ZXHxerxf=L`K%1IG!AP zOcNWF5Re`qE%o1&4?*UU;KOyIL$JdVgOoB#BfkzbCt!Dz;YU-BMjr;&!rqcy<}Gh-*8CG>gX*|zw> zU5^WNaNb}k`SFRuKXq|@06#b6owui{)_B+L-J+4Ve0YEidX)dQRQ~JwQT=BO4VT8$ zCGOs>{O!h(JGK0U9j8w0JSRQ8Y{%SrN^%#vL5irOY!QtsJbUeDK5#?-0u^0KmXH5u=wzx%GTA^XgZ{m`j?;lX>D zm5KP*d411lcKBy|`6|8By)(S|%v`83s;w-qQ|&w$6{K;ewz^fy#9SO=`FF=(pYuzE zv@E?aAyx^|k38IYIImal=p|lf(eV=)IH^|#9W-+cT_g=#o;GEP(miiZ?i@ZfL7So7 z;J?dX<-0OugJw8cRX$!BlM#aIg3mUd@q^bToX0* zgTp6woKn@)WTw?x@LRL$;P-wRdYCZiiPLBa=*(g*VZ&NtUjIx{e@chPVNxuncwz_wv=UzH6xS zA}sFF;3WmxNwhOf-{vRHitw8VY0g=|oGb<>9(bR%bcP|DR%&Rh2j$_EmXVPLrK*{k z$~yo1Lr8p%G#8Rv(LazQD(rpCV-nA3s?w@-x(duizdII|rB=iiO1Gz{XQ!z~mr&nY zIw6Sq`Ofg775$}Io*}(`dE!It?l*(&ZxQs41-?&$6VLwkF)=&7=foZ|?CSCFj^C>! zQ+J-MKd~S9$0rGp9`x6U#w_dOb1nK3qSlwTockE`y1`&(+LgI0t)8a|u_WwvT+_BQ z!6%%kUtg$T9^>EWb9nuJCmh^nwv$b3cCD!PEOmOFhL@29QAln`c5p~=MraS0QmUOo z!aU0Ys7q{tg$eM^1ah^^j+?6JliPA$dg0t|;4hiYe zk0g}QFxOJg>J{~?oyexgfKnU1f8F7YjR8&|#m#h~n@@ZJzQc*@*TRZsqA#siCs=E*ussXGaL6GKD@6H>LzgWxXGpdMD^*?b2#zPu-il% zE6T0kUcXDZ&jDa3JHSKn1)xvL0Cn;exlNe)CHVq?DCP7v-=dc*p7qnqpY=1yMb8Q( z9WXoaE`q}x#j|Dlk)n>vl8$Bi5gp46BSgCbw?XgbvtUuFUxAO0(kIzB&X4zY znLdwNL`vy95^}Z>9Q-*ylVm;MJFFZ@gyDjM^c@9Mg&8(CA_R?2y5K1K75_8Pwo0+N9&Fq=IMl9oi&Q}{(kG%2Q(bz0d*!% zcwc*T-=SkX3w3P2-v(fy0Ta(*Lx3*{l{$24M-GAs9i-vtBHBeliKt0Fcbb(o2dN9hj&RgZXDIy?Jvu_(t=&VY2l)P|(61$=>dKQ4lNzhs|6nwk_o(|rt2ucY~ z4(8X)n;PV%!h+fZoArf{_C0F;MiVtVZq`gC9dd018QpYNSJcGk>|m%4O|>DO8pFJf z0SfokZ_S*!`m@WQp8V|k^^vKsEhG!uR&_9m;FI$7V)GrKd;o2`g44 zdO`kt=~u+*$GS)L-)g?R`A73pmD~nZvl{9(-=+&RsGw$uj0PxvjUqj#UEy~I`P6Sz zg>H?HjM0RWzH^|H&HRxxzo4kFNLjhQDkhKD6&*fQs)TB|^c?=M&(fM@DvzaM>!3m? zV(a#;D$HNv28v%Q-(gakp_YY4tU4(`)N$z%Hc@WBdh9@Pi_ z((Em)uG`N5tsqfiKL(Vyaz=f_PiLgTfjox+rNC}Vp?8PyMl7S)8DHfm^M1Dq(*>JSz`0-nXF7O8 zY^5w+TjKolu&?^uad9GJ7AjKChn?|1w)|7CE1s7&o?Lgr`((|P@n=>p!(GW1#|3Zo z*}mwS&&jMyM^1ujlID2)@cZ>pBsE!l`O`qJ;~LD!vqka<{jUZcFrXb!8kDNVM@F%Q zbfgkj99N)Y?xY@^0dLQV@L8%kymU_W+c*k~>9onXhn7N@onhiQ*|V_{!~#ZxPBAnG zHxO$m-I_OvO#Id9r<9+LU%2sk`DbTNe0sn1&WDG8km_fOQR1=SshBS#>wAgTk@b)* z>J%$#Fp^hqu_JUgW!Rs3ESc<6Goyi}^7Nu7gm%V%5vAC={r%ZciArZKO7%7sj zxBX_{zT;RNn;sFHFnK;TbHxT*WV}UWT>{9~ z>;~~dhlN607LgOHowa0;8`Rc_q~4wbhtE*q_6*3KprOqe`0Kl#8XTg`hI~G&IkseL zx;AFxJC0i1AeCuzf}I6_O}2uy#zV?+JFp2h7t;)p z;jVsy;w@0jGU%E!^lMR_RZrnaED$GwSD^$vx z+g-D1lIU4uM~h-4SR@b7sn-nNqK<0AdIiMbrepxiC5lWCJu3lWcBbARSDoXlz?}jS z{tpzhPZtnwdrn4fdbSgFd64}Cw52{G^2RU)4z9{-TpG;+WI5epa8l%^Lse-GSxkmG zW^V@pLzz=|kc4LxWHNN`Y??t-j`AvO=(3=K6z4w2bZiOJmFd)c{0HgTsafe6PPFIL zRAMb+sX-yE-FHOxi3nmyxw*;+{d!SOIx@j9Z-$AmF$8CiVFp#DW~8TXPjPx^*q9Sf zq~puuo#ZvcR;8wAKs%??E!>kOd^5d7>m+ZUw=tc0O>@c%IZLzhQXxi?>IlH*tei|~ zcJ}t|*%~PPjuYi%Z%59P$++Jq6*O2y6S!gvl-+3_))$W zNDkzjV&L1;C-a6D@#ME}{y}D(09?aN&E^YVc-&Rp{o=v_==Yv^f_hSPh^hKt6wrui ziSgZ+nNY3V7lgPjvoB}}K+xkmYz#*hsc}>B5Lgl(i`7HKxQ4eUOEHB=Dr3tczg1V3 zLAb=q831uzO!AD+fvF&}=q&AoIu92XaaRH?LWsQ~Vk88UCCGcxAjO8aW_!7+TxXv- z`j#dYI_(2!EbTqMdE9;A$&2qde}9h*2p|!3v8Drv_)M`tMa+((?I(fo;E5EE=|LZNwH( zPq6f(wwlgShJ0|=8Cv$q7#p0sgp>*+qN5{t!xeEvba}Pr14(sxc{Q)UBCalvj?gTY zkUXJ$5(@#e*L&fnP&&e}`g(P^`GX(qp?E4&LiO+s6!?i`y^JxcVFAMx)(@y@R^v;7 z@d}Mk#?p`x-T>_#%?B=j%WIly+FNJ#EZ5M{-mC;;FV4NG0oMM_i9Dls%>AEm+P0mwR#{94FO*>n4HHDg4c zs~+-9_YlHFL+BI9PSy@+3^8jAG!Eu1IG73t=TE_FBm++mN}yw6wU3FX0(cG@8VNa@ z5*00h0FDBho-~?WWd4^}-KW$^hx|z7^N2Ikpeq05;g1?JCG1N&X&0R@rD+}W74b4X zq)EUg!Nf6)(zuCWpzaR_>SVo(etQ%ZoIwKNCx@F3Cg7Gk1R0kmU&=b<%4}+G_|Xf0j)13&!pSbR9Nkb!5MSjNAae zv{C%ZY-RXf&!1^>;qJgM%;4)LB z$oe(1Ki0fRHUv3;`0pK-<#i&v;?=QShA~?a>q}oj1I%WeBOUqm>peo}spfg?Jhom# z9XGSQO*^yTBaMEF_@gr)wHWic1<9`uUT87*XsBIwuhOAi-8JB)WB6AtUYf_7Z<2ckLy- z-;n^J{cx&UHGr3|0HJvBeY#jBccoTC*DqV3IXhS+uPCYCoeSL!eOhqKW_1Y+Ch_an zq~ZwF36oRrHqL<;D$Nw=iqj} zBKn=?5LHSV5U@jzEnlS!h}i1y760U53Li?Gx3p5tXVUUb>q>o8@mtcP5{i=x(=?UZ z-M+<<(klP_;Ee!ENdj~|M!hRmMkN`(7*&yxSC^Ql(&_Swixame=4gD&!Ya4!m-;m& zHGK>+zWYw%bZ+yGGNmpjOLy=+kDxMMw{3gM)-CA)Ta;_6Hl5ymwEO^HA5*tenUj^B zQ&zt@p@84Hv3U7v3b@XhTa<}A5({-jd3l9=^X{vk9y}{ObF&JFc^y7m6g8Q(nKgV2 z30VX+SV}TmdfIm=v3g4t5*!rb)3mBCRC9Cc>A9yyNL%QjY7nI-D5=*1pzqtzk^Gj8 z*iD%EDYw=K*Zcyp_hmPZ^S_WGr*Y1ku7va-E>B6MLc4rR{JJ^{g=_$o>??|oPe=$; zm6L5Ea$BY!qvtBi!*!w2PKF}Tg@Uhp?Z`a%QJquA6Y~AB9Sxyz^PKc6XhXM%!)$dY z#?f<4AK7em2W-!bHa%3-Yhj5jNGz43=}e!*U)L-&VTexRtAsH~SrqL>J+zcQ!QtEu@9w0{+~Tjum|ICc1# zx~Ry0$n-*655#}n)z>Zst$vT6N}WpRwB?6DI`r&Jv}@u?GqWyds-MU^*S7eI;SQpxR`O|6jnVA$%< zJ@ijv)p8qq!R5y?xfJvof0T_OwL5G=X#g6|-i1cPTq@{nG3XZIEauz=c*o0yW`aZe z+67o}yuXW5%Day*vCs)Z;$Nc=PqLlo##~oAh6S7iLpozy^ z5FYMvVybR#h|`%BZ|{3k1th~~3@cnH7&3}&hQ_O(+k>x&&Gu{^iY$w*WLs(8{qjpU zz;gnkTzg7AL^c$>K4!o{XSoK0o(yUgG5tDpFsxNOws3DHj}$;#F*}H3vV@v#qN=wF z-YR;V-_du6bA3PQw90EypQ%2(R?$+asc+ly*N(^1qALZTeWuhO)w?S6a|{ylmtj#L zZ+I<~UZFR(8D5K`zX8ANENPblG9VO)3o=%D=-vVwQ3u8kMmsJ?o*Yu+8#?JoNWZZ4zmrJ^ zdf?Pd_5s6;t^RD!%1#q^F|~l-OD6vd9i8b=kjOg?ED|&^4#yfCq2Txo1Q=b%6GZjg z12H`@Jdw!%T8tOA16q!azTUXIN228Wj!yDD69p?Fn-y_!5m|AikSB_D#L+0W>y_Q) z_m3;hsxB>cVyq|Zv*{IIN=q@&aQ@or-6D#N;FWC!&r%V*S{clY1SuFsnh08%;-)KWNT*e;ols z+-vV2yb?Yz*F20}Byqb&}{B9jteD6c~o(?x4hIgJ)d^~$}XwbpHgXcdv z;3G9S(@aHCQC3AlkyI`gXtl*rSqWNgLRM69LXoy2tGHN7CQbz-W7h8Ia_^&#QRP8d z(b2xXj?q!z0*ZoK;|{lXy(^-2XO&ktH8gv^w#aR_v#Fy&UoPhWc9pWp}7AI6> z6%|1r_V0?5_vV~k(>U|W%ssDa<+qgaYqp0Z3<#AT&8~^eQig6^wqjB6gbkrzooFg5DJm)|OesjyWul-` zb?9RZlzweTrCB)Zx!-Q!%gT0E=LxEM@pwzp*=q*G#(QeLnS#cSjS8d!*mHS8gBqI*|zDzUdc7g-Ns4 zEn4g^%_{YYU4_jRP|L!kS!)W`Zs8x*om+W!Y~`kJGZGg{ zsZfCPSbyWGElCd(r#6^+m>Mf^e_M87ym!1!EX^R;SY@H#(M$A}qCUHq`ws|wi_YO45sJh4b*p)LNpdPP`QTwCx&FPPI(K(ac^Mx=k3`*;T#TSvy7ApNhMsZGC_ay;q$ z#`LuTkW2ZVCK}$Z1{#3FCeng?U02Ylra+VDmhHQW?+wjGJT|95uY8Lyx>|O=rcsI! zq#q0)EhDA7CK#S-CYTJkoFN>!DL) z=8o$-m)ZnU^_ppGhbB@hX;!*Fxcq3}N;>J6Eai~}#P`ilFk}i0eISOW;#b~CDnU1; zP9&|4%m#;7W{!%IM@XeqZ>y@`xjlQQ=3>f)+;f$CbbBgxRYFC?802o+&!oEcO7We7 zYYbCoI{`n`Cl`Jyg|x;9vm?hIp6DeE23!GTUergQMSMD*Y@+6yr=(L!&~sHUAq6bi z;f^^{nxtQ%AcyHTkU0+Fw~a>8!vIu)368o$pxZ`42!$MjlxX@zFCtuf*-+9^->Wm% zkWGGh{yiPvd9Rn~9OUHn&(2Ec(g%ttdY{$;-fH(79e2wDdkJqoE8QhcTUU#-61hGW zTZZT;`U~jz_PE!9JkUS?wYzL2@!QMy9|5faf{sFHdvUIj$!nZ%%H%f8Hjvqb%qC+t zGiEcdflaUmHn$^ZqQ!{?$vWsL5qGv=(=$f)tmQJ>9k|LmTBfocbTUa%%e6Ka)ba&3 zJJsc9Bs;;0EzFY1otc~czq?79o9N%&%$b|nf`1Du$b*}}3 z2(g_IO+TIMNOyuN#hy>+ig23E%2jCJDH-?L96J{?`X{ zoX7@n0?^MSNN;36(j0V$TCLkN+35lhrsq8ksN9ec>F*R7P`rL$6q)DjNGER+#kdty z;g>4p2`s_n(@RjGJPPTJqMu%xP#!{Uzm0MtlQ+?M&H+){^_2lml>tY!`zp!2r;Z*_ z_6(Wkb-V9?OSl=O8)-}#IaoaB(Z4QSc0w=49l$1|NH6{(#~0imeYf~iC+M6^G?oYD zYNO4&T`}bbe(l5nmFD%{7kRX}a-UP>KJBr93OesEN5J@iEWNUqFqy2xn0R0R7`^T$ zz=4zKwJLhE3Reh~m87K-$gl^{%Gb7$8{2RdQW;5Gq~uoTI0gNFHT_{V{u+dyP}$NH zX0VK-A>UDdG6pPPf6_l4$@eF_{_8E805;Q9tCyCMka4(f83V4sHqvT@(DLYsn|9GTvEfuFu0$N@MRE~T8V7Pw zbj(B1k0z6(e(g}O(6~Y|3Bq`bCfy~AMCAR|3d3~z1bfiw%*57nI-9~wCUZysb|9at z$s0hQ1gfB}HHJ*kKPG{1>c~{$c$LWRkr80@9acheT!3)j=MP4dn?}X~H$+|?(+h%t z7Zhc~=&XkI)$Rv2w3Oc}eIKh^P~JglLvCb_Ru!{dn;a7!7lFIA^Kl{TTzi+6e4VrN zH?k@BP)>DPZA5WIQD}5>d_oj1lOM+hOG8$L#BRtKnL6vMeZQ6-|B+lj_4U5@ziqr2 zvM=uV){>Mxar+udiuUiWDm#%Z-J4bsQM{ zu+Wt_eo*|T^tn6rSEN-(lx$1emKGn8yDc}OD!vL>s5aW_+>$C_*y*q0kQ`IzpC1+- z9-ZR9Bdk1Ze@b0>ZF&Cw=sM}M3MfU`c{uTmZ@uqMuf$Lv;1Dct2yF;CquY5{YODv@ zvxy2s7ktFCXk)NXaN@H1jqF4H#-_w0^+$H;&V?M2LbDeU>RVaG5$PZ6$Rg@;vI+>o zDUf{8zD}2cqzFF7F;H_pH@H9b{ew<`jzJ-qH^+WYPm)OQ>_rue4tYL+K-@e(qJEH@ zo0o%oFk6h)m7g3Z6R&4nulnQ!3MFJaKjH;IQ|WVk$3R8o?v44ukwM#1HdY2z1|3P+ zRk^z=|41a%Bq1YXfM1YS7hV>g8lD;(o*SMQRvTNJSDRN>n_3GcgmuqnD^hm_R|Ka9 zr$hzk2jvCtirSUGE3aZ#%5Leip`Er0`Mee3M^=>hg!_cYd)02N@i`rTxb{eG@tLjA zB^w9c?zHM{sQ3t0@u>Q$xa!=hywa-FYAIbzQWO#U))j8q8n88aU3EZpKx6X0>b*4u zjS>5>l>L`q&~CsZ?S|?s5Og@U7WC+0{M!@iZh&$5P|+Yadt@#!6Z90Q1V;qTW=>{( z%?6kaF&kkv+RW9=&1{C*+h+64)|>g5Z8i%ui!zHhOEOC{%Qf3&_MzD&vm0ign>{f5 z!>rwWn)yugx6S97FEaNuUuEuZ9%-ItUTEH6e$4!&`8o3s%s)22W`4{3OY`r|e>MNz zyxm-H!C6>a*jqSRs4a$DOtfgW_|oD#i(f4Muy|_GVew2T6iS3v!v4bH!imDyg;Rwy zg>!`qh0BHOgd2qc!cbv^Fk09wyej-f_)ugaau6v+ylA3mn&@rOJkcVNr)ZTZT$Ccp z5`84PCi+5jPb?M>6Gw@Y#M$B^agBJFc)z$o+$g>+ejxrs{8-{DnJZZ$@sg~S_(%dJ zp_2C`7bG7`u1H!WMDjw~M><+MQR*h0A)O~(B@L2plg3F;OYd3QTPiJ`Etgs@w_I(R zZCPYlVR_B+Tgx`f=Q0bKrOZlZD|3{MkWG=zlm*JtW#zI%vPRi^vL@MYvUXVqXU0i5 zp6kyI<=i-LE|iPr;<*$qlgr@>xE)+Aw~sr_o#ejeTDeZ{c@Og*c0FF}q3Yq>V_1(# zJ=}XN>9M|tPY?ed;XPt{B=$(_vA4&^J?{2+-qWI|rss&B^LsAsxxD9^o|}3G_6+YC z-E&9J6Foog`K0GFE1A`6Rw}FhR@1H4S%q4~S>;;ktV*q_t?I4zTD@m=-s+mwEvwsB z_pE-ldT8~h)njXswcL7`^(gBJ)>Eu!Si4)#xAw3Ouuiouw%%=h$oiD^dFzj?FI!)? zZn3^&{j2pK)}1y|n;tf{HcA_3n?W|iZN}TU+Dx}uXya+K#U|7y!=~Eipv`+W=WQ<9 zT($Ya=AO+jHox1n+5BZgZEbA(*-o-`vt45AXB%ysZCho#)AoSvVcSOA)3)brKe7GV z_K|J7?O(WRd|@ZHSmU7TH>U8!A_-5$Gl?M~WV zu>08Viro#nAM7655jlpuTqAdp50np+kCso9&z3I$G_{X>vpifLEsvL{$TQ{n@?v?F ze7F3d{FwZ-{G9xv{IdLp{7d;a^6%xp$e-E^?R(hU+V`?|u^(zb+J3720{eIDm)ozl z-(VkNA7LMBpJrcVztjGJeWU$*_UG*{+F!B1VSn5HJNw`4+w40PW(u)_Q#dL#iXn;# ziW!ReiX{p!#X5zbVv8b75vhn%BrEb16^gxzgNmbyCdDPi=Zd?EpA`=kkFl7UIaoSa zJIEcJ95fCt4uc$qJB)Fd;P9ryJO@vQ)eajR0v)0pQXKLeN*yX4>Kyhs9CUd1hD;A_ zolH?DZ}q0ko$0D~->kkIBI6{l2YODMto%Qx^x~c!lwP-gqx1p{`@c|n-TphJm(h0r zru619N-uU?kZFcw^E7~$gbl)|Ss)`va4`g`9`2O}%O3hM-jJ(mu|W(5j~ZNrI`Ft2 zWwh!VgIGBP*H^KT8h27JyDS+lDV>i3UQ;Aer&z&At2L zO=6^bUKUrDp&Z0RI8V(1w3181{4GgSqt(>L{P3WaGbt_&u@469rG%S_WF%9OgqO^e z$r&=h2tI339Ev>{R>#waGKuxR3IGCwdP|X6F;|#gm7?6X-zE=E^wnFd4T3 zRU}E0ae3+zS+$yD$iJK@1&m2a%B0-H{1l!WgT)SAGiE%~gp>kJb8(hK+k=sO{KDZlhYmtwtU8QFFs&!_^!XDr1R3 zc<01#s<|K(wCh&TW1x(Kz*-8bXPEl3m|J>cO*8l7o43$*-S>vTr-;Sy8y z#eh;3N1sC92LKeANdQgs6bD2vHOC;T@axSn{ZbmPOC4jNdO0dzV8LBpjBYSW&E3aU z!VVcXQf7saV87r}@_Emuchm;d_AD8z^Cjx0rXm@)lF=-D)LewDmqdVDpxH7`u>>;& zdi9t$-yFj&lew>y4dKL7P~SEn&Js^pO4Q^Yn(8vL!w`Oa)m%-!IvqU}DNByZIL2?{ zfgQVth2EpHWtO`0yrD%w($vpZcdQbfTQ>OEbd_OjtIRM~GX2=#bDn(1>St?2VRhs+ zbse-_#p|`?9b^NLW4H#D0E^3xy}hDan0U*KY9efSj_B%sRu`!xh}tc65UZ5UWf$H3kd@)B1zOeOj}+vqk)aY!c4P z5}?&`Swu$VkEmO{loY6$j?~zkxV(7WJ8S^Q{6^}bG(>=H zCJg)@wtQ$ocu52hqBqJi1y1{8BFTJNn%$XriX#C2Hsh z{EoR@l5s41OV^xeZa$&6ldW0Gb5B#%=mMlS2dyHG09IK?Ej26Xl1fugpG`me3hF5oWJi0U@2NL;O=KMF zK5oPpvk~T9E-Ge61=`x46so!UkYic(^-i2(4@RCI%}?X#e*9n>#;#eNleb2*D1VLj z#5YGQ>c7@$*L(FBs&4Ln=s30s=tsW~z??fsN%rHs8K)o1ciJ0t3T_GJMEypL&7taW z8P|K6D%ZmNNX;D}u`;lcK=Qahwbnqs2~vD)3bEkG0QKGmj-RuUsx!Uk zNfRYe*^%3$_}13SRu!m-&f&SFkLJ*JQ8p$!ow6dmBBPvtyN}uh-?>gl1XZAKPFc$H8nFmRbvPPxK~0d6Gz0} zBvJ<9pPW2i9|pXkqPzmgI)c%Mq{uiQuyX-=lk5HcxJt}I`ukv1jlq528)Bd)SwZM` z#=Vx5^ctS7hg@!^XmI4J*&5JkBP9VeMnt^~_c^F|)j2G|RsdpxV=zJIB#+z-DJn|W~c$4yYy({+$-H>epg<|ZW zFacvWe;t)0d=t|>o!9}{d@&dU=H4B5>BG{}!lFEYot22Pqs0lCadAozYbH~%-cQ2a zm9gIPj+z^bySi-{By8Ho0(oQMhckF?m+aebzn$=(e>u_!od!Y~SC~fpFr_;J_$~pQ z5#k@!nBE=5Ef~yaiDeEjZ}PW0ksIQ?OkGM&+8Ju;s1Mt`NKG$^XOPJv<6NYnEw128 z!p>nFXrI8^=D>$$#XxpEIMQEc!HMgz1=*?Q&d7}S*W4I2mMIk09%}>}b~-X2f0+tx zR9C&OV&`tw1I-aij64IR2dNZiq6&uVT+fhwdy}?@zcD?gRS5TnS6(lFRUU~Zt zGr1{hC|3h`TLCB8hxv3jN`Nj2MR4}m5racd&4tPII_`2TR%=j9ImQ`vjzNH&Ll)WH z1-sOJ-hxYArrYwF?q~QWU^~}I*jAW0sIi;kx}m(gkhr;8ETps%TQQKcfeua&b8)4( zppD}ylFQ>uxSJO*-sB{DHR&lT%hQ#VL4UNQD77dlpHIryW+$dYafZ~9BVO36iev>k z4Yb^{Qt=PPtU$mR2R0eDb4;ThHYq5Hha{>jrc!T(T?UPvE{aV}jE@Ckr6eIQp)iF{ z%g+Z+5k$VBQX6S6n$F>DU^SH5`D^+Z#)|^Q)COv%Y%piKs2_4*!Ux;SVKwfrF`e3T zB}LmI|DK<_Jy(@3(I%#*CM6`rI~hcVU7}I?ZzLR5PM3WnI+yb|?%3$yB}Zp;JX1*%x5s>9go16*%wbicZy09WXv?wq&avK*{Qjt=w>Vlf#O4VlEB6Sz1D)u;%-Sgin zfpm!(^;yP{)rrqCuuYl~pL5VQi&c4J6i8<_bcG6{JucWTRN$WWHApM_lc|U|A}c=L zY30iJ_^gPMI46!WR?g35dWRkBiJBjMXR}4vL??ZY77FL zEW*?ZV?Wdp9Ep6@sIwL96F0Vwqt=I=~*i~WsL39t`4h`JK%HrzPH$Gg5=^T`Ru3S@_KL-#SE+k}qR!BXk94+Ip z$;)Dm=)ox#du(`n=*mxSeSY%djjykcoyZ&h;@0vZ5fNJ>L!OLqEG{i6D=n7R)N=!; zPwVH>GPRYz|LN83s)E9z+@egbpA0;)+)>)5f4=56U#$%Xj7%8l^I8qJ9)jxkA^z8J zl*xe^#r!x)aCz9y1U|h$mr? zudY3Zy}d81x>tT#aF+a!l^d8~SX(~75;$H%F3~FrZAM~}R>gT#dK_G>0c@*IH0R7$ z8@^U?CwvdBUF++&W^IG-@#75*$9Xo+**e6Hz$OyRZYU{Bj$`|NOyR7>?a7xiY%Cc# z75mGPN3y+~-WGot-Gxi2#4UuXx+=G*5=S)>##x-gWj{8ioCzL~+){I{lc@P}YNdjL zck{D%CKSJah1mbDoZQl zK1Cm3jQ(z17W7baObWydUGun__0LYQ3}Uz32<He($3v zuqxuBQljJIdE+6Q=f?2QTErZ6Auil>fbVj~t|Rf=9dw8%0`Z~UyANr&9Z(SzkJ*9C8)Y3j&GGH&Bs>flCYs!aj; zrNJ5wcs#W`R9}h<^OKS?LCiwm#ex5l%u0`q3x^e1%&C@zZ42dk4bWSYyVH{Qxw(&%*v3;EmJp|@{S?_V*Kjj!&D*JJ8Gxj72wQlWCta%X47wF!J{zWT09y_I4KB73FXiH*hq|3)A}L ztd~D-Jd(S2FN@lbS8=K=1}`o=bK+|acLWmw*i`w;824fmm8Y}X3`(=+;7+>`0~cCd zqG}U&?@@9fV+*7L0m}z!15*VXqZ`b zE(sg<6!^ua2gi}8+##S=abQ7cz{;AK%+dY<5H~TWBS3=cN87{bE@fOc2a(cYkRz=i zJvefcwGxy#^Bi4)?$`&wKpvd17adFsdkMb~bK-`**qd%C@I@7cp_aosTQFMb3n0}W zRdbNhVq+b3#E$Ts0f##d(olUl0sff@>;x9f^75ZlAYt|wF9foeHp`bb3$d?Ro$MVkC`!#y>{y&H`tn$#R3otWWp1 zUU-8qybH|4Mju^&SjfLazx?nIPA|XxzqH7DSc=3)CDLR6w-Xhbbt1}bs7sMxg1}j@ zPtYJ}6nrH3s&}70e4jO~R;_&Nl-7Bzt6Dd<`n7Ipjcd(mt!iy(J=%J;_1o4zTA#OB zwef8O+6J}_Z=2FKuWeP^mbSRIoVKdAhPHEUSKGdA`=jl7yHz{iKBawL`>OUW?Q!in z?N#j!?dRIBwtw6H$5Ylf1W0-Bf21sEwQ23$>ejlTbxo^J>!#MAR&8ruYfbBs*5=mh zt>3k_wh7v7+MJQ{ptg~1Zfy(N*0cq+Y1{JJYTAypHMd=F`>w6EUC?gR-n-qceL?%0 z_MmocdtQ4@`;qqM_UrB6v6NqYkG{F$#lja;UyS_r{Kj~{{ciop`l0m$>)&vJcHjCJ>z}QEvi{Nf z2kY;xzq7t)eb@RM>#uRScH8o2Xpu>KrZZMUp%a*f8Gw)MX><*NVk?f>5=v7iS= z04HD<#~5~Im%r>6^Vw=^*QWvt<3JT$p6@!6CDAg<_q`V{p1-g(6EmL{2+{QqZ(U=~ zlGPu+|L3?dZ?w<~g3OxXPb=6e(jpmwU^R>VpC0zT+kGV)kO*UXH`>`dCJ2E9=BwWj zCK6${FgN4F{NQ16usGqSG{(o=wSv(mKPId6qbu&7rf|&7RBmQBy_?cDg@L);_-MQGZTt>9>d%e&!BS@| zAB&g08y{_Vxw^kunBHMBe?pkdUw0n=&188pK7W57%KDbcFKZ7|U3I7DhQ9iu+ujwI zDeQlmT7iQ3GnM<_@(lOxwzlauH=5#vf1xq`?)bXht(j@c7wScYcjV>o`mpSdll1}i zm}>=Yc#Q3Da%1Mpc)IKZyW=;yTfo2Zd$(!w&+=%h3sZUE&&}k<^1#@d)7OmB(0afuINbCe(I) zV{T^McIFq~#xaw*v$T!r!+bTK|FoO@!5n6hh%l%amLHZ5%n2|3YXutQSp#?D19y$_ z(RP)k+n>rjrnO`s}--{Qf`0zdj-yKcw-Ql|Znfx0~w!zqd?@PM#J($IXcPY%i zEZ_h1z^@g1Ol|+4@tg8wGTC=#XOF2am>qfKn907Io>$+Q-Sqy_u7zJb-R}@W`8!UQ zcf@Io%VaV)??c4o52#O#V%#1nXgU+|F>@jCcpKZ_J&A z@3MF03-+%5t`!Vm@tMZ>tLZTRq8EaGtY0v9QyVgOxLGr^J1@q*V@d<={Y-i7cC%-3 zywbm3mfe^J;$ivj&b!(ametFDK5R`erNd12{AYbi%)83U;>Nr+5`MbsN-G#{3WIoD znEk*1TOcrh-{|8tGo`?++wTaNU3N3C@eIPM{E6?6zA8c)@KO^scH4!o_z?+Q%*wmn#jm(a1a)TTyWOP%NAtDac1wZ1xhWn_FxWi1+ucgwYJT#~ zK%Cb7e0;;4r?1`W?L2GkmJN~4qeqVV*Kp^l{{GI!Pod5s-l5(hTfH|7pBcC%Y-)se zXkdW%%=z;?=1iS7X}-tI8Os*TU*xgWJ0#REaEtTU;p2yoG{&*O-+OJSH$rdp4si|( zbPn_NcK$oTQ1A6&%>Twfe8iWHh}$_VWbFp;fVCl;o!5qih4`%tH+tC;80NR$I~2)> zggJMo|95_U!@`0ljTphgukFg)aKFHRbQ}R(I`1u^-XjEW3IYW|f=EG#z)#>K@D+p! zoCVVbYXw^c-muMrZHr(7zB>y>3q}e?3H~J*4*OJrKYq@ygbFpjc?&`jF2opm1ANXz z>{}4$R6zvXL-7^>a}gdNK{#Sq3%@f3^9Az+9)daWH4PnaKI}6EGX%>73t(S_x2487 zLyxYu^5reqXbk0y)C1uXhO)6Q|5RQUW<7kE;@^l6 zA+LmC@2nIomJp<|0saGwdEX4TwQyzbeu8x<)8DadK`8dN9==1n>mmd$toB~5jen|b s)(&B4mq{38BT$mA^w<7dxZ%e9{-66Cfg0+{%@$)VvB8fK@L&J^FN3;7EdT%j literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/fontawesome/LICENSE b/xhiveframework/public/css/fonts/fontawesome/LICENSE new file mode 100644 index 0000000..3042281 --- /dev/null +++ b/xhiveframework/public/css/fonts/fontawesome/LICENSE @@ -0,0 +1,4 @@ +The Font Awesome font is licensed under the SIL OFL 1.1: +http://scripts.sil.org/OFL +Font Awesome CSS, LESS, and Sass files are licensed under the MIT License: +https://opensource.org/licenses/mit-license.html diff --git a/xhiveframework/public/css/fonts/fontawesome/font-awesome.min.css b/xhiveframework/public/css/fonts/fontawesome/font-awesome.min.css new file mode 100644 index 0000000..cdd76eb --- /dev/null +++ b/xhiveframework/public/css/fonts/fontawesome/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.eot?v=4.7.0');src:url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('/assets/xhiveframework/css/fonts/fontawesome/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.eot b/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..e9f60ca953f93e35eab4108bd414bc02ddcf3928 GIT binary patch literal 165742 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 G*yFsbY~cLNXLP73aA74Mq6M9f^&YV`isWW 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

      5q!RY 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 + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.ttf b/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..35acda2fa1196aad98c2adf4378a7611dd713aa3 GIT binary patch literal 165548 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 z zq1_(l4SX+ekaM;bY|YgEqv2RAEE}e-Im8<@oEZ?Z81Y?3(z-@nRbq?!xD9Hyn|7Gx z-NUw`yOor_DJLC1aqkf2(!i=2$ULNfg|s8bV^xB!_rY+bHA;KsWR@aB=!7n&LJq(} z!pqD3Wkvo-Goy zx1edGgnc}u5V8cw&nvWyWU+wXqwinB#x7(uc>H44lXZQkk*w_q#i2O!s_A?a*?`Rx zoZW6Qtj)L1T^4kDeD7;%G5dS816OPqAqPx~(_-jZ`bo-MR_kd&sJv{A^ zs@18qv!kD;U z5Evv$C*bD~m z+x@>Oo>;7%QCxfp-rOkNgx4j-(o*e5`6lW^X^{qpQo~SMWD`Gxyv6)+k)c@o6j`Yd z8c&XSiYbcmoCKe+82}>^CPM+?p@o&i(J*j0zsk}!P?!W%T5`ppk%)?&GxA`%4>0VX zKu?YB6Z)hFtj@u-icb&t5A1}BX!;~SqG5ARpVB>FEWPLW+C+QOf~G-Jj0r`0D6|0w zQUs5sE6PYc)!HWi))NeRvSZB3kWIW|R^A%RfamB2jCbVX(Fn>y%#b1W%}W%qc)XVrwuvM!>Qur!Ooy2`n@?qMe3$`F2vx z9<=L}wP7@diWhCYTD?x)LZ>F6F?z8naL18P%1T9&P_d4p;u=(XW1LO3-< z`{|5@&Y=}7sx3t1Zs zr9ZBmp}YpHLq7lwu?CXL8$Q65$Q29AlDCBJSxu5;p0({^4skD z+4se#9)xg8qnEh|WnPdgQ&+te7@`9WlzAwMit$Julp+d80n+VM1JxwqS5H6*MPKA` zlJ*Z77B;K~;4JkO5eq(@D}tezez*w6g3ZSn?J1d9Z~&MKbf=b6F9;8H22TxRl%y1r z<-6(lJiLAw>r^-=F-AIEd1y|Aq2MggNo&>7Ln)S~iAF1;-4`A*9KlL*vleLO3vhEd(@RsIWp~O@>N4p91SI zb~+*jP?8B~MwmI0W$>ksF8DC*2y8K0o#te?D$z8nrfK{|B1L^TR5hlugr|o=-;>Yn zmL6Yt=NZ2%cAsysPA)D^gkz2Vvh|Z9RJdoH$L$+6a^|>UO=3fBBH0UidA&_JQz9K~ zuo1Z_(cB7CiQ}4loOL3DsdC<+wYysw@&UMl21+LY-(z=6j8fu5%ZQg-z6Bor^M}LX z9hxH}aVC%rodtoGcTh)zEd=yDfCu5mE)qIjw~K+zwn&5c!L-N+E=kwxVEewN#vvx2WGCf^;C9^mmTlYc*kz$NUdQ=gDzLmf z!LXG7{N$Mi3n}?5L&f9TlCzzrgGR*6>MhWBR=lS)qP$&OMAQ2 z`$23{zM%a@9EPdjV|Y1zVVGf?mINO)i-q6;_Ev|n_JQ^Zy&BnUgV>NbY9xba1DlY@ zrg$_Kn?+^_+4V4^xS94tX2oLKAEiuU0<2S#v$WSDt0P^A+d-+M?XlR**u_Xdre&aY zNi~zJk9aLQUqaFZxCNRmu*wnxB_u*M6V0xVCtBhtpGUK)#Dob6DWm-n^~Vy)m~?Yg zO0^+v~`x6Vqtjl4I5;=^o2jyOb~m+ER;lNwO$iN ziH4vk>E`OTRx~v#B|ifef|ceH)%hgqOy|#f=Q|VlN6i{!0CRndN~x8wS6Ppqq7NSH zO5hX{k5T{4ib@&8t)u=V9nY+2RC^75jU%TRix}FDTB%>t;5jpNRv;(KB|%{AI7Jc= zd%t9-AjNUAs?8m40SLOhrjbC_yZoznU$(rnT2);Rr`2e6$k!zwlz!d|sZ3%x@$Nw? zVn?i%t!J+9SF@^ zO&TGun2&?VIygfH5ePk|!e&G3Zm-GUP(imiWzZu$9JU)Wot`}*RHV<-)vUhc6J6{w&PQIaSZ_N<(d>`C$yo#Ly&0Sr5gCkDY(4f@fY5!fLe57sH54#FF4 zg&hda`KjtJ8cTzz;DwFa#{$!}j~g$9zqFBC@To^}i#`b~xhU;p{x{^f1krbEFNqV^ zEq5c!C5XT0o_q{%p&0F@!I;9ejbs#P4q?R!i$?vl3~|GSyq4@q#3=wgsz+zkrIB<< z=HMWEBz?z??GvvT54YsDSnRLcEf!n>^0eKf4(CIT{qs4y$7_4e=JoIkq%~H9$z-r* zZ?`xgwL+DNAJE`VB;S+w#NvBT{3;}{CD&@Ig*Ka2Acx)2Qx zL)V#$n@%vf1Zzms4Th~fS|(DKDT`?BKfX3tkCBvKZLg^hUh|_Gz8?%#d(ANnY`5U1 zo;qjq=5tn!OQ*-JqA&iG-Tg#6Ka|O64eceRrSgggD%%QBX$t=6?hPEK2|lL1{?|>I^Toc>rQU7a_`RSM^EPVl{_&OG-P;|z0?v{3o#pkl zC6Y;&J7;#5N#+H2J-4RqiSK^rj<_Z6t%?`N$A_FUESt{TcayIew5oWi=jxT*aPIP6 z?MG`?k5p%-x>D73irru{R?lu7<54DCT9Q}%=4%@wZij4+M=fzzz`SJ3I%*#AikLUh zn>k=5%IKUP4TrvZ!A{&Oh;BR}6r3t3cpzS(&|cEe&e{MQby|1#X`?17e9?|=i`sPG zL|OOsh`j@PD4sc6&Y3rT`r?-EH0QPR*IobE@_fkB8*(886ZkjkcO{K8Sz$H`^D-8P zjKG9G9A`O!>|!ivAeteRVIcyIGa#O<6I$^O7}9&*8mHd@Gw!WDU*@;*L;SYvlV#p( zzFSsPw&^UdyxO}%i)W8$@f}|84*mz&i2q@SlzMOd%B!BHOJ<(FYUTR(Ui$DuX>?85 zcdzl5m3hzFr2S@c_20C2x&N)|$<=RhzxI!}NN+yS16X^(_mtqY)g*Q%Fux5}bP3q$ zxQD|TB{+4C1gL>zI>g~-ajKMb{2s_cFhN2(I(q^X!$H(GFxpc6oCV9#maj|OhFZaI z;umX6E*fQVTQ@lyZauuv>%E)5z-?zQZne18V5A}}JEQmCz>7^h0r)!zhinBG6 zMQghGt!Do5h%HmAQl~%m+!pr-&wlrcwW;qw)S$6*f}ZvXd;cHw=xm|y~mHbT3yX>?hoYKfy--h+6w9%@_4ukf0Et^zr-DbPwFdyj0VJHi}4bqRetSNR`DoWd( z(%n5>8MQl+>3SeL-DB@IaM{NDwd{{v_HMIO)PKO}v{{##c@ihB0w$aaPTSP4^>n3Z zC8Il%(3dCLLX$-|SwWx1u7KVztXpzNhrOZQ78c$jd{B9lqsNHLr*9h;N9$i+vsrM1 zKzLB_gVdMCfxceejpIZat!MbR)GNZ%^n|fEQo?Xtq#Qa_gEWKTFxSL4b{g}kJNd{QcoQ}HUP-A)Rq;U(***IA*V_0B5mr}Xp$q{YSYs-b2q~DHh z?+muRGn~std!VXuT>P9TL_8Km9G{doqRb-W0B&%d> z^3@hs6y5jaEq%P}dmr(8=f}x~^ z*{I{tkBgYk@Td|Z{csd23pziZlPYt2RJW7D_C#&)OONEWyN`I19_cM;`Aa=y_)ldH z^co(O-xWIN0{y|@?wx@Y!MeVg3Ln%4ORu5~Dl6$h>AGSXrK3!pH%cpM?D|6#*6+A# zlsj;J0_~^?DHIceRC~0iMq)SJ&?R&if{fsdIb>y;H@M4AE`z8~dvz)(e}BqUWK^U~ zFy`PX+z*Bmv9VxAN;%CvMk(#kGBEMP;a-GgGZf~r$(ei(%yGqHa2dS3hxdTT!r>La zUrW2dCTZ!SjD_D(?9$SK02e_#ZOxdAhO%hgVhq54U=2$Hm+1^O^nH<>wS|&<)2TtD zN_MN@O>?A@_&l;U)*GY*5F_a~cgQb_3p`#77ax1iRxIx!r0HkDnA2G*{l|*}g_yI% zZdHt2`Hx^MA#VH7@BEN68Y_;sAcCNgCY7S&dcQsp*$+uW7Dm@$Vl7!YA^51bi} z*Vy8uTj{neIhIL|PhditfC1Jeub(uy}w|wV5 zsQz)04y;BY2$7U4$~P{k)b`hZb>gv1RkD)L#g~$*N^1N1GfNMS)4r|pT*V<&KE1M9 zTh}rzSW#Kcci_#(^qf0gTW3&QN&zsW%VAQ+AZ%-3?E)kMdgL)kY~@mC>l?RH28u;Y zt-@_u^5(W>mDdtqoe){#t;3NA7c@{WoY9bYFNoq+sj&ru;Z`x>4ddY0y*`HRtHFEN% z@mFkp=x0C6zDGgA0s|mP^WNEwE4O}S?%DOtce3At%?ThxRp@`zCH6MyzM)dA9C7IP zI}t;YUV(Jcnw$4LoD4H(EM#!{L-Z|&fhNYnBlKcQ$UScR#HH>scYBTf2u|7Fd8q$R zy5Cbt=Pvf^e}m4?VVL@#Pi3z*q-Q0MG8pGTcbS|eeW%R5bRzKsHSH#G(#$9hj9}0O7lXsC zbZ7#UjJM^FcvdKK3MOEl+Pb-93Px}F$ID&jcvZdJ{d(D)x|*`=vi%1hdg(dd-1E>& zoB4U&a${9!xyxoT%$7gFp{M<_q z9oVnk*Dcp$k#jA#7-pZbXd=L8nDhe<*t_*%gj^Vx>(~KyEY~i&(?@R~L_e^txnUyh z64-dU=Lc;eQ}vPX;g{GitTVZben7||wttapene^dB|oSGB~tmAGqE^`1Jxt$4uXUL zz5?7GEqvmLa{#mgN6la^gYO#}`eXyUJ)lFyTO8*iL~P z$A`A_X^V#!SJyU8Dl%J*6&s9;Jl54CiyfA`ExxmjrZ1P8E%rJ7hFCFo6%{5mRa|LY zk^x76W8M0tQBa1Q(&L`|!e zrczv>+#&b2bt zuD1Bfoe>oW0&!ju$-LI)$URptI!inJ^Dz|<@S1hk+!(n2PWfi-AMb5*F03&_^29MB zgJP7yn#Fw4n&Rod*>LlF+qPx5ZT$80;+m*0X5ffa3d-;F72#5un;L$}RfmR5&xbOf(KNeD|gT1x6bw5t;~j}(oMHcSzkCgcpbd>5UN z7e8CV*di9kpyJAo1YyE9XtfV1Q8^?ViwrKgtK$H60 z%~xgAifVV#>j>4SN10>bP9OV9m`EA-H{bzMimEQ_3@VZH%@KZzjDu` zRCG*Ax6B^%%dyLs2Cw{bePFWM9750@SIoZoff4mJvyxIeIjeZ{tYpbmTk4_{wy!_uygk4J;wwSiK&OpZWguG$O082g z^a3rw)F1Q!*)rNy!Sqz9bk0u-kftk^q{FPl4N+eS@0p1= zhaBFdyShSMz97B%x3GE|Sst~8Le6+?q@g6HwE1hJ#X)o^?{1!x-m`LlQ+4%?^IPIo zHATgqrm-s`+6SW3LjHB>=Pp{i<6FE#j+sX(Vl-kJt6sug<4UG9SH_|( zOb(+Vn|4R4lc8pHa-japR|c0ZAN$KOvzss6bKW^uPM$I$8eTr{EMN2N%{Yrl{Z`Y^ zaQ`-S_6omm((Fih26~Bjf^W$wm1J`8N+(=0ET@KFDy;S%{mF@!2&1UMxk>jTk49;@ z*g#0?*iga;P7abx1bh^d3MoAy*XQp{Hl*t(buU@DamDmvcc;5}`ihM!mvm36|GqRu zn*3}UmnOSUai6mM*y&f#XmqyBo>b=dmra`8;%uC8_33-RpM6;x`Rrc0RM~y9>y~ry zVnGanZLDD_lC%6!F%Jzk##j%?nW>JEaJ#U89t`?mGJS_kO5+5U1Gh;Lb3`{w<-DW; z;USPAm%*aQJ)UeYnLVb2V3MJ2vrxAZ@&#?W$vW)7$+L7~7HSzuF&0V95FC4H6Dy<( z!#o7mJKLMHTNn5)Lyn5l4oh2$s~VI~tlIjn09jE~8C#Ooei=J?K;D+-<8Cb>8RPx8 z-~O0ST{mOeXg+qjG~?}E8@JAo-j?OJjgF3nb^K5v>$yq#-Ybd8lM^jdru2WE-*V6W z>sL(7?%-Qu?&?wZNmmqdn?$FXlE!>2BAa^bWfD69lP0?L3kopYkc4>{m#H6t2dLIEE47|jcI$tEuWzwjmRgqBPkzk zM+(?6)=);W6q<2z95fHMDFKxbhPD-r0IjdX_3EH*BFL|t3))c7d~8v;{wU5p8nHUz9I?>l zVfn$bENo_I3JOh1^^ z+un~MSwCyixbj%C?y{G@G7mSZg_cf~&@djVX_vn8;IF&q?ESd=*AJHOJ(!-hbKPlb zYi-r+me!ezr_eCiQ&SetY;BocRokkbwr=ONGzW2U@X=AUvS^E9eM^w~aztd4h$Q&kF;6EJ1O*M7tJfFi}R1 z6X@asDjL5w+#QEKQE5V48#ASm?H7u5j%nDqi)iO@a1@F z*^R+bGpEOs#pRx9CBZQ}#uQa|dCH5EW%a3Xv1;ye-}5|Yh4g~YH5gI1(b#B|6_ZI; zMkxwTjmkKoZIp~AqhXp+k&SSQ)9C=jCWTKCM?(&MUHex;c3Knl(A%3UgJT_BEixIE zQh!;Q(J<0)C`q0-^|UdaGYzFqr^{vZR~Tk?jyY}gf@H+0RHkZ{OID|x;6>6+g)|BK zs6zLY0U>bcbRd6kU;cgkomCZdBSC8$a1H`pcu;XqH=5 z+$oO3i&T_WpcYnVu*lchi>wxt#iE!!bG#kzjIFqb)`s?|OclRAnzUyW5*Py!P@srDXI}&s2lVYf2ZCG`F`H-9;60 zb<=6weckNk=DC&Q6QxU*uJ9FkaT>}qb##eRS8n%qG`G9WrS>Xm+w)!AXSASfd%5fg z#fqxk(5L9@fM};~Gk^Sgb;7|krF-an$kIROPt4HLqq6+EL+62d@~4Hsy9nIU?=Ue4 zJ69;q+5+73nU|TQu}$>#v(M&Vx1RD=6Lu`d?>zHN?P7J&XWwsvwJt|rr?CZu+l>m4 zTi^VLh6Uu2s392u(5DLaM%)Dr$%h3hRB>V7a9XG`B{ZsWgh4IyTO9R~TAR^h^~>ko z(k|Hy#@bP}7OyN92TKE%qNZfyWL32p-BJf1{jj0QU0V`yj=tRospvSewxGxoC=C|N zve$zAMuSaiyY)QTk9!VmwUK&<#b2fxMl_DX|5x$dKH3>6sdYCQ9@c)^A-Rn9vG?s)0)lCR76kgoR>S;B=kl(v zzM}o+G41dh)%9=ezv$7*a9Mrb+S@13nK-B6D!%vy(}5dzbg$`-UUZJKa`_Z{*$rCu zga2G}o3dTHW|>+P_>c8UOm4Vk-ojaTeAg0-+<4#u-{>pGTYz(%ojZ`0e*nHo=)XZS zpp=$zi4|RBMGJDX{Db?>>fq71rX3t$122E;cJ(9elj+kBXs>3?(tq=s*PeL^<(M$8 zUl;u9e6|EP5Us-A>Lzvr+ln|?*}wt;+gUmd>%?@Wl@m%Qm{>Q0JqTcxtB`ROhd6TB z$VY<7t$^N6IC(s*Z@x2?Gi%eB8%(hYaC zKfY5M-9MeR-@5h zZ?V`qr%%FlPQlW5v_Bp^Q?^)S*%Y#Z$|{!Lpju=$s702T z(P}foXu(uuHN!cJRK*W-8=F*QlYB*zT#WI-SmQ_VYEgKw+>wHhm`ECQS`r3VKw`wi zxlcnn26L*U;F-BC9u{Csy#e%+2uD$He5?mc55)ot>1w`?lr$J zsrI^qGB@!5dglADaHlvWto@|S>kF5>#i#hCNXbp*ZkO$*%P-Sjf3Vc+tuFaJ-^|Ou zW8=}1TOlafUitnrTA2D0<3}&zZz^%y5+t2`Tk`vBI93FqU`W!zY;M%AUoN1V1-I2I zPTVFqaw3Pr-`5HcEFWuD?!8Ybw)Y>g7c0tt=soTHiEBxlY;RlQ`iYY-qdd94zWjyD zFcskM^S{_!E?f3mEh9waR7tb6G&yl%GW%e&Sc5i;y@N)U5ZFLcAsma^K?Cg^%d{PO z=SHQq4a|l`AakzEY;A{n6Rn1u`7v~#ufV*6GZ$`Ef)d2%6apsU6^>QJl0@U& zq|wIBlBAgf0j!YaozAgmhAy0uy;AjRA2%(!`#&e>`V` zg`MfSf5gWvJY#?8%&|`Aj0<@aZ;-q#tCx=-zkGE|_C4)TqKjr-SE6po?cX?Z^B%62 zdA!75;$my<*q)n@eB<^dfFGwRaWB25UL#~PNEV>F^c+e2Be*Df(-rIVBJo2o*an$1*1 zD$bsUC-BvObdmkKlhW<59G9{d=@bAu8a05VWCO=@_~oP=G3SmO91AK_F`#5 zwXLRVay<~JYok|rdQM-~C?dcq?Yfz_*)fIte zkE_g4CeLj1oza=9zH!s!4k%H@-n{6aB&Z;Cs8MK?#Jxl`?wD>^{fTL&eQHAQFtJ_% zNEfs|gGYh+39S{-@#MrPA!XpgWD;NLlne0-Vey1n0?=ww18{L)7G|$1kjI(sjs z@|alUMcx*04*>=BWHv_W-t=rCAy0q6&*;kW&ImkwWTe$lzHJRZJ{-{ zl-mK6+j}V`wobm^^B&2Tl?1r=yWbz;v-F<#y!(CT?-4K(($wWtmD631MN9?trDG zMI7;9U7|UsC;urLP%eH1h%U`LJxT3oM4=gpi%X@lpVR9N6Q(uhJ00RWXeL-Z*V(O8 zsIyyVUvf=RXLBKX`!peifjIMvMs1YT0n$0*B;K^yZf&HN8$N%e=EgOejqihLPBT|< zs)z`nNU}BOdT7wYLy}R10eXUksn9o)jG)&=qteGc|XNI~h5R6UBfaPeIHbA32@*>orZsCB4`Q79}A=z@najfekt-_eTg7a}Mcas^D1ELlN6(y28c{ur|tmueFvIDOQxXs1)_lKrA`L2-^^VNC#miFvO%l6w5uK2bFyu?hyNLCjTCNRRVW^i+GX``giwc&TpV~OHu(yN&o)r2$K$1kjh@>iP z^&`?sCk#?xdFX+ilAb(;I7<$BQ#6j*jKsu%LEhQKe=>ki^ZICepr3#_2#pE`32i4Z zu%eXsgL)3x3Q-^OPPRhm<^!TEPoek6?O^j+qLQ*~#TBw4Aq~M2>U{>{jfojVPADAi zurKpW{7Ii5yqy6_1iXw3$aa!GLn|$~cnvQnv7{LMIFn!&d6K=3kH8+e90Zq5K%6YfdLv}ZdQmTk7SZ7}>rJ9TW)6>NY{uEZ zY^9PI1UqUFm|h0Vqe60Ny=wCFBtKb zXtqOa3M?2OEN=zDX7z}2$Y{2@WJjr?N`auMDVG9kSH~FjfJRNfsR@yJQp4cQ8zaFkT4>5XQqSVt5c}`-A#Z=3-_mGZ^)Hqayei zhJ}wgZ5UDln%)!;Wz@u=m(6C_P@r9*IMPe7Db`CSqad3ky-5-EcG=*v8J&{RtLJ(E zw2h-ghGYcDtqj4Z^nU7ChgEXO0kox=oGaY;0EPqeW89T6htbZg4z!uU1hi;omVj+3 z0B%$+k$`oH5*SeoG`Ay&BAA%nAUjQxsMlNdq8%;SbEAPVC#qm!r7j75W=A)&a6)3% zdQq$fCN;@RqI!KPfl9l=vmBFSFpD1cAxb@~K-$ZIlIL3W}?#3+|2p{|vZVq`YA zMbx|Xl57kJVwoetAo+opiewCkCIO=uBLEaG+!0U$MRdReNsx>+PIJWN6dW)pfeZ(u zQ8ei-Ht69)ZV`qv=vmorhOkF)Squ;)8AUfh<7A_xI8FGHMRW>~%o`1Wt3|8IMrM%& z8)|@=#ssro9=f9HtN0F#O085{Bf6PJnurfzS_yg?qqszmnQIYDP{N=xqPfvl;VNsK^qpoy2&App~Fe(MB7KCI)$p1!&YEB&%$9gTk zmvlt?t7!>_paNt_fYJvw^~LCqX{4opLy!n)md7}<_s?`gytfSAdoScQWTy&Tbr&~( zg9myGVv)l|4-umFBL0)Y(d}Rvt11)(O4ij#zeao~K$vh~JDn0_@3RjP2M0|79T&9+ z?>Vx&M30Sb15&<{RtpeYUf|n7n5GHyc+-FtA=7H$p6Mh=&M0O!so)tze7#WT>pp|x zfWae>0++DfscU2%>|@oiCQj+6O827)1}KsN^a>NSI*4?#ylfG-{q?3MMXX$dUH^S6Ni=Ve1d0(janpz@WqGJ?cG&sewpq294Qa zL{huwuoARdt5F4Dbh#?<2ruzSS{VeDAOtY+52t^xJW=!(0f3P&G3Cs^%~Q~~Wq{YA z!QrEk#>oXK{sc&Z7VB1_>fA1^#YyU1Ff<^9G(!V0!JW`n@EDdj$$2SVK6*7$!BvXP zmAC;h-W75(Nnzpro3CE9eV=~Lp7yS(vXnk@$g3{R`!(UG013==W*Hj{-*F!ujl+np%IX?E0*I&-K^u zY1z1I!`iOu+Ll`UtL|F6Vb?~vk=x9w6}eE^*<)O?pZQ#8YKE#b($x>w$3E*F0Kfk zfnyCo#zOpX1(P2yeHG@fP7}}~GB|&S27%6=@G^V=rmeTB$(w9rC6J@uQmcAMq zQ=Ce?Z0RkF_gu30<;5#jEW32il2?}$-6PZ?au16Y)?kUFy3L?ia1A@%S3G-M`{qn8 ze+|6jh0vqfkhdSb0MvIr!;;*AL}QX^gkc+q0RJ4i9IyOo+qAyHblI+$VuZ3UT7&iIG7640a)fe&>NOVU@xZ*YE`oy!JGMY%j}bGq!= z`R5xY(8TK&AH4b6WoKCo>lPh6vbfu1yYy02g^t9bDbexN!A`*$M5`u&}WqF?+*m?ZoW85&MFmXqQ1J{i;_Oz>3*#0?lWa zf?{tv`_JzP7D3x2gX&ICRn(aR$#>;ciH#pO?<*}!<}cYh_r{hb6*kkXSteV>l9n6i zwx63=u%!9MdE>@2X)3$YXh=DuRh~mN2bQFEH&_nHWfU{q+4=t07pt+Jfj90Or;6JX{BCQrE8bZe&wi3fwEXHRp zz8{VAmxsWU)3nT;;77X7@GCm7_fL1p_xKEG&6G~luO;Bc3ZIa?2b(*uH7qJ!es71c z{Buj4(;Jds$o78u<3df_2~DLq`e9*$SGmrR9p2OoVB5Q(KL3M{1>eq+;+lHK9N?xvyBPHni<#j$sZK{QrKEcdR9+eQD0V? zGPaq!#<-c#a>t4bt+R#Hu_|}dlIGeve@SR!d((u)Ga45+BuhHfA88G0cPrw>>(`ID zZ;aIyn|qmhuDXBthoW{J(WN+`Yud=y(wvd0rm&1*4>6?#8&)Fz z&@V=a0w4)F{^!&W_l6<5xg|-0F!~>aCALbeVsZTd*)M*^tr*!)O8w)mzKThWyQW@X zw%BFs5_@CIic5EPcTJu8=CmynV;``)3}gJ`Vl#VY_3Yib@P-KvBk_%!9OVu#8tG|Nc4I~A>8ch-~X%M@!>yk~ERI|QEcwzgI66IaaY>gx0~lm<@f z5-k^OY#SGC80Yr-tDRP(-FEJ{@_4LHsGJ=)PKZ@`eW75-r0ylN%0Q>&*M;@uZLdJ$ z)rw7Dt5ajr;P;~1P>jID!><(7R;w|Yf}qI&8klT?1dTfc@us5mKEe;qw;YKR(cp-D z6NmUMP8x7cM%~ytE@l*Mp^oN*mCF`gRNhw3gpO1PVi_^JzCJo>#mX(q+iJ(Ts$5=! z13b45gILEULS!=)SmZ{qsC1)$8-4eADGR?v z>~4k_SvdvPHAC}=4(!I^OLgQ@9EMDE7d$PvJbi+K%-HTh`P0#Ea|Jm6zj> z?R)(YWtZoIRx>AqzlG1UjT@6ba>yE z{Wf<5moh^-hu;ptAtPG}`h$4PWcOn>vy`#bH#Ss>OoAEE1gIbQwH#eG8+RHG0~TJ$ z>`C`c7KyM^gqsVNDXxT|1s;nTR&cCg6kd<-msrdE5Ofk=1BGDMlP2!93%0c@rg~4` zq)UFVW%s|`xb>;aR@L^*D>nkSLGNmM?cv)WzHZy3*>+*xAJSX;>))*XRT0r9<#zIpug(}{rSC9T$42@gb zy8eb6)~}wl<=or)2L}4T{vum>-g)QaKjtnp5fyd^;|BxHtx~2W^YbKq1HfB7@>Hw@U5)?b^H=uNOpli?w6O#~V`eG;`irLcC(&Uxz`L_Cl zS8r24e*U71o@dV6Soupo-}Ttu*Dk&EwY`h4KdY-k55DSqR&o7nufO)%>%s-Es^5Q_ z60#cReEy=$4|nW)bLh=|4bxW4j}A?qOle+wjn88oAeYb~!eA+EQ;8Ggp-UldAt$3M z7*E590amz>YB9L(z?Xx&?I37XYw?Os-t+05x6Z4vkzBE6-hrbB=GAB?p{DQXV4CKg zls@_wh*&XC<3R(CEZxg8*Y(6a>cIOq9Nss7{=UQ7Nv%O_WxSyBqnH{@(<>A&2on@z zn57W4Dh*E)o#rJ2#tyxV2;C5#rl8%%As$4qB=IbMt-z|jnWi>>7Ymq37;AW!6Y4nx z1Ogx#!WVdA92mEipgUxzy_?ddg|x)KOCyK)P5v@usc;0sN3{=0slt4CuwaxK@20eO zhdp~Z8iJ7GWrkq_-X`~(eBpthn9|`tZEUCIGiFpJjjxPVE9I)#z3Q$3tw`a69qxjuf+~ z*?v>d5~pcH-AQ~0)8PyIjumD^?SM8!Wb>KZoD7hOlc2nA0_(eG!in>}Ru}>6)>5 z@*}T`Hw{I^-?PS9>(#UFBQpW72* zsfj(2+_9@5x+57aN!`e`f(Mp_I(D>}p8)@&g^g+X1%d{ z%X5boE?hEoj0CiwTh9)#8^?~;|wgor_=Z1BI9_dI{ z&t*f95n?ZgZ5CnQa!v(p|JT?y0%KKgi`Smi9k5r!+!Mkz=&Z$%CFl;?AOzV`YBKrY z0#Y6~J6&dA=m>T@TYb8ukaV4z^Z?VX*MCKcp13-ye1*`gAj_Tm@r{fpm?K!U@Xg2AfndEo6jZN} z=XK0GRNXVLW2c?}B)rH^yR>u}b?|p(W$!TkQTAgu1AIG>MFfNchMQB_^-AQxRE$Th5-E_tBP@v(Cy|ojjP5LEU|JrM8 zVF5;$>Hl^jlHWDPChrTH(vh%bARyj5#TPb>omAs-)4zN z9?9(wybd0$Z5s+}Fiytv}-8U`IC<{6U2_NqEAkv;7lys5Qcq3EKt z0-!^Xy3idllgZ~qX^QTe=i*oGUCJNk>Y26?+9U(Ks|C81S{-v+6ebc`c(yibQbuB% zxM7mk>}dI-TfUi5Jqdu6b`4SqF)y5humuCaHhssdcR(jKf5ZGprx;Oe7VG#G6TA1+ z8oZLl<+ey(L+$Qsck^4fi{I|)p15MX73gHFUU!l${lN{)Ht_Wb%j#UE6cZ9}Wq^>+1wz z9TBA@%f~tby^0YWafmn&8Ppjn1Ng{d;S01WImtMzV<`!zU7;+8e-Xko>qM^OfOZ`Y zEZG#vcm>EGF??&G6+v(3l`X(xMn8ESv=@LdMfdcxFi%g1?0HDPG>blldR`OLlWN80 zz<$t+MM9%1K~JT@#aBZjOu9*G{W$u7cqTM|&a1)0wR8R^*r$<&AhuCq1Z{-aUhc5P zdyaaK{$P=Y6R{40FrWmLbDOCijqB(1PrKlnL)Tm|t=l}toVLAZOXJ*~-dx|_A&o65 zskcpT@bs+d@ia`f)t8ivl{(t%H?O?;=^s3O^GXqopx7E3kz06f^UQq<>gyNmo4Ij; zrOxuzn{WOqP75~PwPXC;3mZ#YW1xy&DEXsl~)u4`-v_{*B%R6xNH3* zJElz8@d#i4`#JV(ko%x;u{LMqLEEDmwD*(ccB9Wp;u*9I?=sC7g>%L{%$4m#zhbjm z)gK{LWQvE1>_yl|4T$nYKNVZ<)vza7FKU5*W~4)KNgN@;SA<9&ERxIfA&UZnB=r%N z5YD4fY$9Mkzy}!G+`KUy>3l(FSi1 zw)t)*w$E4#ZSxfm3cZLC(o3aQQ7uHk>_@fMTHoM0=quh%mfN6%{`O($pyzg0kPf=2 zjA%M7bRl4BhV5{{d4HbnTh`HM&YKw@N~47e7NFGr*9Yzi(7XQl-FJb4hPEKOC!K2x$nWy>8=PJYE)T$=Cqe(n*ChZE zklF{Ms}h0Jd|@o;Gz(~b;9d&c#0O^j{1?tF5dtMj9dG`|j0qZi^aF1r{<7KC5hZ`E zNX2nxJYEr@>u86|tPjTDet;fLn1R+IOm6&3b*}TOyNpIaid@W9c9!jIfiJOgK-aw=xb5Kpb)`E9x%CU82 zEQg_v`e+tWYClJHl=_EsSW?LZO3)o#ox(#2UW9|V7I8fYnz5fRtph`u)dywWL9}UV z*hdU9-BBK5G&}j~O6&dSdWDIpFX;&Or5wNbm^Y+A-x6(K$$Of6JTVl9n0gFY&=T5p zZX?pCxA&w{J)eDSfb?Zh*LT#AdiPlB;A%p|-`Aw6RP2mYTh zLmL~zM^VS0V@*4LkOEG~nQR)HyRB+;*KWli%QqKt&%16HWyMXRhtwdCgyoTm*5#itgp(Wap66 zyr-dgKgjl&t?JLMuw}!Boz)TOa2|37p^FAcPmxX0apWmfp$B1WF_@-dsK+?1F6~yY zEwi!-))Q_CbOP%?p%bx|=d^nLBig-_$e!nh19^Ps`s{SNq{nnW)V-qnz3y+Ipd7HS zsb}z%!+}y8izoy>Nyyj4m_br&8TGFcze#gP4?v*NEdl zzGBLM4qpvdu;5vCFi9^zXU;sW`>pPi|NFD# ze=$xI@7q9B4WPsw4CAO~UJ(S)s@u41E>#9D>!?=*N5m$%^0E` z<0RjkAj02TN9RLX3Js+GArg=Nu>E5z zPa!vMuMV06#7$1dLbwv+VGT(5V_&A~Uy3T^+|y~Q2>lA|=hZZ)ex%G`rhkN54C5gq z>w?qN=A+LgB0-@s{OJs7Da|z%dK)uDH4?m5Y=K(N5KWL)uqDxwBt>QmOk(h~1u6_s z>9x>G_+@bJhBQ;(Rr?20>Tjn}^Y`|rQvI3Ua5$aGq{HFf4BhwAFVk2oHNbk)hmAri zjQ_!g*-c^AKM>A@je&H)i1PsJ5929F<8bLXvONK4;-n6d;Zm7Q=G|k6Fp*AY!b1a`eoS*c zF413z6`x;!NZV1k5)sv;-Dqjt?t&|JLNGSA2yWhU-RYC^oiWI1+idw;6*>m1&Io`^iPgF6c$sN zw9j3KFYs@%*HNz1Jr?F^RiLV%@DyQ^Dnc1h&59pWKhD#AMQV~3k7}>c@gdw=dyRf5 zHGNU7bA_hHWUnI-9SXtjM~LT>U5!uS#{ zKSOhB>l^nUa&S8kEFoAUIDG}(Lr#|uJCGb%29Xr>1S4yk0d)9hoJ7#4xNbi?5Dt?N zBp45evje1L)A;&Smy9J8MJe@1#HwBFoYPv$=k%GOaq!kd58)tzBI~EkGG3Rqy>GOTce-p>jH0rb~c(K z1|9q=$3)Vdgcwyvy&>S3p(f~O;~?XK{)Kch&2!gs=%kNH#-Ee-i}S+a@DNWR(Xnv< zv7kIUUD(c?RS|JmPeXBC6cbxUl6qRxl;fFAiK%!>EzFa zJ$-mz?G%WqC+P-l!DLX&nfxzGAnLaFsOg^Vq~gaW2QQ<(qixj#J=;Y{m`?kHkfO)i zdxQ*`2Jr3iXdj4QE%|AlQ;|Wx~pKrr7xuNnTe=t-AO)iha6xDYpH}>yZ z+FD^H2VS0x4us;Wo_95^kElZ$>j2HW@wyeLi3i%Q28NXxQT7V1{iHY}Llc~!Dkv8* zM><6X$}-pv0N#?+N%W`5%}K0Is%8kCOC~LuR6+;gtHYPi9=dqUoin~Q^MhE;TSIe$6dEI=Xs(`oTlj_C-3c4KT+wJvpu4Kkn_RZVg5jE+RF`XNx?0xmaV~bW?v}wVTXn4{5 zO&2X+*pF%!%qu@3SLRk-npU5?`f_cV9;|pa#ktlD9VuvRx;TK+fWUv_$vC8-@TcO4 zN_-D6?7|-4!VWMEgQ}TUe(c3w4{eyxe8C5t7pS0MFe;X@U&B?sVDIGR;u>?mPyb2F zV5WLiQ2mX&1v=E#B`oe9yk4Y2^CFRk8*rV6k1!uW{m47&7E!m%(ANz&+ixrB^ng(;#RLHnX%tfsjJWM- zyBo5Of=eNl8*;gm`ozE0weGdP7~Iz5$$pI`$C5 z`U46T|8cnpt;J+VO?%~H_`Ph??bcn%Jzu`2`z~tc^PoA?r znJlfFuxIeRC?a>J?C!EC2Bn;dnhn3XeZ}sbjb-10*a7A?aS00$P{m0wm zO_v_`nJOwO*k6S$tHR@xmt`N`;fR%l>^^ZvbfRm}PUBtryK5pTwRdIZgj<#_irORP zr7I?yj7m&+KkD(;PKtLXmF-s9=>`j_AFjI$YN7_w1g7hD(md1~ysZj9;u_Y4i3Ssz zgRH~g_UH9AHR4A!67Z@2zch=Odh*4WzWc2=ekK0-ueW&=xy{z7Gz9CSbv}Pk+4ST# z#ZxnW&!Z1tS0A}`@LT_*wh{sv=f-Dy+2cPoUi{nzYTGjx)eit9s#G5^D0+(|iNBlJ zV$vUX35MrZ8K19VAN|i75_}Z#DO`R~MZQy~2$6gqOvN0Js%d70SzJm|ER&Jy5k>-I z!fh9^fC*zr22w0EG6&Uqo`eqC7_L8gi(#?!A>;y86ak0F7|oHQIhmW!15hHkZ(*|o zF+vd5r!A(imA-b0}qc4-&FS58}j>!?PW$SEg*;W8H~a^e%b?2`O8 z*`i%!x17FmIo=X;^83K2Y3Hja(b_rMns6%ts^>=(bA-9V<9O1I>564?R3a}v1yYtH z*l6T7AY0T66-95WtZgaP8(}|MBGlfNdh@=~Y1m!IA7($BPUtE`qT@h@;M3Hd z;_dtQw^?1x7-WaPK4XDxuqd5+qVz|PQlALGw|x}&MFa4RtVSK`(e|RtFN=u%s&M?) z7+HD3$diG_iYZuX{0ijc(*2C7cTX)p*3LRRtn3r@wq>%<@A9jY)yX*dv zSq7pIH0)jCA$)wa^7RfPVlWXzzoH}vzHmu4?W&f|zEC#fi<;dYS!Z*G+=!O(wLx7} zkfS~!6{@R-(Uw86L(mJl7`6&&tfKDx<)c+WIlqL)3pSX=7*`N5ysyr`8ap$bd^E3w89)ZgPiCBi|f{Ji^U)|AMCk%95n_gVk3|_XmE_Z6(keo8NCgI|@0sfZs3_s1} z$KK|ZCF;AE#cQiOrv*z^HWTBHM`H8Hwdx20FDq8lu^{(Q!@5s%Urrmi_ZX=7)j%7* z2x#|wO+pMI^e#2DpLkU+erWUorFxiNlu1s>XIg^5wIEm|joek2Rd2IsPtNkBRLQTFsnoh4v_<(`f@uV0I_G*I9RD+?L~j{1bx`#0ta zEeZiTNBzhh^|GEN+1vl7{w)Wm!`yhLKAuC&Ve`GhjRo0c|E^`tZXfkQW;&_kBLS|M z7!XYb?!E&&=u`h5Ld{_dyivFMQHW{aI!yVS7oS=ttZ_4U4sb{P=wmO6wCrO3g8Cir zRxN0ht{}^=kNOy`2fdgiLzr_8?$^fWMSdbcHb<)&+4+$`i%$>mB*aF7fv0tiFWhcK zRThLy0Mtx?A6Q34Vn$tJOcHkv?-ldg8_%9Jr8YX#=C;}%u*pWq^?L5VVi61EUkC^@ zTi3LAgna%bC9aB?Qos0?XlUZtnp9cISx)1AbGeO~JGb1<*DpHId@iRrT4e7+!$h07 zWDZ4FAXQ;*hdB%9)8U`#Aq1XW1`G)sm$Ol@ZCv2#2r5~I^BXuYJm%NgOkCQOAufat z)Mo2&C`TDc7EDz1sE;V{`=Bx<#5gYrDb+@@FE3>Yx=pZB79-7UjD-g%Z#qc&td6cl zI`S1u2Q2b!m^1LOg{LEV_eV*@cFW|i{!+a94itA#8 z2;?I%3?C8LQn5B+Ac|?$1Ejde^`AH_B}3`>#H=np*@XDR^y^=fZDd~Fz;wS>e@!M7JaPvv zPU?=U|2$6iw_+;&j{0oiARgl1!2p}_PMTg!Yxs?H%{HmJgU62_ghA}_;}{7x*brZc z@>!rSz|M}1YPdKizI;?B3~2O%LY`8A1SF;-m z+Oxu{+PYOU-V9O}bVd$T!;AU2M<2*KtciMEC29!H9V-u9ZUJ$M-4#Nb$5QVy@LP8HyfiyK->WR(e1g77J;isq@ zxu$>@C(@*mf}RY@L8hJXBrWMOEKDqt3i8iwFSwpR$W>G_j=iMN>(!1>S7GdmXt%UH zpfdn%XxP3S<>d1=1{yBn9c@?(YZkyNN1 zQx^M4-32#mo8SKR;r8t_CV3=RwbSNzS!Jbd%GS0L=qT*0!ERw05x~DzSsUKHYQ||Y zuwKD!+2nux!l3~g>0-F=;qnW{w$F|jqXuhZz#N`4WtzLDj_MYvu(*X@fb3G;s!oPE z?QMW|e7J7#=?C#3QWQRp-~(1;_=?J(Y^}oNmHRoN$^y4Pv2Z8cL)EmwWVNJh@>2ER z)el6y-IQ`!2h2{kx3}jwTf$_!N75)(mi|n=?Ylj_>QzqjfMiO67Wc4{rOcF4JS+{j z&z%duf1`r(U@ZlI{F=sZFnCGJv}cN<(cA|5AP8m+HUK z@vG9%#_zOu)ChxFSxmKsBSSO9XX%g4SU79e4=G!|Cgo(;VeA8dsRxIZ$Eqhj(brh0 z>Jh)P2`<<#u_i^?L>%2jxXAxZX%?<7l073C+~1p!t{Dj_9ZxL$sz|_G{C#{Hv@t=B zP}EsMr62u$;U#=d%MRJHCiNv=5OI3(_o-A=G_9B~AsrRui@pzUDE@tHg#6PmWEuT^ ziPt|@8=kjTNmkqdOlyJS!m{E9I87hqn;%9rT0<0-L99QeURoyK-&OxH^mcao3^t~WeS^K zH`XC|VCLo6*duA78O!ugN@5Elxkhd!CmdSX&*f=utfmDFD9PkBHMk3&aFB&)R8NL4 zD&i)OQLO z(Z_o2Zs~o#^$zu`{XU~$I{T&vAH3;ofJ*ZpJ&JR~s{J0}8cw}`t#a3NvWA?#tMY67 zLG}{Q{#6^CipQ$*V2|W$g2v->Y9+4=(K+K`;I4$BFUb9!Nrk0B*fL+v z_lcdO1uEs@|8I@xoKCB{68@q=)}90JCVF33Lb?M@bC5mog<2~vPXXzk7B$|75Lya& zL)t=%E&Pk`S-PznN<)4iAI;NU!@f0_V&wOND{4!~b@1&pAN$Goqzvq>;o=lr=43Xx{tUtEaN3B>CWZ)Uac%%Y9--wFCA~Ek7aAC_APm}b zpXAnlNOIF+;t%pPlAxIkvv1neXa8*XxNLX6ZDDR(+U5bi-=^>US$+3TyUFaf{gSPI z&A@*!TUbRQ-p-3$KUDc=Hp9j|c+t%)Z{KNid2DyGia&p6lgtpOkDeM{Qy=)H&22V` zFBRKM=Etf98a&;o2pD`R2ctkyWxz`aTDZXBjY52aOspy*2=?xDIZi>&&))8y?Pe*( zt;DkFm|`@cFI!Kx=wFn7fh&cqy-f1RZb2KRCK7JNBsApYHWk=M5J&|wBQOdb+2_^g z*;b(s3o^wX$sWZHhUhNh^+UU2+hPaWw)eN~kHy66akHOp4#cDm_4zDetK1Mqx+sR1`nMz9wwQP*hL>=&Kei3+FtV>|yg%{T(6f`N5BR!MdXj8xHG^3) zqCJiEswQF>ZLP}3Hs3ciKciD63}0Z^MFL6+`V473sGm^=U1^Mx3`Y|Mrl>H0pEcT6 zg^H5MH*WeRUNMs9VN5fcZQ=>}GHBs};LS}+P-y~P#IlYJ0P8ym@R(0L;jYe*1D4ll zwDy~vES0HtyCCI2411OeiC>SA#1wX;8DRXzVihdy^T9BjrZUmN_=b)~n*!R4%Wps~ zkbFH!%W;I*pJZ#8%)c_#RUtKlOksrV!Y3i%vh>?b076sjL-)-NtH_t7E8;OBZOPa@ zAofQ3jdT&<%k!kzaG)7qW3j4HcvQe1&&jd+f8}J3!f+>UDx7H_B8^6hA&r*!PDQ-B za5jys`+BVIUd>7lmgi)Y&fyh!`yosPQAwyIh?7D-h2#b7);pTpdfDrCm->#&W_JPe zRvi?=>OgitOs_62y`!|JbhXf5STOdjJDPjj*#EK7D|Q>bl1&L=hPkN@2)(QE#vP@l zt9uJeTG&n{WG78N)aYu19%#`y%8i44oVsSwNLRxgR6hF`tsw;8VRy)COB4`B4i4SsLAa4`Y(WRazi3X`Vv!fMiDilJX?r1a{9%U3-*f6J-iKJh{i^La~ z$yJ?ASG(MP>=IKImh$g9bD7xJqR}YghlfIHszUwEmoF2yQ`Xet0HgZCGNmYge2TvH z+d^IF=q3{GD`-m8K+R-7AdPA64e{l|c4AofbmD)4hUvwM1bw^%@mXLok{H%R#q;qz z+gU3h@JZH-G^8$-2?T_&a!E51(fhSa5Q$w^j>=mA9b7)O1^G1VKyM1v8fOAgDLfFwlSN7aDkBbh=1Vofi; z{_|sQ`!zOY>fWC264~Y0Y;ZbE!j3Cqv4wlfV?E8SiTe3tr;ceTaXo*JV!Oufp0KT} z!>xB&7aARQo9It=F0Wa;$5j)X(=fKBtv5LhYKFC6eJA)BwZ>zny85O7zI6@a-&ln8 zLF2LorHz$i{9dO!8mb#Jp?&t4L$8*9&!)KTkLxQVHBP8FA!bZwX zC$1xtlqa{pU|8*e#v_V+#E4OT zjwi(7(vGZ$V!mG>tD`=FtRvSqWZ9$*B?GPmVd1ek!0@{$s=gg&_gx>I&W_E$e<7Y+ z5K(_sDS$qH^8rKPSita&*B->#;u88_rMf;Axsguitwh`|=XF8(EVlU^L*PKbu#TN~ zwj8|9X*SENE}$egSAG|3#!^5By}_`$$?RM3+{=QMMid7b`V01GIvvI+&E63R2wQNp zn}sc$*2c&2oUL%!tO4~7wk4n)tpFT)D3<_3R0r=|=}&0KCf!VqIpm|jC(z<~qb-#Q zZxk@2wJZtt%hiN1;J9w_Hzt9B+S-HzVkb8@NIl-+0XLm`=_dDWyDqXB zn&w}0*`hmpYVLH;R9>jKpbgr%Tssmku7 zB4?i;DJ=yE$6)n>a-tiWd=_(RksK=Y6Abz5;b5mLI|>)(FA9o zGzACes-Q@1Vend}5C)iY7*G)}1M%Udge?eW(1HnSXri;yq(~2bXQq`x;Yrz#0k&ke zS%JGlk~lDWC_ny*-Pvc@4#dzy&@`+2PkV%% zOIv<3)+u>drFF184*~^AoZL$_J<;#J>d$8hF1HEz)8d7HT$%mI=(a%Fw_CitukY~T zzCPh-wvU#V(e-YoddEiUO$O~Gr_8a91@$Jc+rpZOpW6;!qTct6s-1GiRv51Kzn!ku z>d;8_q{~ie0yF5Z-59^#vLXATUx*cq!zD=G$XZeu&u5Te*HqWE4IIDJ=3 z;X=s*MnE=AeJ9|E8#P5YEW>Y3>i7+gy{D`72zWgEJ6_;p$$k1u>hqEMJ4WhXT+1`J z2UoHdw1-mEKE?MEYBN#+HGKNk5c-SiJgPNDBrxIO3hq2zQ?Q-Gzn`%I_?VYp&dv2M zvIvf0jiNBnpf1lm=3_A6ApuPS)>4!*8O26GMgpxwaM6T-up7}x$fShgk;qe5v^RIo z>TaB#z4r{2{wUbivuj#sL%^MIIAif88=Zo8VO`(VhtJ#lK)G7`AVbhecjuza-rrB| zo4s>x>$20;IoY}UyhY=kM#Bz+WZSjeUwYHVtw){{#_rt79ybJJr`6`3xa`^N&f)n! zT=yimh90T==dW``)l)vNIle^QUoEWPPd=w1q+I0(zj?aa4;5EaZaQsy5FJ4LeF}5{ z$zg##sP#GwKG2!Ph}IYe2=jqBViZeEZy;=DiXR5O3_2O25Y~Q9y=cg)D}9l1=&&Xw&3l?g{8))$`(k@{a1p3a{ens7utuI^2=vshxrlD-kY-br`D+hAM=))3(PZ zpyB3*357l{^D%K-(OTUkjEoJ4X>x<^UfmPAA7hlXG?QgK21ybCZk1lxS0Sifv<291 zEjcA#Q%-#E!a(4PJtQIWk)#atL{s*GU*JZt07Zc#S!1%fwV7fXkwZu$LI=?Jii9b& z9N7&))d3Vh8fPHy4GD@Ijl7yD&?%NGuJ_OccYXkIaDN7{Ux?ntALbeUyb?sbz03s# zLfJD@r)GcJGkZS!PFErpG3low5RJ#jCL63{qLHqyaMc*AVNejQp_b+{ucvHN$a_^~ zK+n|6Qz^l#n5WiWi;#UEURyWC?C}74{5m0i9bm^jS=(82np)-?!p5j&Hj8-6#y5q$ z-cZx{GVhaJT^!E3OK(B$?9)Oq;h*nmgonr@l}$~5ny#*74^BUz-dtT@>WZ;S_3r_} zQNaQi9BKB}jHzND-dA1Yeacj3_qnU%q4vw$L-Baogt=3ig3Ri*h;4T_HQn8u6~D8% zu3dIGR>z7KUO$}07IDA zm>ULZ#zLtQpB=zl`Xly=k@2w#_&57?*Xi!kJ;wQT>Y(diU_s7c9> zJt9NLo6(QTdY?<&%(7s~gGuhxX6Ia@TxNd)1c%NSn z1vg!?!9F%t+BbteRT}T^ikFtgySn40Y{9CQ#s-^l6%*Z|a#r=PT|QRt>uzZ1KDuU2 z_UG&)_39e07-r|Hmy8d@CawADtYBN~ud`dnC6l4WwkC7cwB?%@#G0C73m(O(B@{A= zKYo4MwAZI+m;dFW_8z_0tM6&w{t;apJRSqCB|8-3|G^xy4{cteem4EFg?KyO^H>jM zvPiWhJ7a++c1XQBBKT_Aev;X1adZCx?O6i7i}=MPVM!{DFhM1no>Vgi=FJObSSzE4 z!cz06q4?jt9&?tl`>Ym||8Lbn@fQ|L_G8v#F`IpVs|l!&x&>B}_z$1B(XGyIsHAWY znA8qOJ=@^)4xPoaU-h^g^}_jK@kTQ7$?aFf|5I6D)sIC2%qiC(coF8shYu$ie*)ue ze%G2{U`NRIn<&=&^cNmI;H`MZjd~?#3I1s@KF{obqiu%g9@l{o^DS=Z{*u!j)-EktzHk%L~ zUeueNeuutfbuxAHnCfe9zB#!P8?xVF){CM-QK}``94{Bxq4Q=lI*@*(t$ z0*llTSuC3*FY_i0Esz=DU(#!`f?@wi{if=Z>r@~3asMrB8H6RvvkTcW)vbP8ZeWX4 zzxps+&i<@^TXl<*)K}C$u*vFs=c>O<uva_OepgZ3^mp(p%~u)K{5Z{k!@f>W^5N zctHJ;`gb-C%!>u<(kED#4A{XPx$+SHa}?%+(O6P8P)JhxL-2PKS-#1p!TbB=d;5nL zMMOs=yP`{Yvn%^wn}ki9e$C!VtI_NeVz`$Lz%L_RchA@F7J^6AM{gFM+M7MOSKOPu ztXH`F#C^w(VO);r;56Hd1-i|6n#b*T>ceqoYd9adu&Oc+x`?PF5k{oi7$_HEV@K2z zymA4)N+`DI{|3bN<-4D@&N)YxIVoqR5q@8N=Kc5COtz?XZfomYb%y==nU^drYn>b!5Ctr?PZ$sZJGC4(Lx<*GmYK3@9};69v2?xCz*86!x1fq z9-^Oe{|eU+0lSwM-%%oRlZiDYBcsgabpN8BFSM>vThx{{TLd#395z2-=dkJ; zUPumj_0A`QOXa%S$dG#HKaV)PHrXJUqTZlMEURp*D&K#c?PX)`>TojQ>yzh(U5ggE z+}3v2ww-mQmrPrgHX82`E)7LZ#9*S)OrYMVHZ2*%Ix2 z-f6n^R()lg_{@W9puD-%bs!$vZY>)VYBn{#u=iUtgZ1U*4oibOw!C4kr;~&cIo+d? zul5rmlh}%uY=)i|^mJ>IyR&mweFZIu_7x~{W-C@zr5Q1cK^!y+OU~frPEZqXZ04#L0$|tY}D-NPT^J>z!>2 zLk;VdDSg7vTYSmLjc%I1lCVSm>+G7BEY6w@(XH|*G{ zSt~)o`-!M-5J4aV2N@%gOd!0FRFIBn|vW}Drt z-eWVGJOi3H9hf$!nudR8+Nmhg011-@!@NC3DA2QVhVsnWtq@_vVUsn7Lgo{)!})lf zHnxUxXX|Z}q6~&9Cutz=WXN1iJCP;&D8)pBPR#N=xfBTp2pd7-lFF5XXBc!;f}%nR z1Ca6zjC^CAo!5Zpsbiu(lgpE2dZaZQmR3Pl1Nu#$p&}HOO1KhD0hr0cDxiUoC%PDR zz2y;b(?1FUenyXAUfrc`fgeIi%?Q>s#3O>1`S`d7)!ab-ztxcdp zi(oNgfzqrSy+Qa-h~$kCFl>tV#u zT0yo>Sj8|%X=Z5eLYl_j3H$wFA3GlQ`NIC8!J3ZtWgQ*Tf>iySj%6K(I%;b=*zAUs z@a=8sq4nu=XBezD!_2jBtet7FSqQn zIF@m`p^X#2_+Y@)f(;Nc7NdxOl%T-$NRFKpzZ*Diiyv-9$byI~Y_VA7@fF$z4H|Dx5g*3@-my-zW{NS^+s=4LU=S;5ULvFYRU7E$thNp8*A(h3CX5s zqQ~5@=c+ot#VX*Ndavjg1ef4*RI#r4+51F`-Xy>#L9~eMYl6w8mrb%>5bZT?ljVD6 ztEdNv0*uOqR@o*xU>7I~%q&O{-x-#ny*Sp3}O21M?Rd(O98C84<|F{P!iYQi+&Y*nsLu5^Ihu$V)k)=GECZL$l#xZCMb z%xz~?w@;eYGR~3+M_}0ce(?P zl902^TxqD4$DQx-Ouql3YC)>Mv?0+^0b7X9MdejK@03cTh{%+U%}ktHqQF-^C6`xw zO``FD0}P~L0z_&PDjancf@m?ZGR0TUYN{lM-RfudpltLzU;yJ{R+GzQ*P|q&zCuzY zP@pguLKr`*Q*oFilK?v&y$CF+j-b`jSz!_lC6mW>m+2px;ND~mcq=BCmMTz-PuXY< zOa5z2j)rQ{(LTN*&~0=Yh5whf_W+NhI=_eaPTAgjUu|FYx>|LuiX}^yT;wh{;oiU% z_p&Z@Y`}m`FN5C~v?rUXJU2@qOB4H#QH{+~N5*}@@#Jm2%V%+B2D zcW!yhdC$u$WMz8Y@Q7Sm;An!nZCaUSSuojY3}>m>9D|bq{)XtxPsx!lnpMKJ$>l0=VE#0Q${LhbVQ?(avB~M5H(A<6VIs~Hmen|XCr57cj;wDg~y7PjIZR* zau8CZLCaPfRJMsKeNi~1P;*LSAkgMF^Q=afBekooDqXYIppZJ`(kv}2%`0n&8lEg` z4=C(+1ET{^|A%kM#z zXK7m|9Wcfc3=~;>1jcJfX#rU|Ppz!j;7pMyJxd%-z##=(QTY&BIZl!@lVSAb*KE2t zsC)F&?X{LH;g7;@GHGHi9oIy36f@s3g3 zRt#I$TBG}b-9;4UrV$&5Ij9vP)Y;Np6VLT3k-c!=P<<;z&y-p^C+_T2?PjhnuA3&) zZg_w4iMx50MTey|GHd-~Qvv|JOonzEpncEx-PZbcYu(#|MF)Yep>~>mY?NK)j*MDlofYp2?IA zdWFjqQYB^@4u{F4kONMK_E=?Xxs$LThk3UpU19S{Nzmr?e_{2qb`9sV2yanqH0d@5 zKGJp8aZ;((RpJ-E(g5Ey-P)#3bab(6W+bgQb9J5E$fs<9fcfNuxIvFo=h1Dgwcy+w zPuTU(HesXi2ZPm;XEiGog3BROSUdQwi5UwQ_J3+1m1G-UYluB@01JOMr|AGf`7CDG z0ig`8Ee4)kL6qbPGy~CNdwL7bt`jNhr{b~f<0Mqx@25+$lS$DH(Vxp|&m0t?&qQTw z7?k*9V*W>p{DU=}4O&dJVTtJY(^>`^lPL~F6O|IFf&j!DWck6E9}tqnNz(gl(B;1+U04#Mx7H@PM!jr;8}`p8X5AFzRgZ z`H&lBbVagpDgs^cAL}3%1zD$XOne$PNmH;OFF;TKQt?TS2u1Xly;A5E%X>i&LS8)c z94WDnS|omqYiN=XeK3B}x+|c@HmfZ(WQ<~YG9AvJ!q|jbd#I*5WUrl&T>ys=H|eYa z=2P;fwY|sZguD`qxdX)M>uI;{{E0Cl55B`!K{}wLHeN|4VH*YnBfJf$tm5E77<2U`gq>@HG1qNC7Hcyb!M;d687pf$B(PUZ=T|xM7)L(EmRVw z;~E{-q~ZvOOr2pdE3KGuy*wmJ%9P@R0*A2yuAhIFS3E2{e{lXEPa&La>y?-W>-8zjMwKGjQ$BzcAdCp)p^-It?U!LP5Hxpchm^Keq$?$57$5a!Z+()BJRD{ z6WgCQN}23z-^iC&TytVqsnMs6p-*RQ(ixw2F8vzfP=&GB|8F?{vwhrLatNCSGk0hY z#-0-r+MT6XGIxqGf<)4vq(!0^mfU%UhXXyCkz}3fmG;0s&`8l>X!W^JfDuz9HUo@{ zuuFqpp>Uv)!psk76{RqQDF$&!v^n_ECT`}V@{zZoqC)oA7_w~`M~N|5Q|_k zJ;Up>vyh*=Kjn%>HQJW}(v6${w!9Z%lq8ZlF>@K=Ek<&|IT4DB~B~Y_O;v9%9bdID;FI$4}a;O}@l!+Yy zZ67)fU;`NEa8WOT7DH7N_&*q17&?q>qwQXMcFgOOnF<0N*-^sEWbzzvC)kr_vv+i5 zgPm2{O*$B>IAd@{>+WUK><(pc@%$Y%QkK)@5Tn}4^Ln|tOsDsh=f>O`Mru?jc?N+S zjv9?oZ;e0J6*s%IG6n*@)S#6c137i!nnDgDIU_YINmjH(${tUCloc<{sdVK)q-C~s z^SX%F!SQCb+A?8SAq-ab;ILesL&}?2F1w-0Zdb;3_7dq1y_J`mAZv20%2Kk(?Wvhm z?BgJojYahs`X@A7)HA9Qm5P}EkW30FIDr{C1ON{u z1g5dIMr=}b5GjQLE~kiOEsekhAqGW;iWew{c8QDP()f-j!!>b}0<_?aiq6~yI>*3B zi`CdXW~Cg76+JS8SL=N!|F26HjVUaAW#N(;&=GruQ@h?1{-Ra%60++(*a{-;SN={& z3m*yJzP9zU)P6F#y&<2IYIRcSWv>_H=QF%ksji&bymFkwB+s?s!OWBD?KvFpwAYaF z6HB9tl5(fq9jdFlXQI1E?Q^gHxncuVOg#lH7*|HYd$Tnnm)HD6gV_v+Ekb4 zp_-m+TC}!*?8^M?Y`$XK{JN&qk1Sq6xYYg&+mlym)o2Awb#46$jTWSN#;OI(jOptu zaCbaIeUAorw`cR3Q9bDuE~l}?)pf9WSllS}RTN5{AmKP8TP%l##64O+ z<9w~)>KD$L^#-v&PKLdn&JjL-V;0%hPd@a%E}(nDen@49b&%5#O-QsX6;-7Ym_{)3 zVl37&u%3X?ma&!7b)K&CFgV2vcWds-QvlU}1h5qyxV^(mlpUfHjzhVqKa?A?iY8<~>_=ad! zk8dO`rvOwQj>Y9oP2*Ot9wKK_hBC~WVtf!r`yU%(p%oD8e+cg4QUi%h2a{}O5}EG* zZ-HLS&Y#FkWd<|*0G}o#4taLmE^k0-iGxUlg8Xl6I@jpH*%~?tx@JuRJn#pu1 z@%_I=rNM%Y&`YFTCG|8jY9=GAaO%H4EqhwG9gJlaZKg1oi{db>rau>VdE^b)^5%>b8}?cL9itw!Y(Bor%WpI?%Pj4J{j!bwjl?n=A z?##%PqWmuA8zS)5vCxk(#bC(9jFU0xQk5C=7R7TRzMFn&JpLe}gI6mL{C!MbWW0*I zJeV8RWO=t%FK{h(m362pOLR55=AN7W`u2&T{v&qlpQUo)8&gl^+xyG^_=H+E&E8{g zDtj>Tm&AiGOuNYD{?mSBc+fDm!jX{TQ=#IZQaQll|>^G`1^D^SV zM+ZBRqk?)b(96%pKAv6kG#;Gx_9RUJOrL=Ch#REmXQRXa?RfD@|1DZPOH<>K-+Z~L-ZeSdCe_=8y zv$DFgjbD+f$Xn5p?QtF#T$_pgT|@$@QGPJGo8D>TeAt8fg6onA*w0M>p@iDdM_^a=-IIAa==ijmLcDs$P+!j}iuEj;;q_SK-hF(6t&u*(3 zU!LE)pqCz!$h##W9aWv*rYjeIUm+JxEFjgC8ezyBN-_G-vS}?09R$E(jR6BMU5U^@ z(V0P0B}3^eADjeW+@$S6T2jX+!gXXQh=c{DMBthD%*Muwk`k2(;0!J{>|O2$aekt_pC0cNlWBQj*NqU$H3%h)ui z?qoV$6o>@NL$D;;M02ATJ{}%ng;dfcXd{fw1p6fDH854f8 zL_5c+rAD;odO-?4m`z)jE@0QsIP#m%s{3yxi%G|qJ9mC592Bk*4$?J5vvrf&4==v> zL*Z%RPT^^~#-wiB-EW#fR>F=Qt#Nm25b;_CbGzR|l<+O7jV3LT3y%tNHaS?@`}o41 zF$uNZFw7Y~77Aa>jb2bAph2cqyb2hF{`0@kc^4I@JroH*5@Ck{3%HA7J ze{=QfTZrXPG(~C3e0zG=<=@}#yeD$(it9e|@}t3Eyl(l}7SBEY4FhdhBIcb^!*gCl znFlPvfq4vU4akQLkM!yPH0F@Xp4CK5WGsrIY#-Z~%66Yny0cS6LL^vZ{#CoPf547v zDOQeSMJf?e5Ldtea!LXg_#yu@^rU^*gZ%^VuaIC)(1`K^c$#TLNtk$0pons6AR0!$ zLUWQKxeJ{spst%xMbvmTKy*u_|1@&<2(Jsb3$Ne98JRk3nUx!DJ=x2tx%A513Tb^+ z6{A$>`g952ZR_y#^#BMQ;Q?NEWr8Kwqc!wGt6zh&EFKrvp{{ zN~{S=Y!iu^0Jos91XK~^De&WAO?3BQ!NF<=uyq~mg=ar(~#oOa0#k@s$PSzc6DGpZY zT%MiJKfg1}p{soS^vIIw;22}*cuMOjV++=yo`T|dD%z@Ov!(S!t0^oRsA=_x^+YR- zRun2H5=~%|fM4gQs|vMD>7n5f8#?tsN@5RaH1W^l8V#@Kb6(2f^@31PSCF5~CtaD} zHvqx#ExV!o0Lk}Jze|zj2?JMi!xC>^ZcUbx|8oD`UrHT5QaV&bC3|pDTvIB|$&v2% z6%>eP4*a&})c8hn-$b+WaF^U1-Y9%4?aZpl@s?;DwsrU3yUt6`1&HKhr(r4L3qt&ZY~Ue$d;q9YOJv}hM+5p1Omb%T%HEakh-=S^t}!cIW|NCt zvYY;N*Q~sC1sQXeEuA^!svEU*$tdANv&&^(v#x9Tve5*SsoPZk-nva@m)o@7>0Un? z!Atj^ZD6Nk^lh>fKMh(sMon0&1|FKqIv6qslh=z6Ed%72Dy!IIOJsI&k(zNe{r5j` zk_^X6`ZxFWKTWP6!%seNfB&|pQNmWNqVSmX-rpQQ`2bN0Cje~8WfmX!`rCUhuDV6| z?tzm(+(*>4Rl?Uf)zvuzW2UIDP+k<|WI}{Ib%x>RC*r31(n%p}+BT+-9GkW+IrRJX zl4DHYwrN6EI=PMW4E<6fuero2mvA4UMJq5i)7)epXyn;=e>z3@9f-LGcf5hMl*Uci zj^i)l8w{96&a4mrQ~GllC9!c~%TH#{M$B;EW?N3ttH6-F_R*bkE z%xs+9eK>1JJlEyUi3|T4SYbBZx6y2}B_?h-TH3hruKPE(H$8SVQM-|~4Xr_@In|BW zVgnhInnHim#YFuiJF;qqG`&6hB@?p%o1y+ku}Y5rxPFzA>{ANaiBNe-q$cmhZ(g6f}5CD+Sf>5JC1{YNhE(3F0!pqbX3(RwM@_N|c zFzw=ol!l+B7sM0Mdy|AsMx{HQl(76 z$#hO*p?1?0eXP0O(<)bIWm(nM?>D&fvK;|!P?al}G1;T~4{9s&3~cWA(L?15m&fK{ z)~>Hj3O^K`+eU6-gO#NfAS4*o;1-7UNR|0&(@~!?n_WwQKqAZxwyrJL|JM&?c06U%ORPS!-dO@oAf`H*?OVR=v)~F4S5z zN+5)YCd&}E8gy1RrguKlTO10oX1m^K%4>6G=~)DM_>yi%EXJsGuk#kUP6`2@0mFH& z*Y7NFja4Y}-Gp?I88a-Qs4d@6Y3k4^;uG$8HkVZ>6{d2Ts(+j_*H>Op!RM>kkox{2 z;Rsw5Iu&f8xr|1}tTY4tlHM>@EiDGFo?bbl;~Fu({1Z6Pa>+DgRgwURk+FuLorv&p zv=R76sC6XM%S1>W=qad%1G_wM3Sh6nDM0zsc0|E!6pSFE;zY!kd0?&wr8l1tn`~l0 zKjN<7P2T10Tav&7>10G6STwUFdt$Ckoo6!J;)Qlku~Vxs*jOESa`jr1$`w?}mAukM zx|OzkuRpal^rsm`;TczAm!Ag(3+p`9y^Z2s;Xjy+&E`xnc2|LnIxpPt&XsPg6uUf-7ft7w~JT& zfw+4o-?d@ch@?j;51V6l_vA4*Mm!^38vC%}t2Q0LXa*LS0U5%JS+ZNQ2IGMa4z4Ku z1XMXlM4({XWT3mXmejMX4KfvQpFUQG=p6zh1P(#hx0TaeK{z8y&FKjo3kEhe;iDcE zfcF9NrmRd+z#75I#zyOzI${$C4z8egkGJ98@%p80)mt99&dA=tEGF*_>L9oaR=CWYsR-P*G_o6S+z$z#(P~a{(6#ymX0~h z+zw|!lNvkPaUB%ja-FB?(Fv**Bgd~HFZW*OO%_;My4Q{$zEnTq*A43HRN?uNFg=hl z(mS>Jp)!boM~Ci|rMz6Z8QFl};xW z+VC;%K?kAOOY{Zm7ozQ4hK7!RFs`B9d6c9mQ-&9ZPv@IOdauhoi;5;SiiX_ zWHK;M)?aq=IP-A2oqKccL$m)pH~*+mz|;ySZZ3~)-BsluH|nc;xl+!#{ao9QcRBNG&Y@@wdtJbh8!GYyZ)Aw zzW!rQ{z;Ot{z+k{O^#r%wLyJLxwd z^XJOJx5eNf7|~5`*>4^z8HR_EXsbFq6_{Qh=&*U_cl%k zwM=iU2Q-PXbe70@^dA>Q@*j7JJAQ6|4-hly6bGu#Guf4I3#=NJmMq+jRMnDLMGTM8 z6FZqoQTr`j5OI0-s_>JgLyrB~1ISJSSW>S5iIM8Fd`kT8G)kmiG74kB5_qw%knBSo z@oyzBOWuPdb_$`9K7a)3Pq%~9W`D>*IUiM@0O!f@)4ww;cr6QD5gESP1B%!6;MicH!*-Y@P77+wB?U{(vm~ z0JN-bp*I7tds}$B|2Yv_ml9GUw621L=mG8zKA?tYOyL8Y$OA*gF20al| zE!BG;U}OpgXwsPQkfX7WgsEmUAWlI(Q%5G%c5JA@ zvU7cnaQC>*j%_XCf?T?a7#|JPH|92fQQw$ue`M)hN67HnNs*fMopiZ@%w_PtA1jc&hb32b{w#B}vxOro)&kk4QYrL#`LlzCOWDbu%nMm`flvZfG|KV$j$ z-FNRE&whE;GvWRhXt!eH;b*Q&eRI=I-{8}UJ`2g|xFh(1d6<`@`9woMA|kP%%i+S5 zK1F0WhSZW`Qt4EZc`V(MZsAXaeCedS(Vb5ELclEaS@QrmjTB5H)0hpPEE5EQNlSt? z21ITlh|EwEWF@giEs@COAQx(+_op}^iJXqHgKDa5asPlpLpVlbgj@6s?#6S zYL9`li=n^zx)AA&B=wJxE3xcTD*N=wh_LiAeKO-y5#$mc`A=Xw@xj(!AZfrCg?F2! z%%%|*5?(3e55O%Be>hdJWqz|Y>@NYc35+My#uxNsQ%rG0cZ281FRKs`l-S?BR7$Qh z-dVrO@Xl=E(CcZ!zjWz~bC~pbD^8Y^*o%J<{*O3DPI*%37d~UUCSH7g{XNT97LQ$? zYDwS3-Mc~fzXjb-ryofsKuafo;|MWb{O%5q#oGdD3s3+{Gu!C$mzxRqo(e`nj_uaPooI_7+V3f_n$&KXNEvegYzVOAmOI2;f z%Txl_vJgS~zx%NlOt`B5A1jvKoKv>6a#W5%cB9YQE}Ng#F-&RRe*ZmNFS`A= zffzY&T}2~NcH;d+T}$M2l)?WJg&c4iEkTi+0V>Z^9RNlas=*@uckms`6J|+}MwkVl zE*N-dTsD!&Rw6C9;`uACcs{*j*L;_2erJQvcU_02%bc~Ubv}FK!A+YVd~oxo2X_nq zIxLJ(Kec`BV~&r=1*4{GtdwIw_4r|;;(YY{D^5OnWS2C@x2K~s>682AHEryBn;yjZ z4?M8>3E?~8cUvB~Zsk;R?@dJv+4DFYRsX`H578avc%LRj22up7SnVaEaV$dP+@Mb2 zq4CIrhOkSI?M#gOW_%ee~$=YyOXUUtta- z@3Q5iMlTbdyK_ZVk=cxE)U2`ldFI@H5%zHXu&HYiR*LHY$S&l*@|^Pwk?pbS!QI|E{fuLT9l>Vn41g5I@&W>ri?f&GFo z2Mvui(Ha1iNH}VO&gaA?EjuED!@2g}wMSvNZckt@^ zbBcT{_aqY7%7ddWm!=M@i%rJXYvdmtmEHZ<%5=2wE#Ya?`{vOxdvUPHUc~Hq)u^&+ zVxd}piz@JUQn_L0+rqRxfv#aS1_Qa)SFTn?$r9m8tB0)&yDHj4Q)OzVO1NO^@T(S# zL(0QB&KiTUe&dAnr^5A~AR?Oh+sP8L@Ls*u%05spT>iM4%=WoC#%#@Vlnc)Y*M>(1 z%>k=bX=I0!#ZUiZtZ{s3P3^i(18oF$Y@`P&pb7q@ zvO&%Rinll&IO>Nvk;2BP83HY%nxOt@^RQ6}1388?OVhV+Wsgs0?25ERVP|+&EE0^` z9;D*zmtfJOHEx^cUSPX*CM%hFt8IaM+BUL@o;Mw^gE?}ONuG9OHsL}9goCExOl6k9 zcBF9hZPPbzo-Rz=Cbo417-4=XMb6q`w5^}k)dn8)rye-Nvy7(}Gh*3HgK@Lu%)3+n z3oI%!*v)_P(IJ#lCcqSZfges}9(VST_vZX!8Iyu_9WRljFOkeF&%DGjD#;zAuOeiL z)kL;tDxm*yaTD@D7Ic(j;`>P;SyBFLyqBneU^?`pM<(c}IK9OD2nZ!U*T9lL1{g;P zQHC5spChCsLWwhCBD+2mm(S2;iqgWTOcCcZWEYknl3hS(8+Jq-!Js3u!vGXFx%%`X z1GZyXL7}pT{gaax|rmpxnPf6C{R0 zTib|2S=j5#k%yaW)!9?dat0A=*X;8^v`SQ&KeDAp3DgrAcLuh@xA;PZBR zg`=d<4p03_tdo51mGomi;T*5W zBR30JjLniAk}JV|c8{b_@+!PN3ED$3pu<0a5gVJRMq0Nr)(md5j3YKqt%Cs={mM&V zt(QUujwTQ>MqnxgM4FbD0^omUM`j%X;ov|kMM@GAVteUvCTv*~XK!V8i8e-rGO=_w zoddypK}UkYEyU(oO|oKfA7hGR%Au_RIi%5mMX8P!NNn^DF#hO?MyUXe5YZ^CBuAyz zAaoLmQ4tEOMf%#4pPP{;jWHM)?Ifp@kt=LAg`7AKI~*z{W3ezw)pVPUQEMy~jk*Wh zTB*WpR!FsEi}0SsqLk?wqmj|el+#Tnl^ko>maAr>%xuC2=oZxEl4o@~9aI9XR%h1D z(rWcqJyENP-l}^|YjhfkRH_Dq0Csag*5}@Ne*Zr;M)&xhr-|1PuRQ|g&-ss8aV zHQ)cOM)PgI#`o!W$Vm6yr&5JrWzH40eATw{n%~Tk@(&l_f~OwphL< zCqVa}HZY$G%oj?XR`mrDRG?uJ%%7|Dde!ITbG2SC$p5Y}8a2z$XEq>ISjNkZ>1)ov zgE4B@ZHNjMe(1B_iMB^&AdI3IXEcx*Chj7 zB70ZAgoM~V!p$$OCVPKo`w;0RGhZ4!{v}p2VcgvrJjUJQ`tKgHL2`y{a5*?8l{pSS zVw`E_9ZV7@{DRZbcUGeBT!b+Rqb4RXao8LXXKXTqpXO606l_ghxNxwE%@d7RW#3 z3UEXjf7lI6*9ic+0Pae`^tPR>QL2SMsL3oEYnGOP$E&ou>S`~7xQVo(=)(GU4qQK3 zr?C@W$tk9f*D9E@M03cl(WrbDVpAIxG#Fl;5L{*BOWVj61YAL>qYM>lvf-j@87tpW z>ZJvtU!o^7M2?;aC>6H~*pz?_@A_f43oiSGu}SQ@oNif|jUiqc=UP!8 z=>_F32*pk3PFPZ*vcpA%CN-p;Wxmn4U-oTG7E0BO+K-oF$b+b15-I&yI4^>TevPA| z*`O%f1ySQ{Y5ZqvdO^$W`%*F%#Lt9hQ~Pdj5nk<{#WM`}1&EZna`}}EkJxL5;b(RK zf@)(^i_(k8hi0cS63J zs|Oki5QJx-ntFo~>>H%pY^E}xqM$b5MkoYvA@~kW?9WyLsNftU=J84%FU=uI1-qz& z1e^PwZW2CepU0^YenL2@YGH@)Zu1jQ{eo)vbm78VWF|Q$<=}w5W#K|%AkIaL_Q^~f zi|eTOp-#ROKBVnH#1e_)P3HY8s08{;dZ}0gP%Po!hLQr;BV~334uMWAl-Bd--#Lr4 zPP?Qdr)gAseNmTiQDw`*c6`PC1Bk z|3&YFAt(-S5J%N3gxme>D{!fPNgp+SjP6|uarzfLH$e)iK6*+D$1m-L*m8QjAGFH^ z!4#H29_}tYGe9>0-gpLnEkFNVf|O((Fhz0>mN{pkLJV{|+nAL!+nm@Nc5q(1;$0 zM^XlI4futW(0Z&+Dmx`;z%>=+F$`--08{c%b07caoO2rfcx&P4E_cI%*(-V`x`@j; zY3;gE`&aF}^~k{oo~)8NnyMR&zN(UV^8aqFW1e}|cCqmFEzbNRLwxxa?}InfKOla<+Aw3N@!C?SkfJo8^8o_ zI-fw6;_#rs8M>Q+4?{*lf6ip$gGD1_2)F*3nIb$OJoLNYv87o1MtGo;=rMVHc^Mg* zzJq)5cfvzNlfHv34fMZg$+Pso7znVXSU~|SIp>ji?}fH(>3^H-I{4m&4?q0ywD-t7 z&`*A`g)pImWS4M#Zu;G9Tl!s%h6&iR8RREo0+8h2rQ~oF4^Cf%UjrF-Vx~<}RSZ*I zE(2MIVn4)+wu!iV_&KCBJ7WozHtAvFJ})oAL?hICnfWHzmC33lUvkOkcX2xQWGg~> z@BaL}sp{L$pV2vjL?679*l!~z{`9L2m(0`GtD8C#ot^Q#F%1oEW0p0nz3W%&ub4Tl zv7>Bsdu8sZhQ_w8CH3p>X8H^MuC2*;raREK{(9zN$DD5BT3H_a=?1Nud0!pn*^pUZupA z00^Tj5tSm3ES7<&%$QX!=9c9_0)sU3X6E^ShyF8t!uA7Cb=}?d)XA@&a=V}EW*W(c zOu_RclPZ>-{Zx1NQ$Vf%1X5Uw9d3Fmy}|)ud-_SSfJENUoGgFpK<0AjCt1h|evE%Z z;>VXe18_1@Fu#N{v}Dy$lYcahh+FBgOa3nO3B5w!-!FNJjDG1I;T;eXh*@fdciwr4 zjDCtq-A8v`@^_NF?=`aGOWz0iLhnbEgMcy@d_;QkKk$7ipcWA}i23ZFsLEMr>E*^m zNiljMCxS`D0CtQRk`;cwZFtH2PC&AwZk-Esg4y{wTFw0ENVACmqI*lPKgx2}QEvCVye^Z; z7cdw4Cy!~hT58(tTvkqTwpOE+DP#Ggikowbz?sCpE1Y-gkZ|y`3z*$+64-JWdFkBM z*Ij#OYe`h^Gw4gVEuZc6IEwvFsdR;*#pxI9Sj47n+C_64wj)Xcy{3t;pT-^ zp1g)@-ZnI(|2o#{s+>8q(rfAp^75*M!p%o28Vqk=(~!6B6Rq}RU(=z=?xM1(WkubU zhnjpJYqg*F8xK`aD#}}&S2U^mP@|C3P(crm1S=Pk9!@{A(q$bR3U-;imDb8&gx;j0 z;T429XfFCd_&s7}e*eKm7kxl#5W7Zh_&9LS%OJK_PssaKWeGE7bk2mF(NjBbZ8CnPRDNY_y0vqvSTwEU)@I|E zO68Zv=36_MNF$?~kh8xcr^0{F%jpBc+=KqI8uz?&m(F%qRQMx)?AV_(LB-(KX^Hq` zc*ZkN%k29pbUyV*rbJ(s3^CW0uoy3ptf1(|FpOf9QHdS+wI<@yAcjwBu(VmQ6c=8m z6b?EH45R20DOnSoM;S*<`PnH@ znU-mbX3h<@cXoy%caE$qshO~gkdgW$q6rpc|}mM zfW4fn2@zHg?ak<`h$MyQiiQ`Lv=lS5hhmgJXsl0?YsZi4E)8$=c$QBnnXh9F&2c*$ zo}1qk)E{n2YI&bMPp&&}lpO)v=eQDNTY=41B&;b>thIE#&z#?7w)+at2l>OB;qvN; zop}qqD&bJPd~C*5L)|+2Gh=x(#-YO)hiLs$8|GplsgTtp7@+wT*fLZpU7J+vUEW}w38eItqmZNf`rIh|C45G*4gvtuv2ThuDXc4 z_`F(~o4xr#n>-TrA-kYAe{7|2#8J7Z{f-(gd;Ga>&c1)lWrqs;pUj`koHIS(pOU_D z^8LS$#%g*dRg)QD^LVnOJea-VNlv(W8>d}4abi{VBvc^g{(<%>=A~8;kSobx+W^dd z&`(FbE}}m!n<$swWH;yBxQ58)FmSG&`4)_se1oQtH6u;oagR#y4*UV% z$RlzEQQ?Bxx~KCmCdnIwnIbM2*apCK_K0`0o;qZC^gB zrnD~peLitnc+7HIOQfYaR@=5i$KjSiQ`sTL}ZLR4Z5zHCAtN>{bMsjN!6PEI-ku9@ESMg(;v}J0-^JMuS7w0b5 znX@cD7-?=8W)2tRaCYfAMyrX35sT!5f6!STjzv9;6_lBvK768%HD@<*NHttQXnIdk z?y7^F`IN{L?uU%rCUVHqK1zo@akLs-EoXkZnBZUz#7i_Tpn#3a5+TYeLYd_#dc{U1 z(h#`k#S*5uBs;gUF*loal*U~7`L0;$=f#;4=AN=BEs2&1-}$2Zg%57C1^v#VI#-t> zJzRMAY0~-3eWdazv*eQV6Mxve+y^*iS4kA#R|fn- zu&3e;qG3vLMn`=l-=NG{P!dW@q#yXDaL&2329-vr{@Uo%C`>lC=j2i0{4mP|q$wR{ zgn!v%CnO%Y0uBjp+Bjf5$TTk4KkHU)cFe@~QB_pz^SCGfJ*?JQKf0@!=#AcW;GQ7N zoi;maX8SBB zw0v&=GnX)%`~NoZ44HYcOdJ!a{DCi*(Pc}iWH`|I(H=k{g-Q{v<}ma?m=r%QWf!J} z8H0%E83q-u1cZqn?7c^L{#>B=FH!3BvbI-O&wt|5F=H-$V*bp7Etk-A)B;d}v8Z?J zB4WCFFCq`qCkDZL$3!R|>lU7)++0^}S32aEDj4OA`8fRuuF~3gDH32)EFsOzy=Bgl zbuV3)$8@b(Z6hmq6?u zdXVtQzxf91Fn&M9rzk%aFfXVsQ6;NGq(q#$=}<**)WJ{ZWib+A-;a)nqTVnf6_5cn z4t)>}4PzEXog;w~#$Z1ki{Lk<(qh}xw}&MofCb9!BjRB5?P=tIsR5L1!lWmvIA=!w|rhUdd}Y5$nj z@Zd2XuQLzdk4WtBzY3^hY>D1*R4J-QL@7{T4h1Gs&|F;1!b2qrcn-4Ri{yl`y@Yd0 z*^pzgBXmX3x!4)Jdgi9aQKc`rW~P=gL~>^9sMO=stc>u zp1E|DPH z1|+>G%%}<4&@;lb7~m`>2842kdFnKRX;3oaB^xJ=tNn^$zN#HJY2(KGHZfn-jm65O zv2|Y|sE=$MDk`P#+f=niuhp-qLb%_?NizMK%8mDJtX!j)P1?vF8!9)6SVmEIG{8bp z2aE9}WF=dHrxwk=qJ>vZKCOv%Yh zo)At7f2FjnBAx2PwiC{psVaa#f^a&N&m&A4FlmWM^^S9%ZFIKlfmIcYLA zle~cwab?#R3c6H?C69~O?j5+5(Ku}I{&=DcPF1X14!C@Ld06RKKXaA|hyZ9WLm+u1 zYU9HRsSL0LRFN&gn`8*8j+(;EIWTVc&J}Lr|J??}oqO%vFY7Pd{Y6}OUwA+M#qNvh zzMOllm$Y2A^8D}4UwIj6VU8R*BHYKNenP=LIsAo_?BrvlN&QmChJE`sbiAY%o;Ws{ zJ^8}+nDF|rXml9KiJ>Kc>Yu7U7@IPDQ1zHiY1R;GVYn5!>kiY=A@hYZ6D5!jXKm9F zjgDUbX@8jR^5dZ3&mH;m`~C4Uo)bA9>NwaLyc_};espuXotf1sT)&St6D)?TGRdDT zPCw<2Figb7ochV#|KTi>N(;hPVQX42l#brCNgD1 zvWp5s5{;f&-4$_d+2V?%|A$k^r5fdYhRjiF3}qc7I;+Crs?HH`C`>$a*KxQcE=)hS z=pzx^E@g3}=pCRZL~ZT#1ON~Xut5lx&eUcc*{uON08|U3d`6q&Pp<)B?F42E1NRRy zJM%GAHH^}96C?Sr?6UqhDb*1YaDnW1aE>TLszQtvMYxNSj>v)_3QAO@Im7ql1+=foE6>vkVT=e zML-E2DW}+g0qxjgNR(UI1)Cq(jDO_2P2H0>Z=T$}>HXxWlfN2Uojavei`8=j+%dd!-BCV*E({dFq=jrOQYQES*I7_41O!tkCj<#5M2QaG8ryvdqK7=gu9TZr8csspKTHAy4i_ol!q6 z<&!|m64QwpObHr;Z$XeC@yn?D)x@T*VtiL!l|DIvw7dzSd8F_dSYno+%Z(I9k_YJj zv|M0aC;$HDo7~;~Dq$pkFC_j<8=icM@OSfRWQ@v%95YffhmKT`I%QJSENWZSf?);l z!poo|oEX;_!8Rr%>f(a^n0^QrUm-z17`_DZ-=T;mxdE-G&1&Sa35xRsy&xnq5mJN0 zK!wb!qvfZ98jkQ>%^p&%D|XmjyV>G3!aoc_lNykvoS^23*1T~x2U{uIUmA95?=I9L z*Jlw~^}!~T5!peeSTkrd+Vf# zRppW?oSGxi$X>^L&`5?#8hsNQ=(QGe0tSE&-C`W$&(dQ$TdnBh+>We?VZv27Gv#S`x zZY2OyBt_P2SMC;6st1M5LWQvTL6yp|2gJf0<7BwUm3uT-o3rxrvdkMw@MpJCqwJhC zsZ*&j?k0Nqf?0WWb$PpuYUTD_yS6LUDAXx#+PCi}1wHVwKmF-3dLTu?Q9A&nV6oSo z@k-UhPdpYrmPL~F=$s-#*jh4}6K)VM{Y!r-HzX`A;+Gyg=WM=6{lGoW=DZ`R5fm3e zUJ!qT%nyqa{2SQ%$wGES$NUcb69&&849DX!S%_!9&{1|m^t$s{#zpXjSU!ThAZ`em zpMkBPEKH+)mURqx;F(k6X~?W8PDi4?A>1LBv62%KdYqIl(To)^r+k4rkHRibtuKrp z+A+}kFuI9BP}DF9=o3}v!~q124L~~#QGm2Yp#;K80}BN8x{HW(2&G>btrLYno+H9@ z35Jh4PFn1&B4`XL_{g>k=KW^r+_+su5K}zr`hwB#F1xI|d$y4oOH{&}z~X<*=X;n5 zfz3sWma*%`tr432PLpt_&gu7BDvm9EuOiIYq6=p1X{ncj7rFYuMO!}UiUBs)BTs*) z1o`Z5JrSoV`*u2pM+f-Tl<-D7;B|slWs{gddl4xwg@uU$RM2QL(h>#HgZf$A;YVLG zl0$wIQT7Opo4-^W&Ft;P9i#4#aYx_(jN}G|+H66>&7adGyzLmnne=3yCCIN}dz^55 z%q53NnLa4o_=l&E4%Pk62f{t%3gK|tBrIdDXQSypVUnQ#)ZYSK&Dbq7n*`JDF?m)27D?iLX(kMOA%T@ zfiG0Ffqf_p6^<=Uz=~9Qb}N=Wa;dfq39?xAiLF(tr0^|+?3lV+4bD}=FZvDP!*|ZV zleuo#==FO+)Lay)iB4#-+S-?Fy@|QJIIp+>9J{11)nNVZ*TGkL-3_oO9~YaG97`l8 z*{J|YePRu82%1q-h4#rUt33k4Y)Nlow(4E0rq3O23t7Bbe$|x$vS#+eW=Ftc^%IBu z#`5&R9&0=M)JgGTyx2DFr|X7BOXMQjAPG%>5=Me~z-OXC8J2#zo#gSvuEokmLq13>Ks;moLJ;z3yyYjIm? zg0+BGvYJ>*qa~#P6T$wBIE>PGX-G8vh!q|}3>8NeL~*NpU@c$^L@~tDK^DVraY>x& z?bc$O#cGkc2@KvrDU$WVlNFHR@nrPQ)cb{S2>N5OmC_7h^vhB+a6Q4DaVe_5(lU!# zw4+1&r_Wz*i%LbWS3HQz&{u#fCNW?^PSAZ(dZ*GecfnPx^t#xIhor9}Uia*q{^*2( zor4b~3k1>VM86!(%Z+PMc6V6DU}B5XdIGL@P}a@}*xZcN_4A&%c+8lK56{0owQc&0 z+cr&|vU&5AsnfR3n7%D_{rtmp-xKq$XXeNZGSNw8Bf?kHe2W-ikXB#O|-cKR7uZ5(TT(GVQ1;IKD*BA^?N;j z@0}ix!ATR1xOEQ{YHbdiSq;J%Z=uHSbC@*_zsJ8-uF;r^io9-jp=FLI67~A6TB9W( zn-kh*Q+vJO4pAtKQNPEeH5!aIo6)4#n%(}Fki*jDi6SSb_5z#QlcAS z@#%&1i23tyME{#Ci!?+UvreNCDv`Mgsb5hG8a^*#cNk6fiCMnPiX-Hp+aBztPl4Oh zyHn6D*0IHn$3DB=tiNbPC^UlpZ*J0?V|6jJJs@Q`rA}qn+Rc8tYS7vYi29IOYhBsd zuG*5FF<(~HWYziASy7zd5#-z)PSo2q#2&G$?fT0GFSTxP_hrrNTFu!t*=E!SBi0Cg z2=SRH$2YzncHm7u96A(;d=Z&(Qi-??nsK-hIGvf`4q1jA~oib#XKO7tb8)6w1$r@c;e$bb_`&F~Ni2jzvZn2Fw$ zz~B)d_)khjggJGS~kwcJ`S$EEhn$FG)b)C?Be?Rg4{?f);@1;dk*(~!#;TB_6ue~koujG{(Beh zUbt{KVXkcLp4__g$fK)QtXTahxoGr)j=G9-8WhCenK&*7rYIphp6F!0FZDa$cKI}A zbC$PH6CR9|P9~in$MVcdqgHQm<%JWmV76W(Ra?!jyjZd}yEEKSQq&abG|$;JC;bSc zi%r_Ko|C*fHU5MMZZ-d!_K;<@%9@Wx|6OFrky`ijgBLxNotf;yC;P z19KdM9L-wjp>Ck8BG5)h!T0r&0%+sf$hTN2Lv zkjxKXirD2~To#O4g3+K1RK6xdDPT%wEeGp9$`BglwrgN{jB|EL-iaRh)`YmW(^uJ7uLBa*m(&$7XGI-Ke zN;nA09{>_C7UNiom=;}hVi~*+tXPQjh2p-!$Alh2G7T7~LDWZk#B@Y`_||eS0j5c8 z+}MXS8)x<*jNC9-9f5cm&Im-bpfa@rDJ#}aeD&mfrlGy%ww*gk?W`wa$f&eubjT!agn2CWzTsF$9FQLv-MyCyzdwe%0(XgSv}M>Fy@F$&>plh^`XnrC<3lF=|wT zxwE#mprEjD7ST?yA%cmit*xpe>+d> ze4^cc(iT%F0-o}GzhxHDd0~0Nw%;391a(%WY$gC>p7cuGwE}l#_6uJTU3%q&Du-Sv z1BNQ6(xHc+GOV2wta51Ju2zM;w9pK?-$vo<7hb5Tx!}@jjIK(9#}tXZhOa3(4AZCt zeR8mWs=yNvM86y>IS;5hz*qP;0}qHi0D~PqBaSeil!iUQlCV3>8lbEi7?siLw38X7Ay0^wp7>Q~U9X90Kmz9u zGh;-Yf!@kam`UQaU~ zKC^g{E;aY>7jX`w7r}f$FY=D2T_qmcXkvb7<8v^QFe+0lBwIdIEMQiJi?iI}QvaG9 zFIlAGEc-(x;`Yw!xJj5VRhrI|!-jRvUkNW&`eTdRs$1-4wL%XTJcV-aZoPtMmT%{l z$~8)|v|`{C&B}j2h3Jt^>K>w12|Y-kXd!bQUbiuM2zE$ z5%+bOo?z+mdio*1I#~xKh1Nl9@bD{9rvijuq<*AxPY@W|#D%3Lf z|LDW95-oJ%uc7PzKjz*$Fsdr;AD?r})J$)wlbIwl6Vlsc5+KPWKp=z?2qjWO?+|(s zVdyBJ6hQ>RtcW5iifb1!x@%WfU2)a5#9eiDS6yFsbs@=IzMtn#5`yBo@BZFDewoaj z+wVE&p7WfiejXa4W`Z0o=tf#%Y#8W@tEJz+IKR>U~HRPH7}){FA_g z2@RTRpp84qzJ|6Tbl~m%2s1O8`iyqZ5(?E!d*MNCf_fBIp0pN>Y$)^p^{g6c-qdT) z2G|`q!rdp`_EOQ1xd-;oeZW1skI7UsOBvE8XfB>qbJ|9n@GEyp#)N$*zuR$;iHTMl zMb6o*mJJixJe)xE3Q6_4>)`+&0VYGZT=+r_+-_y*&qQ=9TDu^?KY|vD9{9zI3DK(5 zME=Du$arMS#9PPZ2`ya}-Oqi0SJ|R6){pAu>P}GuxC!H>S(E&)JRvc zK(%pLIt!%_Ggh;J!P3mN(C&zQ%b!{2zgdp>O3i+p(=nue_40cDaryCg10&jdx17tO z(^oG`_H-m)1cDqwb`64b;Smyx)_@t0hzGhdMCC4<9`|!TD8jm$rK?L{m%e7ES5xX| zjVv*(Fl`#N^Ymjk_TQ;du2gC}db*#$3;ZWOD(u{Xf?=5$H@|z8nKTK#24ycWnW{7M zAKQD&^LZK7DvgHE{3S1zo_>f1NH&P+M;%Csfl8EPu7x`aIkw>Sb*g?XAd3zsX^HUS z;UC1y6~<^aDLl9k{x&4~;8i-HtfOnX;mQ^KYx5>mteILiZ%SkHXs&4RwL5E-R@LO( zM6u}hNxwS1`A=KMZudb^r4d&kLjbo*jB_XUZm7xw()$Npp75WZModdD;0bDHwr`R1 z_{sVCpn^HUU7WwBZ2nzSn$~Q2(Y)xssf8Q^yiQfaGpCL)?csqTYl$*OC+Z@HVq^XB zOye(GF$~=Qgsvvqt>JX}F)?~g{W!WMD}jH~8i`yrp|6CFShk_1l1@(nOjnF*SpCVK zPZ>c(Klp(l_zKcZz|T@YCZ0yA0EZ^D{lW`$b84Z^U^;j-tpQBvB00=t(w>;jRGNw zHbmPcyBkeUMyN*Dp&<=!4Z*9_kr2sB-A2w*DIcMAtDSr>qu8;Cw5OT*sv9K9fcGOK zSm!4y(a2K=dfsK5;!ihJii?WuI$xqIGc`8d;YdoW%gL@wbJ?B#*wjo{qOWdT^k9m- zk==Ptc1~SdlEaZs=lt{%`6zA(m=DT}5dFZ2(yka(5~#H%rX*T@>g=_aAidv5RVz4Y)D3sGFSTS2r^}yJIAKH`4lg%ntx|R z@g|#cj@ugfX#OhfWp`jJqBtUbHkZ4DSHKDHin0O4ELt|2GH9gHaP!L}3}X%RMu9^v zuS(%Jt&VKN;Q3N&Y~gBXg}t%bWVW+k1Gq)5L#s5@ZkEsLIw^XNABqBodZ8Z+V-=0W zNfK@`WLS{B9Hl>p2R#J6Cms(mA4-IIVD5qlOg);Cpn%vztqY4NIw=`LQ{iB&^7#Wa z7a&uV)>V||WdnY{zt5auLkdb=`8s!>hE*dQPt81kI ziO)fk1BII*_SGJx{lTuOLY^sHz={3|Pb?n%Yie4$M&R<(ilKI}PV{R%0}AWba;7QM zlhO+kSbd)<)y`7?fZ^f#8IR88g^8yYJUP*(>zlFUnxzNtoZYl6N1f{El@=@+k}>b# z?4Dj;?9= zS6nw@ob*rWHR+$@M%;ibXjl5MM&Dm&83`?45etEsp3Zfah6&wn{SbZWiSl#g2s8QF z!b4X)kx8BIv0a|9d#)&qO#jKn1JeLSU&g}PO{iQL9$?_n`%N@9{Doli;kV#$3Nk1^ z#U4_1qX>;tNcxH3ovQtK_!)Q;noSJxssaap?qI9Elad>s5bi2j#ytCs3 za>OCS+>#mBw~`ecHs)WC{zzU^cx+5Je#R3lToHj6;g(tCOO%@6wkpq&GX4R1 zbtJ>0R7-sa=3topyX?tUg83mJE@(3F#$*?KY=Y=`;PXg{F}hsA=r60uXOmHR?c0m~v#F!u!V#*&AI! zFCAz1AzPG%yv`L)O!?wt1!(?ra)UJ3BIHo!{9Yy?_5{>Guyf`FChX$Fc_I zzkl<0r)IOI1!D?xv z|1Xy@#d)U%ppGeWtaJ{l2B)wBCoHNdN?uM*O~xylSFjm1X(4SGMWdi;NKxSuf(5t$ z(yq)xWA3qIH}GW;dPcJn8YKu5f;{oiO;wizg-JCFwS~i3j<8^y&6ATjN8`%xe@W3ZTPIsDF&xo?<=iJvK1bU>vQqQpAR2|98e;? zywn>Lli7c4!^k9)D%NBa68o3AL)UnD;d+hQ!;L5&d5@<^J+vey>4Buo;w7UeC9Ww; z>UC`7uuab)c08w7zw+VUfg^7(8}2hqI@xh>QPckSg{{)#cJ`ZoB^^z5>Wnx}rQ)|t zm9Bv?Y4QiD9p9(jwKLujJIq}-HB>Ae=~c1k&Xe~rE;Db4B|o4OT`5J0Rv@-mt!atz zj@X>-1Cp1zVgT55j#C)|HMfmO@q}V#n`2Twx+XYdZTw(Y`5GfTH>Yk!#zc-pZW=AdnU&ctSGLmPRA#Yl%*st2 zE5@3|99PQ)1!p??$QLg?_qS8cq3YGk^9J=x+wtQaLmvIzOJ(X93s+Gg81?GDFTVN4 zi)CtqLG-vQfkdF``vU)J8+thXfiD0dYXo1A1iUiY;}P;M1b7IG9)w;9FLlWY2N_j$6R}D_C#tuFLyR zQg?8Y>?h+f4n;=rDT>*O1&SreUa?-W86MDk6bIlb(X6-=xcVo7u>QE>DaBdEvx-;o zHejCOiI7E?piCY_R(m?>8YV(eH+fkc1o9v@DE}J~P!EEwJy^lDDl0jm&=M6(WjI1} zhsug1OnxZaJWem}2`>S^DmBPMa~QOGSg}|L3CHQ+J#ajM_k+p-7#qsBCaS65;S<0J2iW7)(J59wVcB6%k{?6%EJ!OsS@Utz_$(y8; zY_=t%V?5*DFrIlzZ{ki!YtM2>w{6Pe9$-Sq>~eHS?^dvtrb=lv8>;ST64@AOhk#MC zHzd7!sHq55P!v@j9C-9X0WZ0+LTk2bC|f@z1F_*7DLz zruI=vvH$QnNO|>oNZOsqiluu5BhEgp6xpgOR(aQlPoGxv0hs4a`qNCWlU_c;dVlqi zTDma!WiF=mlT6^9KFbP?yQEJ)%wpTyIW&YF?FBzULCQyRsUJR;KJU0*`iv#~`OnpC z4l-gG(E_)Pgd|FRRmT4(%sYi_RPEM6;$3%-Z%5%{n>c_iJhrLhpPL>N-gq#SBPHg9 zDzo{9P0z5IZB?7kp52`GFuR8^%q3e+zbL)g1bTBFEEJU4yBB)6py1I-C^!=N&1nNd zCbKBK(G8K1;))gUZ+7rVPAR3Vw7t$6-x$fJPaG&+8+m@w#PTMtSUR>8IWwlE8>A1U z(8^i-@18xi?eGFN_%(Z7r8sxBlq5ZS&Db~Cl-F;l9Je^~taR<5acm>kyS*=)&e>K> zn6*kON8)>1LFFjt>#TO+!OahJ(gx)D`j_ncOO%}4G{JPx7gXF@3{UmqLN~)yN9>Bc zpC>`rSsX-oGVPMHLph6`su_njt$XR&Kiz!upPqdwyjDEi%D68N9r}`S(*JBYcVz9o z&$k{p(E9wnYv-(faNH~R-S=Ja_ctH>=)vYCYu{Y{=JESp5mvRUOUK`Q^Y~KX!uq*$ z+wUr^XJ)0&pP$0-5Nl^v=I{ zJj$bjzVt*|k!cGIjUTvd6KyVeA${ty&7gHGB<#Q1y14zTyV}$4`fA-A?XMQk9G1;8 zp5EWF&#>*jJebfrN6kWh2{r0A9OgK6uv*5?N2oX#x;mx`pR@Uo*GrC8yA6OX273VP`NcBT5$Qr0j?G(M{{P7piqRt*) zN=el73s(VL`SV{oUT6>g%o)xA9Yvu3PritOk*PmT7!2X&#aO|Vk=pG~2a{1WGXR_p zgE>l4UMm$H7b0r$wzikJ{oJv(mqs9+QS`6EILDZbuS@=&Z5%$wIA;~Ut2=)?DwiM7V8y|a2de7gte_wyolz2Y5-{hoV zNoufec(7NxJ*CD7ZahunGQ>M#l7ayb)Ka^pQ*2}^2^dYOPAi<uj~;F1rK7F4-`>hvE3z-Vn_W?n%^t`Kao>fq*aO)WY&#u0N+&ig zJ}Q*7oyn@G$P)Y0@>jpY5>F&PG#&KoJ^YRX^+K*%Ss=<$$y_-}L{UXErgc(E5-&jp znr?_BbPwuI#L%IiL?tQGQxhLhEFNIO&2PPbbo8M$OJ>hnvg%;{q2Ii5`}B85i|$0V z!QOX<^!@rRpKN0Z=T@CRx@XJQI$o|_piwYoJ1MS+k z4@{;Nph^J0Rz&vw*R{6pWnO9y>5qG@xbr22mF}0)L#gr~)}4H_qp>6$<~$925GmFS z&0^K?9>3KCfKji9ml=9*)MPGa_6R~d<|%laTO_^BzGM?4)z`l!wMngf1bd$Dc#b>y zn)D5~h>eq4r8agA3&T>^5wi5Qbc9S$4}>iqA?)E5ky+fW9UZ(72IOS8<1gH;@(K&j zloXa+bBDra6BOoL3kUoHL_@>&^ECv-8f4FE#sp1A{n>?AMziib z$qd)|3UYAtV1Drc0u&k(6_1!N+06DIJd)YHfVjlPDl1-ccwBwGrPxwmkM*Bj&`JO9 zczs)T=dI|h&|7Ak>vWhY=o3EevYFqaC&{Tq z)3qak!8J0(ysUS8nYK5}M38q_I^SDc7B9UZ{n3JhIN{&iL_m^m`s*5hGQUi*X#Er` z6bg?OrWdP`5fltDi&4H2EUat@&_IR9LpUa5W4Rg%4tUpe(;Ger9WZ1j`qB}QTf#b^ z3yJPJRD~)R&xINrsUgCROu=#5G1XI4iK;2pV}O@}KOO%07*Vf-`?EeR$EwxqVsv_~ zH78B)v;dStjN$1NIP~7JcXh{s)q6EbIU@q&-f?ixy=5Md=FW1>?>pa>4E#k(Gs<^oc+1PZ8N16fN=wp54FANlzWFAaH=&b{ zfQAnN$J&Hh3yED}MWOIH7)ogV@}!cEsZ;SyN(m5WYD~`QDI`rOS`C|IRmP8uznuy3 z6YU4j3nT_Wj2)#Thq^tT0U!@=r>Blx9f|3`@u^wA`q~sTeE7h|h2DfqiUHkf@F7ED zuYDvW)BRyvr)4E^ilw7Jav_Gs7aQ@|s+U+3X3)W3FWt2JrdKY!z4Sq+^g^o5V&0dV z1qHkqhFbheojd#ItY@|lQRzNyUi9L?d3B#|Oz?MU#uKs^g5D++Bss#_E~hJT&JrXc zz?^emMMC_0k@h`{lHJLW=t%Jn&Ha_?_9*|MfFDXLc--MM6MEpA;3i*GXw={t1haxc zP`O~@;Da)-23idkDiZUq^f)0+6fq@S=PW6PuYLV{sqOpMudQ0PYG8bpASTE6ZY)hl zG*aHwjnBOO%*LsCJTs=3HujEB7KN<%fvc8PNnxb6k3uS-^=bnQO7TWH*Hy)gvgG8l z85Q}%i&JB8E8I|<5bHDvy5v-s&E`r=ju8y8&IB#)g!{#$77yo#OK1lAl0AaH(6h4> z(VSQ$yN2aB^90#@%0m!-u!JJq(ht2_FagGX;(L(h1it7V^eiZib?`=sRIu_INiKC4V|*i)2yOAx9uOS);1I@Ox3+wfauYF3K4 zOuA;4)LOn_QC(VE-J%WUtrDkDYIq@X0)YDCI7@<^#YJY=;(>PkSyL*zZ_nWm%{ET# zC5_}x+2RxIQr_V`A6&?+38kflYBDbn563}g9u_;~*cxbq6e@C1CRBO&B}a9MFmZHg z>&!U}3RApc!IDO{B7B9g^xk`|r1yg^5$eF`>Vbc3h|%r%WXnmGaS946*%m{#AHL;7 z=?R!_dYl?{EfP$pnC0-+&-WUwd!@fx$VwEwO6D^=?VyBEslcEkgpa6}lN3z`4yHZX z0PJK?bdvJ0Fj_W+No&{9n%>9*>{puinPiN$s+-au%71qGl-(Z(C}l zy-X=>xb4;D(X;8Ib!?q{o3`-fx)3Rmbs0h!^KMx*b`G$h3KiVGf3^t&K3Le`N(YJq z`T??m-Xc>Hm9neQeEFW!XjHi*jq+ootM5tgo!)c20)egr?CPwRuUfLyNo8iMvLbTl z7wD>#prGjauD7x7YW3UykBu=V=6-d>2Mvl# zTMd@Tw#(HL(Xa4!u(TMqUOM{n)hmcjWIp^F%XAv5s*(Aoy|L%plHZjaTRM->L;jn( z(Yu2hvm0`_bA)sevFNaIg4T5+6&Jg&Yy|O_8v!qQUC|6pyf#nEG;`oi7ov(2?tsOx zW$u{H1LI1Mvb{(D%T}Up@bb~XA}v#AsS~tIo6y!hUe3Hpod>3stXub!RwUgIXogZk z%z6oQ`n9kwl4ZuhA>I2=`@QF9hzRu%%$g3QTQ>nzmM@SQ5=@t%DGc~QxEVaeP4Jqc zE{Alb9FSjsl+J($zLMM^QvCIE_uhN%b>{Eb2iB!!>8wMCW-XNs%-qH6SFXIC z3q3(Y{R#O1|M$bvH>XTjkfI*9XHkN54q(mprAzIAYmU6KiOt`%2|=Delpg<6>)oYM zq5=0I!8m-lQR)EeDAT#pyIcQs9D(S9f?ZOoh&EIM?{pHpqp#BEz&v%nL&nrW6Gbh|z9nE=Zz&d4Rf@@`|1|q{5LbefQW~ z(y@Na-`H2D*4*%?Z7cqGjog2Fym_fl%A@S)Jyb3{)5Cj6+>5ufz_Gs;=VK3ci$ultSBF&OH3*5JvSrRY&ov&|RRcDKAZ z(cw&Ty~QfLtM*D4J5(^?V^3o8Thg=GgEmxl+BF8F4JW{^@$+qnKJ#x0Zx>;LPPL%3 zDdoN=vwA^5&Z75q_c;@~T)1b`pb6d5zaIJc$>lpxad^4*pst56UgwNs`X^hT+WSqu4jr1Y{0Y7^+WF+oE2$aU?qR7TA!Y3_<4M?r;FMCY> z>^ypYr$&JXSqv) zJkOTO`5Ya&wv_O*k&sroHp^$Wtud4XmQ7u&@r=;Yy;MG736DQB|-Wj=&+b6p7iRe>0zW&L)D!&`j4@G&%F8+)rOvC}XxURy=?4n#mJfM>!i*&PxL}F-W zkK9IO;HJ||)yaiLUj5NCL14o|7!omTpTvmD-|p^AUS5hQg_f_|cA5JFKL-naH`m7n zI=RB=4=O-BzC3o)xxBqV0Xqb!Tu66N_d)rAQ6f+M;=QQ_1*y{N7hRv__Fq%6 zbo;TFUW#~VpBOGkZ9AD-z}0_ob4dyNou+y3yBady!b zsk!m-lN*MHO8omWr)7?;DG;?sk|%t|#pff(gj0?OGPsDT8jDC;_neTvuR;&>6WRxhYVu;z}Q4(tjcOss|yB*Dg8?( z$7qdB>%TlPefo(nCH$-!{@qcKb>@6!)v8ydFK_+LNon%-`Kw;x3K}$`)|2TElxOd4 znm1NGzMq5F+ilxb_8P59T@woAsifhZH^I;PSC4-=bhbE?ZX%tNzIxlhm1xPGGD9ey)#?$3zhFH_?bxWu38Tp`)Pc?nRWaOu>(v7H@ zlDf9o9vj%k|G|rRTJ#G<8O$^XX>W<(?povI(@G+4a&HDuP4}|f?kLjO$)v~`g&X*S zz!hZRIEaPq;YHFl4|uw~M=0fi$Bt7-bx&?hoe~UINb3*u)8{@Rbbc6V9X8E&&~9{n*uB*L8l|I+P0y*hf| zNK4U>ZwhW$9hk9v`s9A;<}&=58;4Mm8R~;!)xYHW6)Fhbu&aL56A>mLqh-iT)S*Hi zVh9wVw0xuvlQ9-lBDsDgKH@D7cZu={LF`@K&_guDLmGUhP(n_=q-cY(TUG*b23?^S5*O33rKQWp`|kc5{)N;`2O~X&znq+_Ev|3VnupxP#M8lT)F{tXa(Ls#n=<(4Vni86uEij zxr*|XIyD@2Vjt;y08EWu4f$gMAVxChP$i+o2Wl3vT ze{-rKhD#EJ@$K`FxbsVGu2WcMOEg|m@UuFOGA&o#{-?NP{RjMKe8)2bxiy?IQ7L@~ zEfdOxcE*?_JT62j^u$+(_uY>$)saQ&N+fmRWYqgDRx#?5Qhg_K4@cvaa~1tzS?^#< zW`Xyt7j(Wa8^}hmNx-38$$rhAWADKLBXMvj6bUJf)Gkm>Ad7i46SLo^49e>yI{B2* zb1>K990uf+PH-K6bk+q9Dnu<+IR{;@1H7{%dPl))ptQ$`M*zGUTr;9ez`u}u>kM>G zdt?g*8%I+e)b4ngzX&&rURUgJB1?hOLAO9)H9pXprr|v~f`#QgMR(BzNda6c;P(@r z03L%p=H<{f(h)kKOoh=j`b@ino(y9E)c&-jn&BEcOpjEmQv41l;wO9}o`;I#a@++C zlTUGFbVU%HM*z_j)J`r69t!#tAQWWU3>5J`RR9)gdB0CAhvqY&gwCAycq!YK3^4~= zgvuc}i__2?MdiRTvCB_ZqTYCjI#r4M&?vJKP&BlM1bzo!Ovr*hl!mHR9HfHCSApxH z_%)>}6=iY?K;_1Ud`+soz)RIq6(jc}KB$j;D-mGp)GFlBi{i77)ILjGfMX*QP^lu7 z&l(5Uruqbjqf|dOC42C;y!70*CHgVZ)g10+)+;q3rPx=LC^ij82I1Ce|5%%_=(-gn zxbM_f6&oKe&TDW)Mnrz=9GeeJT~4&Bm2rjyl}4ACISiqiVXrP|R(u;|{6mGadqmF3^XjRN+iBC;*8a(j{I;}cU z@07mRjC2VJi8lAJ)Hr=VmtN#c3XOwZh76tEVRBtO>l&%?SQ8V{lltr9QoY8)prCou z(8rpVof99&zo$0yyxyFi#bTw_FYdbQi@S>F%w;NV(uQP>AWGk<0n_p}Cn%M=l&#W1 zQ?F8^1u*a8faiGcX6C%>K4w4c0nm)O${1f#2u;08%PBRg8040<3Uf<^7?%ksjlYiN zigUAK)MicZBsK!MG5oz&H;Abliwno-ox*RPpL%?X(#a)jVzRVWpmSMAb2e^;|)N>Gz+l?B(pIZGYpz!&J^?7uV3IA#fDWGz5!-lJEpLB;|`NorHQjTszjmC z-ebKXp;DtqKHLSOI69@rx=>|QXD6fq?ta z-5z8G>m>ry0eLfV$5^$`?5;@f6{yy5`LRZHqQn?YqRFDyXcJv_HU9u$kEVOCO|l9r zGPd;AyA6iW43kmImagUdZ_S_Xj!Uu#)}(89BpZ5f$xs?i(<{xDYZnP<%WLNGe%~&u zMWwcF>dSGPjxSq&{P^-^k`Em*VFd=2jvv(TNui+u&2AetQZ#Ze^;sFGR$5FqCvh8{ z`du#s^Pjs_ZwGu6VGOC*xC{(QwLV`|1K0^SVH%s+ssr4bxwJx~&e7|W($FlC%?8uJ z6}p(fyy8F|$MyZ7qGWMd(e^1woB-f1t5c`f)%Qzz-EQBPpX%Uwdt%=(%Pp?*dDze) z=s&SGi-0^1XD9X9Sv)Tgqgz>RGUTK9NQ_N9Lq83GlELp9$zvM%ysz-gU@o*P>@ot8 zBvrYXgP*h~k1U+C^6S?vCHzG9{bO7&w3J&?jaj zO`h0T?TZV?l6?;3_||BI3Sl44qHHcOwkQ$U=jhB-M2LSD|0j}cLI< z(l?ECuyNw1O%tPQd(WNgxDj3x#L3bUEsH+V89N2YUfIe7UX1~7qNg`14158Zng(zOWHZZB`0%GAORjEQ%lLEDZf_T|T3sl8!I;#U` zLC?`F!N%B3r}6U1%@mY$MVS)1%M?`#QxHb|q%`cV#bNea923nMVrzz3v?}Ns3Lcz1d|VaGZ6{zYv(1C0 z+pqM%ZPX1Mi9n&bNM3gq;|L#;TA-r{g+kJ|O$amzg;)r_FfI5sH8n9)NDQ}1jp0aZ zYk2S8a4Y8yvu1fU+MIZv9M{m5?SZ7OAgFjHo=>Bx?N1NlS0B$s*YYK&MZ+^&$qq(y;2J`Akhi`c2ew>|nRVJ|Sf!+aP6 z1uA_3C6dCF3pjd}fa9HiZMXut9k>Xpb%|a}7jksHyp5k|E3{*c{y2Oi_|PAG zh`OFh4RBc&G$TqC@@WrJis+;irPD*bRt2ROlCzhji^!QyY1+f=I%C1(1tSq(+8Eti zlHSo+GH4`rLZ(DJcgdJa%=4rhKoU48cD#7g_!Jcr?WTl_Jqf3{>OxY?6EV_v%-xQT zUBX^UPkbEd+B+0ok7kMsTAXo&M~7hU^b)=q#~N`GGPzUHO7LiUnVon@I@HOJ-Z=_6 zDirXC>;@!6f{D&`N1+2C+EK9_`LL3i+Z(_!_!&XEfd~XsfPsT%7pdMLl?I|2w}EMg zTKqJ4TXlP~Q?0%AR;}8pcRBf(9XpU=*4aMi(;@xluMTYQmB9vauS}aUf6bctGp6Ou zPE1_?*wn17sgJFn!PktbDh-XS0y`;{vcC6PhqjmsMA(v`xE#REiM-7hCt#Y66{;ft@pA0iz} zSjM^~tb=&Orj}C=FhH${=v%+Jm=XiYNEry&a0^Th zBfXyf>(lt}6&c)%y(v8>eTO@|xAJyoIC4Z9vg7-^8t;(adGcQAk0)o`^A)eWqB?S) zQ*`rc;4Q@;&B8y9Oe4?x%k#91=@+#jfR9jyt@?H-ORah#q_>7ARkh39fB@D3W3KC1 zv&<;a&PF<|bGI<`^2w7}d9$oZp~+O} zUY+{il&BYt2mU@3DjYROmt#gF2W44BEOhDDq81nEf`JhYWw1aXHH381y+hdo+Nrn* zGQlg@BZi7}u929YwicQ7X-uy$NOoFff3r_rJJrtqMjMfes@&YFTw(Xb8~1JAcjLtB zCDUgMmLV2l_Vgvy?TV}I6+)DKArj)lxMkb-GKVQIL>(R~uayoQSSqiWaPQozjwvmWi`5;Z$A2@%HvTz`RJQFbywZnQ^%PNos)tAUBF@Ka(SRW84X)B!CJ#z22<*6 zFILV6JQ&l^M}Q6(c)JH(8`__uVljNax%qswO+r-n#_nxVZllNzLw7H&?od=O-96Om zbXsXk=-Lv)$T_oU?p$e+)PA|jkP`P`MC@VW<$aO9N$Vf_Zu92v9$KHI@}zrIS8hh> zCproGM>Y@@;Nkzjs$nMc*boqi&}q(}iu(OxwOTtA8vYwi|HV6pd_H97;{N}6O{&Vv z+WKw$`|0(`$?H%5eIwCdqWzc4PO((~o43=5~p6-pOh*OVS)S?o$2~{+?jdTqg(ywmH0_V zD%`WDkb2Y=@4*P`b`9v^k4Q=o4#_!czsI0fAd?iXC@_o9#e0#hy+pL-V29`mXdqPPkfAXtkqjNQ(vnVrWf-TBTXy%VpThV+J86Ln zRRp#Xoy1s_v=%@m47R+Ohj8Q$<>ge#i&R$ZM_w6-#oGB=d2fN=puxe)0#QAxvb3tt z?34ue^qu+z%BH$Vc+`C9wIREv=|ts@$wfJXgfPG%Cg$}+WMsYTKKgCVO_kpDSCH5n z*DH-ZoYw0H+U>qBy;99p<%HK14i#CrAf-58b<^}83QMISvAK0k%SW;FnwhQBcCpDD z?E`46QTr&Aji3|xKw?*rVpx`w@f!#AEj1H04z&!L1u};mB|_q9*O}dIf%q}x+2Err znV;|_NIW5zU}}w{6RO-*6RHmRLV;Rx#SL)}rWC7&h}cK_-4AbHnrwAW+coDF^$^2# zBO-Nu7op@XQJ@X$hVgiuNT$^GE*c)VO9#;?@nOf$#J9K zcAdcO&UtQNnXqe`S-EqLWJu4H<`178%;gmQ$ILyD!XBEoODLoI%RG#1>xFj%ydpNI*<~C9GFl(tM$4k0N>uX1e^R$82$DfY?lLM-#^|M8<&5`68_?lI zW}+zONRW(_aFD}MYD}OJQ}BB<$_SQq*+!ufh5XaUDxBptqSQY3z=64ovj&epFgGWg zTZWn7!2B`N{S$6Fe9V^`4k@*!YL~GJViIz;0siMG!tc|X;FCr^q9f8_xFK39z z5-I2WGH22Jku|J7vluFZ*S4ooyO$OX$ni<9gm>i!MAz~GJ}qp4=EO~Pa}SvReqe57 zdczL;XeamLz`=%~C#On#NLyEMNr9EkdUd?r>nI3mnhinTd_i3sNUt)y6hfHK+!rb` zXLcy8qjdwaxZ47?>pc0=yE*06Id8mCouwWT$QWb>#q8{RvOJh3vil}EG_c8|{0VqtyR!Zfb$ zil#aV30s_eQu;?G-UNINjDl>lDw0u-0?ouQGHIr^Rfa<9+R@KVF55$ zL9={*3VN0oWRD^8lK`fee&v8#z7vuJ@%hSBp1jjjG5tlyuC>Q18Vqs$7|RH0l1ZNm zcn$F|c17tRF2fKn^08NkuC~t5i_27NCz>~nt>0*?pJm%vf6W%dgjK3*wLwQ-N`Bm& z1EmF$*nf1suS|32`aPO5UtWmc96wD{?#r#>m#GBxbaj!3do&}3wU^WuVW_?y8pI2s zTz{EnS^NRM;*w%=E!$ICnC)O6Cb%YU*N&b)YlL(syKls-rDL@>OpHyH6sk;-CEeXEy{d`^M~UA#LiWpps$zpKvy!{UCw86PWiw7no zP1=|^!8E%nQV=DC`{xYobKtLT=B9rU^MRz0!mkt$p_Ww?B37WOaq4@$`j(`Z(L4|u z7aU$2XykeahldZ(`+yr@AFJ9n>AhtOq}`zrQ8GB^mQ*fv?g2RGft&C8cD51mja~(1 zv7Mp-OGapv@?00KVgP|-Q5U9UB8o&0sS$u?X_TP|8;v#u+1bLLF4)iOV(`qOG z_+Z!c5$&Z+J^^45xIOwhq5%T9hKM7@C1MbZ>b|+VoTKeK8Y0u@9{9WYz}&h`iDnS0 z1p9#HPkMre!2^Q@b)ZdE4>-K`c(s1Bwkij^n>C^KO7(@AnH4X9D%FNwGE}8QZ=0Ak zKsVaD%RDF}FhZSG{l*(P)#W+TyZN4VwE=#$v*Ot4NfV^|$IL$frkh)qoiq2q_`z9= zi4aTeVofm3b?k6OJ{xI^&#BsGGG$s4rH^Pm&BYomHehAXa>Pbf3|N%&CFdmlC=^Bp zZ+30l--!od%UJJtpe*)(UenI&eMUaJ{~-y3b3542idFMO!6?b2KL*5!Ij$J_G7Sr+|rgT<=t zsL<=Q<``~>G#0^__eLIyF>AF3{@EC_HF6;~L6xdO(3hF2gbH=ySZWa2+&dbFKp^3e zwTe+xxh{U56e!Uk5YTuaB}C^z2aFt77)hW|=r)j$!9=k1^^Cgqj;cXLuOmT+^`K4t z++l9Xd(sZG!DMC& zq&w(71cMWseA~_!yk3%~qR#;naQ4Kj;5Z<%w`pUifwy#_ugmdESS=N;VdElD$UO9S3EG< z^u$wyF14y!M7QiyqR!sd&7JEVJjVu68>}5{r%k;7QkgHVkQADXZ z8=k=_bYU2mRIwLu>Hpw%&){~rumKQyKkbyHtNsA`x-_(n6?TPamdyb`avHBdMaWsO zt54Qu4p-qWPhP7B zf;c!c(gu=82Sjrs^=VKnkxz(6PJYhqfFn&1ZtFo|V{lk7IIP3JxOp-Dg$;}AhA&y% z+%e$T(q+f){QQ`(@z}DZ$FR}yvGhOBT=(|cwQpbd41cdAAGJjgY=W z7F48EVCw|7KC4`_@Q`%j@Rl#?a!2Y$yX(H(a#*@>XrZP&i!IpCZu?U!yMarHK0e6N z(~Bq3GZ!yrav56W2OndfA3OH>F)5v`W5%`T+s>~Qbc+^_KlJwUrEeab1kY#e#%sW1 z1)*?#;Vn+n&4y`=>8%LZ6ul2fRa=XEk^i@E2CN;a!ad zLb7BsK+ZYv2%?eA~Kv}WS~~$IVP{89HcxWKO`4m{y;*=fr#%bZI^yvS|Imm zr2~&|+VuD)mZcZ;>Dm6JFV!%e%N3J6Cb{2B()Y<@u$s(tgI-N9 zYAPLnm)GYB<)v}Ukzx7_?)1Z%r`X|56DMriG+|=o?u6{LUY@ub`ylx)dY7v|{EuBO zy=x5J&t4Pf>6Mn9U~?HP@q!^W-hrIw@fL$io(saV-c6`NQhcNa(eFK6<(5t8fviTe2ViJK=*+{_BKX?>ElzO@@yBqSvF zNz*#g`_dQso>?*!OO31{6cAu<(q3FiE&KoQp620ZwB10gn54_f5&eGl37agIM_uR9RZ^068 zmiYOw@^LW?KR)u|lLbf_jS&FekOCpqT;|9%GQOuQbSsl8$8G;idiH?_rDs3iJ|VBZkLUMlL=mwS2y9+vhCwAg2mVXn)s30E_tpJkl$y z*fSu%FhyERIvs|x90U!RMSV_0WD!gih+;(WMJf=%Jaz-H^c2Xf2DK-8TR^l&9k}3@ za?<-kgq;!0Yef+X4#trn3C^E&f>#~#I zcUa#^@*U$?-+p$_eD}hN*#47Q==?rw`4Z20{bwrngkfNxc=j4&JIW*9d1i5sSO+*FW&%vPA*H>)gG#i^0hLJ*21Q<1YGUj9u$uxPlPzLa=~j;p(&6w0j|L+ zS^q(P!zq4BFh?|wXqPN68A-trBv@WZOt~0*LGpUX%neqUQlCHr0C5Y_z0Fa9fobB% z!=ooNa|I*AKjMjt_oWnoH<+YZzIDfBUOJ{)wRz_x?uOZXVw|AwGx)7Q(WgKmaY(sufE+i9hOTeI~Wzvk|}?8NQ&OYpx(+-~s6w>BC6< z76Z3v6RTLE#1*I8Xj~zV5_+VUWov?40ZdQ`)3ig zD>3e{*bD1=6;7)0mX&HCJ~?{D_r2%3!Ka(|&r8Tu_sbqTJ;Au=dIpjraHH>dSNigj zf@NRW#740JEOVmt7Xxn|v4qS1U0*eLL?(_%RXOvtPxs3lS_1FKLO&<;PUBP-y_%mq zLRXfVTr)E;{?$`HU;V(7Y}}%u(md(;^_LVM+&8V0#-aY0&r)I0R}c{s$Y&EKQGjz| zFc4@EU|0#>8?duTKq@c*n$yrK2BItHr(uKi#^;YecUbyrX6-eCa82z@W;^`c@zv7n z_aqq}kbe8=R^qWALW^|ox{6UHZ0e_fW>ZV+E3cF8L%B&lG2y*^3onlV>?GAh z6;vKl>Hz=(uK@)_A<5SwXz?m}ivrRK(C1|69|uod5tMf1oQo@D2Uq6FA=L|rV*7?a z-aPI80(N)FXVSS7Pu=tBU0-LLC%njPkN=|rsYT;lM#ZIvLbFHb)y}A%J8J&k)vpdH zy!gVDF-vb*^H|PQc7c0WeD|i^f8fTJra!*Haxu&~K& zd3Uj4$PD=Lq^=Jk;J18h({2%8Y6Ds~_sB6=z^7_BUrp?G6 zT%8{iUzO1R?6G4n4fFL1>0@-x+sQbsIx~uaN~w| zd9+gKA|&h41|$UX>Y>0*d5PJCqE~_#2Nb#j&t^)>Yal@%pFk=(qQm9f+!=92Mh841 zSWLm`=&O{olfYx_X7odvtfHF`HL0~aU!x5w1^AiMGf)EHb%IKE6_qZg`_Vx>e6@1% z-b2TZAG~?d;_{3bp{P(~mc)XYQ^T8g-?Sw>MX5E$*wZ9?RfRp#Y}9JXt3<8Q#97o; zRVJ53uT)i5T3iY2#hmOBb?B0DEpqtnIf zHLAHY!Z&Z(kYEAn({H@z&V$$Ml#9zlp^B!ay|cz7s?~{%A2(p_%&EmCB|(%};H_S6 zq+DWcS(Rwwj0TmqvdWZX5vwZAu7trW7S0(_H(^5E$k`rMg4vWftv{>hwl~f?w|Czg zCS5_Hn&*`_&6-g?ux?O;G_7CF)(0oQuxsbeKnjQS=W5Yucy7%YzsSdmLWT!Ev3+G(b#j%Fj>TBSu>f^ zpw__F0smj++=867(&hxO&!GQv`Y@|iXYj4uzI)T`@{)$@R_&ZtU{4vVwD&FQYmwg1 z8n^EB%;|Sbsf>#>R#(-GavA!}UQpRrsZ6q(f+PCnmycgQv6sdOggjw+{)1!E-!je1 zukU5hTC;C;s5Cr)iK5A3InI=)RK>7+lB)_bbh=jWP@7HX=rcB5nOA?)_)$A2*7Qo$ zaO*4G0nXta8BFNAV*bedf|`lLQzA#lGi!P#y-z zl9w(wls=@q58ZI?bE1^#wBlgX7XKVt@AV>*=n26tghev}h|K z49Acbsu>qTZYYI_ssb#nyBT=J<#h&UrmM7CxM&D##>LSSBX0?cmY>wwAlHA`)f=OXtB?`4oRisQZ4=|BwuRxG^w2{Z{!MGYh`{_h${bV>?josn9j zE%O13HdTA$f7dKrUr7PbWp}i_aX0z4k>3ABV~{Kz<$04j=?Dpb;8r?+FhzHU z-72GEc6M{Q9QHYionTo|*EUFRa|#+Hd(T-CE%&e%V`MQsn!8EJj~<3v{KOC(JGYlk zTS+PlJll(L@ke=%@=}~dR0Y*tAx}4P1V41{3Y zb3@UnR7HAX#~FtDqpEy}jiG8i15RE?NGR0)(x9MQ3GA`4H;@>?i%F*Q6un*M8VW`$=60JJjrr3({3V6f+6E?_ zXIK%zv(tMgdB_cUh$2^v;LFJ&wo?b(l~JYZ7aDC@IueOP0qa<er^N)+%bc*@!y_d=@)A1hV&Y`*M#|WlEr?!!7C(z4)c>-EE zpq9Zhrvcs%0%=!;NKYN`75gBWmy6Ja!2^<^UM_akntdtFmX5r6)5ft0u{j5?%`6>I z_8Ob^=9_E;Rk*tL1*t8+QZ&X2yojLM7*3UE?-lFP9eL!k$%uQTM~$PkXW<=RUElQT z;DW~SBP!~LDB9cdLiEuuqtzg9Xc{ra;Tr)D(_ z8f{rHH1A@gRZ519o0R9v4Ahw=+5h5r*Q^hr$K^pAYa45O%)_JW!dBpq#2?hMh1s_ zNS)-d1Kf}l;-q2RVAu!lE@1XRlIuK=%E9l9sZEZXH!m)^HfD0b9gq&V#`}VRPuER2}!z+-;9AM#K$N(^$dr~Cf#Vz za2h}+P~E4?x|v+~@r{7BhipAjgAC%wWFrj7Ir%bpVMBI`Q1V6Rmv&2a(w_6W!t!PHqx-(kdM)E)4Q#Px zP-b~U!`iXZL$g`dAA66kU)FZV*tHD}#*n6!@*Q>d?xtGqR)#);Cnba`p7RTDL z4Q1sG+(W%5$K@2jXmcy{0MJ0?lQJ~u#~R3rEIzM7x^I# zQlrkL(`qx)(=)VMZL%)2K%*(RKo1+c7JY+ElPhpPBBke;u550~+o(>)t6n8i#jmf8nW1XBHhB>5lJLC~XT4=89`r<8QxX zqo(%VG->F%p(XKvpA?60yrrwZ%D(kcH2MUE0zD1Ak!E1(kZ^knV785N)rA@bqOc%O zP!I=&sVE@{{0sZsTw|meq5(^x*bM>FMr&&o+{dHyl3e#>)E@J@7ph2zpCI6rl)!;} zbZJoGMHSW{k6`f>o*oHDoqQ^Sg`fw6_kl9+{lVYw+IM01=shnk-1Oy;KP;4Pf8|%w z`){vX_crtW>O5O4g}6tS!BGCqqg|HrN0IE}_;t7Y8@Ic&W3<^nELwHL?hAVtzPM-f z>iO5*)3WYu>3vWS+~OUsT566+u-JE**QM{jl$JF!1d)`aqi?&xr?lc75>`tm9zoE< z{APq=n1Sfb#C?%N6Zo-hk325iZrd06icOGWI__c90jj(4mX42>@#7+Kjgvd>V#B%h z9UpOM3VF^}hM^NAd+v4UC~`(}NOzE4kg^8SU36W<8;LqX;upt~5M_!Mid`J8y?hPsg=j2!n+uy7P56f~wevR;29`yHc6Wcp z7?p{+Jy{-iw$DD)WbUgnRVP?#tmy^Jq>2%{&!hX8T1}V#BPJFihc&5%`_^P?;+n9K zze*Ja{BAR*{=e$p13ZrE>KosCXJ&hocD1XnRa^D8+FcdfvYO>?%e`AxSrw~V#f@Tt zu?;rW*bdEw&|3&4)Iba*Ku9Pdv_L|PA%!HAkP5cO-|x(fY}t^!$@f0r^MC%fcIM8V z+veVL&pr3tQ@lQ(H{B5hU3cf}4x7V@V;L~v)I?6_*wq6t@dtRqF(&Zxdh`_-87jFo zg{9(bQc^a6km*oxBtb82j0+|3Gt$9d#X?J%2b?W%t;(wOlfeAIqtZ25;A4nbqKVe@ z8qq%asL^OLI8WZ5S?G*P@uv8q)`9n^>;UDX_ULuK%KXB_tZ0`vF~1;IzRt6IISK77 z-|gv)Eyz#wx}viZ3-c>|-7zgy^wCu`W4o?X0{{rKZ1(}3OoJ%xgbRfJ&Tt)B>$;bt~Ya)oH02^A> z?zHL{FI=YWUC4L_u%Zs96<+WowQSBTzrv!*aGs7Lwv$2y=zHr!2B#q>)@n^jG<&zc ze%{XG;hsiMezkXY7Y&E#ncsi?kFPxOhr2$1aeo!7dhU;Gm3R31ubRC%u~1x$o<2R= z8k`#4%yc`wIbK)1ExM;C+7=&Q70n)*)D%-t6q_iRE0U+rIPYg$_ijm?=dI57%-;XT z{{DGazWCW)*MH=B>?8TP-^D$-<^HQvZBbL>I~nhcugb8+Us*55zK~{%u8P0)+2_6; zKQ$`angE(21O97%3H)Kw^?{5e3Q?J>K!-R4#1|JrMzTtP{cS}&H-*?hL0I&l<9B)i z6o@xu<10Ov6^e?+7tRS`%uDbl8>L@f`0%!E4`2B4(2c2kKkj|(ycU=)HYFA;TE8$q z!RSrw$;uu&5M2;nyJlvhWBAIBoSaoVU)Z|&#fw(@lk>v)QC#ne4`vi5x*f|iGwWM( z&Hnlem(96g&CKF7mzmpEY}>YC<+g1 z-E18(f+jMBv@km*uT?$Ws`}>>XgO8h2Io!Cra!F>uk%$gXCXL2%;_N?C)hp_*NI3p zLO*9c^P;nL+SwtN{ng&RU&-&_%08v`D05%sR4GB}+=id{&fc$1=bESTv%dZrXyY0B zl{^}LttWv8RCRvzoLD`v1a|b__0`w<=ggRC@<{)xcgob>IE|eDZEy5ZXQ)H;UvvRJ zdjbx$K;{Ty_n9R3hq1t>(ZxW(1Ldb;KSs(Ir|$s|xUMuAwG~zi!?c^=p=Xxp=9N5eEhR^|KX^olF;(A#aC4bl_-Q$^6);{6eB9CdQM8S1*_Np2I_X^o_%P!ZYABl3X2mGHCDR>zQW zM&Suv;SA%DgXBtCBtD({cutV6nQ`n0z7>Datx)gle30qL!MpT$DK7KGg=;Q}xGrCL zhbpgr$I8oHkxSNCrWGK9?4#dNFioHy99v&Fd2%5?fZ)kv93s_6;?u<(n9`0*t40`| zB(GDt>P$EW@i}5Ty~yEd;=6Jidwh96CF)-;PiHsfms7YL@Sh4?@@vou0_@DgLsq&# zhhK2HffFY(<(4WC=bWG-{d9<+MByX3&V*<_x!eGAnboY! zVK$59QoQ{50z>REr`aUTlM(s=hgAsum~KePrdLx~Ny(-!FvJ~G-=7XqIVNI9;pqII z$6`h} zUU)nZq6Cr^WSIYowj~UDC{{Lwnfvzd-?yE;CcnZ0a`CA(tXe+0Mt6$8THSy5Gk<^P z?*8iW0Q+#?e&O={`%X5q*H{4mUmH89JGBO)3O_&wHUI?r!jI1{DLMbgtO5wHLJg~P zGaEJlV5LoKmoBp`3*P!%#3>-bN!W00}QqoFh(U5 z_I3)fCvSpLkO+H)?~@-H`}}!1@Vqe~6-Nv>$hb*}RUVB()kzcIXv>RX!ILKas?#Y8)jb>rWA^~=6v($U zWv7;bzCwQyw=J5D9yuaR>)f;J%XMt|KlfcEXDhZ1Mq5|NV~=fprP4LWRr$)+$KUT=ltlgu{Ty{aMm#cPR0)3*R$@YWTsR5O zIA6&3uq7mxJGM^9vKoEz&eva;clwN0t5JN%h%MXW@_N4KSGXKsT6H43YU$D{@tvxr ze8cFd?$owzGFd;+so|5iQjSx)d+x!UG@i&t8RFUl2M)N;WFt$Gv>s#A2-r`dRf$Bi z>AxOF>X6ofSS6jCQVeH>63_Bk5f4s)J_ddop~SgAl^4$0uxL_c;p{9-qi0y?N@4$dG>VPyZ;IP+7B1L zH0+AXb|$CfMJ`#pILf$q_uUtd_-ge+T1HGIX8whfFFttPFP~?DOJ@u`aOZFC{&3Uc z#a=jNOyaR{(}54sc%S$VvZg_HCpz$Th0GxOa8#?DCEGdhE2#WZ5~D0D1?v+*oGL@y z5~4St@wFK#p0gJL8!tbqFgW?1{-==hxP0QN{{E++Ft;7OwL)25*Re+~}0H_}6{CX*0oRXs#@+*Y&tIGCWw(8|;cD7%( z`BrA!|Gm`Zm6GqX`1)k_`wVMT-pgz#XJ2RMzOIw+u3x!l?^F9u>>b`S`DOn1hN7`w zU@^4~_>H@!av%5N}n6I9m zvS)bjSNp!dZ_o1HYhK1z(VlUf-X{s&m6#W&542T6n!zXlB-zx%Zsmv@<^mME79>ML zJ3cXrLWL~$buQ;TKC1C5o*G0`w)>7%&%^hp`% zPFq|?O75ft_f)HXp&{OU^dVM<;wBa=KYGqq1O1V8N|07y+)a?xn6F!hKB9F>;pTuu zgG6>AWXypxT=3$F|H{5PfuwtsIfqT6p!g_fblgBT7%}xo@&{5J>HaLZjs@h9%YqV%e4vbA=;aBYfUvbgnw@=pZFuUNz%ud1nDwW_*iEIp78 zsneHMX_ zOssGM6bn=xAm$numq;aA5H6YM&=B$gPUVSqYj_0A35IkspBaRNOlh)^@*l)_*+1`L z!t%(vaBx-6*t5)Kf5+~Ue^q9Vmj4#xvhjRVG@E003zJT~Ab(+ZyY0;SBD;<`5~t*q z`YYmL8HL&7%l&ydRY_6&al}`hiH{qPhcZr+qvu&HZRLV_`A)#~k&iZ*wwh>!m-}4xID_ zG^|!*hXR=*3CtZ5mh)o)CdLgc0m4fdEPG&&LCBw^P{FgO_mH~-?9zsr#KP#mvO2hc zvxrHAjG%kK*wcGJjUx&SASDKl6_f~UxKWN0g>ATjcg2IUFv4DDhIegjnoVz(j4U&g z86~scmKM9#o8d5-jErZ*FY~#vuc(+mH7P|el=%H6I9dNlEq>- zCKQOK&1)^5DOO{2RMC>MI;)}kUHOZ5ySHYo%3v(oXq_V50rfescC*N3;p{hNyS_($ z<_6j1L5esaFF)`iMXdS*)BRx;MfGCI`>FhUYz4v5ql z6V~H?*!H|}6V`n|7DZcb6R+jmIa+B5D*-w%hIi}vUr*BND`6?@Q1GX~hzUw=5E#tG_8d-|q?Y7r{^tJ9yvIzVGg7UAc>DpVJI{$37J zKpTy)c84=_2JI+igw)j%EJDmdjF=*-sZBi{Y5Ne1L-ndKJ{HihqBxqi+G{X96iGlL z|G{@8Be)RJB-ucc0UeJ}_x-rqMQFffI}}py(;M-K+BG>`$TJwnFg_$_(V_dU zLeDGQZ8H51d)NtVcac%BMhudDsp>4h$Wvc*%4@ zB_<3{JjklBxfQ`oWI|$avv5WXcfRUy;5Gb@BO}I239C$V8ZsbNLdEKfQiTN%)(V`vnnc%4~>T=X>a7EQFGF(W|S5SHevO_?5Ko{=$M%3jD)D{ zgRAvU=plb*cVtH$vDiI7+ZVNeOUnF!A*G?{ysNXPic)d*;@O3vp^l7r;epdB;?oO~ z;?y*vF{5l^s_1`H6|*O@bgGM2bJ)b59V$;XrevjsF4pc`iDl90@lh#JtZh-o>?o5d zYIeq=HqH|^8`4>|x5T!IS#D%eZE=RGdGV8`EsjD9(N1%LIS@VjeEBG)kpFh0{8^hP zJw;8yiZf29$oLm!1Gf?ltM2PuuqZx{B-E7iYs@JhQQXAA2mQw3r&xPZW+JwBFm*)p zlny~C5zSLD`3o7iGvs22^zN_>I^cC4q*_4q(FB3rQ`|0j?2=CMIf5W2Km3toWM!vi zlzI=WCm25bfy1AalAaOtuDWsT+2dnRS<|d{TCMtOTt1GUUVG81S8Zwhs0QwPHSlL2 zl6yOPQ0GZmbFeV0cu8}`dWEfdIH$JCpPo~+ymb<0&)DTuEJ{tY>h-wVK8~Ayeb=g2 z!F@Wz4|c=GODFXP0G$2^7||CBNkB(Kevkr?=O9%lQ26Ma(f}5Hq)bnvvkt6}G@~@5 zCpaQkML$Sj9Q}2!bu^*H27(Y&q1#d!Y^YE4CPuN}&a=hXR_)?K$rrKtYxmE(`Pw)p zdhD|ca$}N`J%-q6Dd`n)9m^K(T@j;qNrGi#Z}EI4NT$cmQqCJos0+Lpu)rd9YxVMb z{q|J3!hW7)oXb7OYd+RTUGx2>y@&KXZBekLD7MHKhskO1B-JlWTi&yNZ=+|0$Eu$k z%}m^J@+>tyP^pl4lir0r`Z&<3I4dJT5Q855Kx$qdKm#EG;>&`pqBlw}67LtCL#LKr zP^n6%fyx4~<*FiG1V-UfAAC0&yp#+mgZ~~%Q{JqsuAZojX+>h9)otd^YNv~T;V|kw zjnyf4Jm%1wlZ@WA+aFxF>u}bxu>V$;T3G1A0dHd{&m$Qi&%i$XYT9{E^}!V4#yOG@ zxn-#*#kEy@H8v^5;jNVaaasPNc}0*Xu$t$x(A-sHcNlC;aGKT_T^V~)Ry}at+B+@{ zjds-~GH+I3hCelX>Y9z~a!p)de>>iD{Mjp9Ci%J+`P&&nMU~C)1Hcf&Ir}!q*G++s zxLxQS5{1Pd?SfIV21sPH1yE61Ks!KUYfG?yMm_;z`P__1pOuD?$VxJ=s`*pE`x!CslJ5wr>oJ+y}lyT%s!BB_805*;dH&79sLC)5WEie6Y2K2gqSDZl`=kM z0*kfyQf4Jw$@R<^E!^f19mUqN^*m>9sQUf1+|tZH#@W+S=f*-K_N$nf%=FprKVRyI zNz0rU^-RQ=91A7V@|>)4p(%P_cE#O=ljT-lo>=ZH&xX9AZ*opnkX1|7Iq3zH*P5qh zW)$#snXJ%ufpGPsoaB|xGLx<#c9?O}`6n}NPQ^}BrYr$x(!G2%> zr!KVMK$Rp|rN>f;J5Bo(?6!P5qU|vT%3c)Pch0badE&A0SC%xadgP)DLtKPqj?|r8 z?o4ln3%Y;A8_*G&Kvo5>0)u2`c_B+7F1@WH1_DY3yFQvf#;ko&!`5i?`K#NYoc!vw zZuhEF-$IndWj?=Jt~XTX2><-lWSdk0{(V+nEIZ#~zf4?zEI*C=4Br)kB`oTJhvkp! zW~`O_65UI;CT1r-cp*$5nG6r}itnyY&N8{3ZmY-W6;2F3Z*!TeoxgF(pZq>$PRf

      |iJ)rNwdGr)EOmirSOj@aI>%6ZNkal&y#akd%Z!h9PH=pX zunSE4#rHx6xEAD*#{#Db`j(nTHb$rq( z`SIDCw`IE4UK1Cdl({%QKiRpYvTI-Ol)2E3n83%6*X4lQTMw!im@x|=F;1LfZo~Bi zz8NanVFA(DOnN3USPvw4gNFtrRu0qgkpyHaDRvGISd351$@kpw`x|c>3KfXn$u&2; z`YH>)`XD!_1eR6A#F*dni;b15*+r!}i>5Wk&f1YAUQr*cES(1_$e9xt2lm;#X>q1N z^~f!^j11l7%FB=Wh5XVRZ?du2qN$s&8EW$xAD=en{wJ`EcLpk)nsQzwbcYS z`Gd1Uxu1V+O&I5g%~#~+ly9P;rmZu+8N?k8GcAjx>r1RXidKDjVTGVLT0Jn;=%&b4 z;Rg2DM0S{X%2U^#WXLMY%5+<^EuvA1%GkN&g*j1>MX_d^W76@)P`%T0883Go2a({ALKF?KFD>=KXUSYGYYJ3Q7Tk1Ni}n_TnL=PkP}eZH%SJ7V22 zNmh?T@7kRtc?vyJuFI61o{T@EJ6rOw6X){5n9c#d;0Ek*S7H2tlnGpED3z&Cv;vSa zF%Afdu{fd=#`T$~KS;8SP>%}g=rPh(qP!r9DH^uY8h5@~kzlghqids+!c%8YwPtRg zpBPMh53UQm?!}(WIA2w`YGpXMVoJCwB|bBDQB<7UXm}4v=IzL^PMtF~nB=H+N83#a z)$d57Y|nX>TZ*nWBxEG|@?BYpj>LtRrdlofq=r;Wd8SR0(sQyC60&pBCCQOlX-REJ z(p#*)-3yQ~%bk~!kQr~dvUqFdWm_=^&YauN$6lVGU&EvSYZy4!f`Oz{;h+$3V9B;B zaIj;o02H~N=!ESD}J8h-5^cocoYSL{%o5NvbyP58+$p9d*FRvk~X$=Ub z2Ipk}2>f&XbGS231p}FPi6cOn+?AjyX?&<~CXM`ez-!(c^n%-K7h6Hs)HHe)q>mS?`Y}S4F6yJZNv{ z{?h5q!P@gT)#`PHs~cwK7U`ouDNLH`&)28CXumgfp)=WFNSN)*w59lQ;%<@eNHWB( z;4HB)EeiZSeHrV6mm!lQtzc&11LE9u=UrX1aMP?*^-M*vpV|PLc`fWelWZH9{J`%M zerZ`{23RdQ^CPZ4aQlQG&?DU6o%IWH$X3#vA(W62?Na2jp^HF=uF6HqmHu?hmG#yG z`BM*eOqoC5?w{kg&zn`-ad1+}gKuTIj(s9YpMF3I3a1?EsGAAop5<3l9GX)2z?+#d zNRfO{{>!0F?;Kpc`rtd84l&!onPdH9{rnpK!?DR@lcgVy>BxTpA1z3+&zo7_acD}> zgKuYgKKfj*|Ma*k`|StwY7TWyn=#*>3&|$?{F!x~hbaXr|C3(-$p^0Nw;n8-a=5c< z{yck1;SuJ5q2+fsZ+e$3HamFo7?&?%+qlfOefbl1lTgOs9qiBK}bP zSV!N%Eo;293od`*1>x8KkdwXXWuZBXda7=zaJ%IXKYCJFdh$1!Mt*y1V_f6{$v@*z z-^sD2{Vr+7ijV`Y20{@JRSICq&Z6Yl^wHK%S;Vm{VXvZ4>(mBX$~nkA!t_dmJi_9%^0c(_i*qJt=OiWP z+?zc)Cnq^6=Q}yLPaeN9>tgwx`_Fsx>V+|#7jI6UQl9K9!>`YmT%K5B8@Tw&8Bxhi z;p54R9^BjCYLgqPTdJqFP30rAztuAL>ayZh?V%MJ5PlVBFJa!g$(8b_tHeopS^;G! zq^Nvl&&D<3;D%|wtQE757RN>x)b!L&^0>U*EtunDoy)$wG(BO`vPBh=)dq0!I}c{Z zr5BW~6n|e?R8(2?)#AbAyu9SWkZxNYBoUo{l-2Ltox2TJG9myfNxy{BQ);oi>mE`510-d+FPV88sw+UkSx zY%s4{&0kks-^g4k>kNfQ2g^GvF1zW%#X%hGK+&Mk@9w`utges@Qk28R^sz9avHSDn zlE#U9_&CUpkd#0$3$77pXRdG+A+HS>aAHI;VM6I}830cLF{KlU3}L@sKJW|c1&ytj zU*5WAa%a!}Bgc*%x$P%xMQ?8({;}wDNC>_uHRX~yE3SI}s!5SHlCOAu6Q%288_%T< z&>TfyjLy=t@Bnotz!;F60oD&mrd&BL(<{=?pc4Rg1Y{n)uH-wn&Xhk~a_cKcrp_6C zWOUBdr>}2qwLce}yWFzd9q)&}>f^=s;G|;tJJRyFf%;XWqpRu%;_CAqJSUoyvllx1 zUH}AA53Fm5s9PM$y8v{hG1t?dc1>}O1U%O@ z`h1N(y~$h=A4o6sT(IawV+E^xz*Cty$FjQi(2bJMnqZGHvYerTc|{fdQL{pBABPLm z`V_+@>((5s?YLt_#m^EG@^ayI-(yx(4*81yDu%FC@$8S$Z%8YhNJ zp`~;R4$V~dPG`0O5dH>X04mvw4)m}Lj1BP$Kwj7dAV=`I{a_A|5QCH~2C4)D)EmBn z%7evN71PkL^|n5#skpJSF|bBy8&r!3Er2im7X|g ziAS7ZSqK+sje&V{XU$zuyigcCSx8FM!s`x`p)9I0v}Q}AI3qPPGp#{t+_ENA8C7O5 zjotZ!DaJTU5QW~gK%lp&GlZSPC@W}*Gfw$|adKLL$5Z5+O6vvj-PCU_fxmO?zyV75 z8XTSrd1O{!wPc}r1WXntL63%)Wq{-1io(Zc7E&ro4K!}h1ZXDk*sy~@e<2g~7_2r) z&t@3~bKV^nidnhyXJs;$Icr|NU)p>}78;vrOt7qdLz;_UBRLp!(2j`r}o`(yqxwEOv*>ejs@{S*0p2Pb~@x^Hu zH48pp!0Qd9rig1UN>=(tG|jw4tV&5sOQ{l{&o>HVe&NWX@>##-waMw}$+i6U!zBT$ z;p9594|3nhbxNlnDfbVuW+^$nBsR7rJvrmvM-~#e;M_O{Jh?vtuZ+tb#p{w`2gr}T zXh63STn#UnT$x!C^9ork6B>4Sb`wJ$FeC|?tPIxED7q{QNAi%vD0A>E16flmB8hfr zD)>WLegPte{;ct9Sthtuo*0*+=pExF8yjV$%Sxs;Xd{cvY}QL@?|@MdZGj5yrymyo z4MgM=JJ>Q;H1Q7DE||B(Fg6u#apjN2cE@k|*avLHC9e=}a3AMa0Ho1%B?H(n@7TO|ErL3%|m{Y~T!xA+4+ zd+Sec%BAoA?QOR6O*Z|fW5?fOFvE6B<7e}k!z2V7^!(6^>}U6#c<2wee$F>M%O1bw zGKiT=^{mMt6|@=I>tls>ga$z-7bssm@rlIo6pf7EF({ zRm^N|<~R0ScU@2Sb=S%BkJ_V;QFaO0p(3RSeUEBa?L0yGMiV67R^ZeRI|1d44$B%a zmPiy9Ed-#WCc*z)pbEB)=qu0q7VWFFq!Yh9=3JS2QB*&zxNv5X&uN%nJ9e~oKC}iF zgd{^CrXVTDpOaJ&6W|ZIZ0l$ijbG2|1)J*>^ng!P(|ZxKSvVh`+Ko?^A4{7ubH$vT zx{i*z;#KSC2E`PM*MxswO9~S)?G-o8>UCnTP+^1?NR=2@%})+=u1CQyPX$d<1Kq+A z%vs`_k3#@g0Dx=aWuOH7=&5nj+~KJI;aOdBkq8SjGNqmgjW4?p6wyWJG*;+~6Y_I& zbMq65^%add(X*g29bUBK`#W}gUrd`QN+07Gd(jaSu_U1x;E<0H zEa(9dY{_VMYlWETaGOkSN1|BK+C932Po=_l$iJ;7aH9*0Mwu}Vx-iR`*m(q*>n6aY z3Z+oO14HrD=-2vh2YOHi5-^!cm8Gr>YIa=PT`1%{fNk6!M@R#{fA#FbPKml)6~P20 z1`0*f8q`8xKe-Wgv%<12JnQQnyXU{?Qb5p`3iPpcN(X5cJ;>$v=-S#Z(JNZ_zB#(& zYdy@KRJwO;-RX|}^mOn3?R4D907142$qzqz zTB}j9g!`i#Uv|z~v}l&|IamZg&|n@y+5C0C-@AF;Dly%K3Yn4d|@i} zw0S@>)vg&21d}bg6rRfie$4_Ve@V5ydj;9v-77!*8A=y>_n#4K++X|ocGk1~^SiVL z>vbec`N;R6hI!SMe`d3l>?fwb{MAjWtflFCm> zqdjdEvu9U88A1W&6Gxw%8{gnN#=VHsa?*bB4?V>_AimbaQ4Kn53gAksICqyTN5su zJD1&}$mz((kWj;@r>z00&nlWd6UqA4QPPQ1{onQD=~bGSDuBTM6;91O2d7F3(W2s9 zLYn8|T-Uz|(uGlC$j(HT1b)7sgrKj;IXEZj>WT+fM&LD1J_OR4Ls*l*q z(0*St?x?Cn66Xlq2=RBXfAIcmuf0F3!jl#b&CDrGE$O=Fk~`|^*v=7bS7u(Zditi- zwW-ZL2jmZbwQJY=ENTCiKfZAN(wlb|t*M++%RhlqRfYV#{G9wl`NvUtlN<7qoXx9x zBKzeX35|WLYW%Zc^=lYDzVEu5<-IgK1gx>U`KST(A29 z7zKa>5}U&3kmea3T`C7PP8?q(!vL&C%aPcrM^Mg1kzT=ZU_koGHY{==3Tvr$@}meu z(76{7H1?;&I71DJEHUJbY5U7kF&c?($w^%6EDR3)04!Cc>mjVaVxT%7K77Y zh?pqBk>{-y%(hC8Bnm!1{Hf0!vV!feb#LkwVyxaMx5<@y*LL}%dvho98^~G} zG!Mgm12%DxTp%-y23ElgP>F!e<8u@r#M`blW%*7XNs4jC{))30i@_o{144R^Rr8*2 z&`0p*=TzY~ufG2^DI z;q(2Q)BlV7uRm}~M}+kHr>C!dWnn&ErK*Cu zE0x>r%5_Y=!9E*3GS~n^U_5eSLiybZxnwPulF6?oQ?HO%i>G#=8S&=)RljeYeqj9x z@a&1IUpOl(sV3iSmhVvVt^C?Gs8pfKH-G)@yI)IBZS@Byro?W5#*eMGzbgOS`0-~wIj{%qH??L=S2NXR ztHxf1SHsRpw0yA>v zFz!3P#c0_0114N`D=T_$``GdAPi)`*1iPhsjS;ks*I=%!9eIAkj-xhnU5(igD{-f> zshbOzynpf4|Gb7RU)uk6%gU84Z}%;`lj%N}&tEE7O~uhZ@RAp>z+(@yf;-KIp8I}x z!DI5P^955(tf|OqvWk_zW+iuA#iVDpn#>zsli$mvI=7$FZGCgP-e?YHo6X_93;UmF zwmN>eWA&Yr&E}k-$*7<8?giVAU#2(g{Ie=s13AS}aA?3%B=_Db)9(y}j{!}bz<8*~ zJ?g%B6!NI+Chq$f<~O#PjBK3i&fUL_9~G&2j~%7mH(fB+3jam%K`7{~!1cNu7L~(+ zy=h;dw&bj>vBtMm9KnNrBUkX)?+a+$*pYEY0AHsXIp-+-6y9(hF$h$CqJVmdLqK&a zaz)CwldWB7-owEOwgIH1fMZBlS);Sa6aa|k1qDt}&g~oVTYJssk3Tk>_X4fr9*@9T z&wOZNx4r$Zl4;pQ*Tg=hzCoX2Y{;`c@qPYdySUmWO6x80W2*PAyVU04t~7VT^GVy+ zhnU@kPx*$lr}N4$i@LL5fcjI#@d_-FBkZq{^@S`jHYmR$t@{QVp0)EJjtpP>CVHKC zwK@aG`T{8vN%%r}=W%B$ z(_Hb|gBcG?AUFkN5Y~VkE(GrtKO*q7;wN+fJOUo29}*gAigXo;osss59xv!U`MCtT z0Y-7tL3UXoH<G9z{;ZqrR6sUVoNd1cHI&I+7p&q;$?!N3uAwtrmOGDX%no4MwBE zYcw26x2D_tR;zm3LQw{z$I14jT^sfninHcc`?<&9(%S_|Fgz!CeQEma<*PGWbp4^j|Y{)20DOhSxob0p(vRs8Wo6THMV&gai%S?{*q({Z?zGt@82bgi}jd`<0OI%h}?mLwImJ5vIN5RxqA_FrH zs@2572~8G=#8x69z5(NV=>~rmtP)1KN?i~;E|k*J)1YM>DD}XM1K28x)-O3(Ze>l-?J=9$=Cy(7F3C?I= zOiomcQC#KDxT_pC^QMT7w4}n6kv>CmQNZ``#3MQW;Ul8Q=rkAw7UD+1DS2AAFt5=8 zA(0!o*B50lJByg6e69S~^~sLO zw|{F_PIhXxNfa*p$t_zOL`Qkrd0#$!O=hMi9nQo;ugPP(9?98#=>=I?S8aao(^>ZT zhF`y0oHk=sMkaa7nFW=1eN=iTkVoP4?m&{jrHbrYIKMKwrruJ`EsJt?C59YnzC*C! zQE}jx$A82GV{%*XJUltl`DgiwiySp_^I88y9q~t86c=iP4J! zOUleNTViVGPR`iymr8w3ZGBv<)8vY4j&06#i|cM)Q)97u{jKbLX4*CPHTjQ2sg`&c zEnW%xe1QwPR>j9#8~m4DwLLeN$2j6+6B4ZEl*vZl{wrR(WvDeV%`t1Tf8LPXfbq*b zW!1kU{S_xw#h^f!DHf-&ED-(&wMYUV2B-?j z6~eSPWM;Y7&#Oer#)Pmg3sa{oS+olnaA``?^re-%BGFb@dQ7QI$e5a!8S92~PqrcW z%%9*w@2k%r?vR+n>=#QrVX2g@V=IT<{4WbG{r+p;zjT3mV*@q6gZa~+$nVMWBaO)= z(wr-w`rxy_AAe~0qngDl_DX%?Ehd@uOH~qD* zwHg;Z@OSyv7j9++e|`O1ksR-mTZaNy$`}2WEw7hQ^6Gt0{p{86?_I%@+xEVSsR4Ns z&@>7TC3|*7(9tHD?tbWIUj@DF`(gVBa;IdW66dL8xw72&(=`%gnh zzCs1%*%DQD!bmw$!sq|PoyLagim<*d!1{JI(VBo(P%#kG@j!@A$c(}>yt)?AcAAc2 z@J=zY5+y+c4O{4OQ9sO*D%dbC07Zs_2{OW>#H3(>#ID;VMJbP904q|7Nu-?yyrbMn~K9OnSo4Fk@c z)L8C(P5yJcZF;~~_JlV8LqFap?nsI^<-%FC;u!KJ(Ug!T#wSog@j;JP4s(1%Im~fR zISKJ%T7pTGUs8NphLdtl@$8n=Zd<7rjaq-iUuw=|`8UZgd>Wmb;xa~$zD2TtZ;eJ9 zT`9TIpR$UZaXdqZN7Igq5s^!a3Kj~lCj;(!JkeM~M1#cqv_}Ts%8;Hh zH12(EWcaYY~)7fzL!mxZ`r)XYE+ zt0PLtbgAx?I7Pm7M1JY^N97k^h`WTX8fIm;KgP;mi1REbqDk8un00no0QaC}BysLa zx3F|qR+-lT;-vs4*|IY6gBc`0&i*HwK019KPci|*!?%>)e^1Fn^I|@ak*BfZi{;nY zyPtP_#j9P|C%d zIzDS(x!~yqYn5Ecf2Jh9=^Lm*>{(AS!%FC^F4wi_dSGSZB6y*CRQIgzW!*cvk942n z8zGA2hoCFA71%OBmJ$;}uWT`($E@x(gc!ZDg-~`0;6^B1i7*L+hrI!1y{AYTqa2d@@6zTCo1Q!H`o@u428IC!p?{x+;^E?Y0l5?UBS4;X7dxD;~Fnwu*TU^wrhboN7w;8N~lBoLGfs-|Qr^6m6 z2+l;l%xXx>v088$i^-UZMLaqhS4nhP%WM4Bgv6RlriFS|_PQ@RG{wp~{yIG%EZUUo zugVZZ>+5|x4?i${#-&@97wLlyF}@Rnc9YvxVpFd7iqUC_a7yKjN)&H{44Es<7~^)Q zj`cVli3wAjPDi+ket?a>MUOv_72z=D&!M?0i14E< znc=Akr;1+YFkp|BV2duyO}yg#tJ$WZ$8Pq0S2##myV-&$Vlc3FA#2Kmc5Q-#L0 z5dz+Ga;S1VUEFbVF#@!6v5 zh!ce$wCeIJWPazJe&>?M~T7=80Km%%z<$p*1`g0SAVL7MV*HckBHJs zx(s}m8rCDeNedfv-)7sjuu&Jww`gIL&drZ#VT&%8Kcj{1y2*k7-b6p-jkmzhX%}o^ zbi&7&51O0JIJbx(G##NnXf$m>H~1emZ8;TqtN9^B958d9Djx*_BnRC2c=rLL}j zV9Q`vN9VAwzIkKBH@&&9ZHq5ZToNwy)%5iElvhK(!N^c#aATwm85+=@KD43+_=!sE z2Spn}bbsG)&8Emue=i;uBBlfKE3@Y{^Evd%Nyq}q^SR(#-++v4WW;ybv|7X-&TfSF~Z~hqFWjn z9O~-t^92jb3X7GG{Lcz+#D_%iDb#h;r4bw)Q78J)4gJcsQ+e}ELq&O7k#4+U?Z~0# zRP)d?btjcIh&tMkzE|nCZp1Ysmg2jxAdDb1UP>Qw(Nil@5796-_C%V8A{eLk$e?ey z-#6SD@tqmkp-Ag6eRz96UgAwV2Fo`**xVNBZ656QH4hIDcD0NsN&5PSyILbd+CUGY z76PVohI(+=cY3V92^Mu{U`eNd>@YyM5+r&NdQSb`=CjHyRK85tIXpZ7y&h^_vkFUv zUH$(}2}KwwwO9I-(JDgbZz{8>2Orrt6v2Ci#-ZE4`p2Kc8wN^9z$xJ#-EN#QU9GzY zwu1KRu406);cgXD1+m@36aLx@U1YH&13UfBU`{0vPIbGEn!R9GPWFkVOFwLY&BcM z*0Lt-|C(6~@Y!cN8*624EW+AZ2kT^AY(47+^Q{;9l>KagZGa7wAvO$?up8MXcq8A! zwzBiEF}?ueliS!RyNF%PwzEs%c5o-#1xb?2pt`z;UCypxSF)?v)$AI!mtD*DvHk1- z`xcC{UC(Y{H^N8IL0ITM%#N^|*|*s(>{fOgyPe$uPgi%byV*VLUUnb*4!fUymp#B9 zWDl{2+4tBZ>{0d@+^s&ro@C!=PqC-j57<#y<9wDq$9~9u#GYp_uou~n*-Pvv@Id`C zdxgCUBf39hud|=CH`tr(E%r8hhy8-R%id$ZWWQqXvtP4g>;rb3eaJpyzkxN?-@$Xy z$LtU6kL*wE6ZR?ljD61j%)VfMVSix4=7)jl*ytck(D6&0XBhW4MQVc`T3P@jQVi@+1y^3#>Y)@-&{#GdL_q z@GPFqb9gS#c`5L~KH}Q46nYZv( z-o_)m9ZCR% zG2hNF;XC+FzKdVVFXOxU9)3B$f?vt6;#WgcbuYh`@8kRV0sbw19lsuQ|Bd`6evlvH zhxrkHGygWfh2P3=F#jHZgg?q3=tm{3-r4{{cVBpW)B)=lBo#kNETa1^y!cF@K5wg#VPk%wOTJ^4Iv!`0M=V{0;sl ze~Z7(-{HUD@ACKfFZr+d`~27Z82^AD=O6Nq_;2`c`S1Ae`N#YZ{Ez%k{1g5u|BQdm z|IEMOf8l@Sf8&4W|KR`RU-GZ`34W48H>a)ewVPskSv z1n}a7VxdF`2&F<07AV6)nNTiN2$jMlVX`nqs1l|M)k2L>E7S?~!Ze{lm@do^W(u=} z*}@!Qt}suSFEk1ZgoVN)VX?48SSlMn~gl3^dXcgLoh|n%{ z2%SQguwLjEdW2q~Pv{p0gbl)=FeD5MBf>^uldxIXB5W1T6V4YdfD*|zVN|$CxLDXO zTq5icb_%a^VW$O5rNuYT+7TuW+rfPuMRU5WXc`CtNSwAlxY2BpehD z35SIv!p*|Bg2=@!$6&}#-lRA2uhlZryk)f_u z{ZOQNu(i_|>Dw6T=^uzlop>G=hlZO6&2(vs^bQPf5l29^i0xfHy~g3rCQu+95kA~$ zpm5jFFz@fy4@P?XH%1Iw`}=#Fy84XDy?8^<5?BLfsCb@jFMZ?+8dG;e8Y?HX+DiJ;Db zNb|4(OEsvfP9rr%DX^!%wOefOY3?xNW7-Bf`}-n8=8gS5BfXI(w8x?asREN09vRSY z7;Notix^ta9k>g_%^f0sLt;yRf47k?w8BdRgI#^Y`qt*&$Y8Tb%PZdZwCTHso3RjD zh9jGYn>r&z1)7!crmnW(PBY$h^fmQF+J~)b5KHE8WYD5MD3qa14X+;=8t!V}BGR{5 zy87CXPR*xW!>{q|sHvXV|f@z>l%BMx zL8TQ&H9Rt4Rs#w|C|yKwgysx&ZH+XwkM#6dweV1Hb5D;mvbnXVxwrXrv&4?B_F)l( zV>{-^V8j^N0zkuPm?+TN(?1lkqQCmO`Z|=hOX$zOh_SV~C(_r}Jg6VUR-wPw(AwYI zi}BX?Hh1(zhRx&sH8OCzAE|u+_u);E$gmBcJ}^Ku?5h8&g&CfB0W8p zR_fMvbnI}%+=*dqQlVQ3(tI~4p^*WTa;FZ7Qh~GS3`9ns6{8g3I4f#o;OtCP3~+dV zOGLkE5Ocm$8g3ry9?}D&qR&h%gI$sKR%~L-1i9)wkvazZM+Sga`nn|mS5 z$Z!*VDdq_UF-g?`b*n`UDt(1{1I*qxBo6ft0@QF(vKf>RCeQfFMj(PULWMOE?d}J_ zbO8R_uq3tgV~i~tI8#dNIB3%Y;rL;|>o9hC14cmlAjZBK7!f$n4BXxcq&d>lVgz2m zICn(sN*625pry;IKB|yvpry2_x6OjQ!=3#@==_LrXrybHM$AY+MK$VMu~0=KSYi5s zm1(6^mJ|AfmXWR=%$5!#G7r$YV`}b2?ah6y5q)o@t-EX3(oRi6E$bs_dIal0r_%3Y zdvSXts;z$n1J#6f;!2$veO8PLe`iGj{?2-)Q8Ay%Z&8CvMxz=gjH;ARNeyk0p>8Z2 z`kv+ix+#D%Z0+rDq3=>=qg8`<1>VdXM*4@ z*#IiVra)PRWx~p085+Ti#PsbN09cQ-s39aPFSQPgY~4zI*A;1vU;(89iOR8`2@;{B zAL{Ii^t9Q>7aFxSQM5!g0lfl-M!JSN(W8Svb`e^5Hn+9`L20YDf&ml&IV(m5kh7u) zK~2o0AgIpa-ky-yIy6+O2W$dmnpLby9jRc^A*_xrzrj<OOZWXSXNDEchhc(j6pqt1Gw_b9G3NSBax3s%#S zmWaBvX%FIN46}(YO7!V8)R~4hzzv9MpmY#`n|t-`plQ1Yh32+CvAv|M z#NN_1+ycZ7Y^)9gFk#Q2Wmvf>QI4K|RCI=zvQ2m%8JPH%;L17Stvbawfz0jSG-SXu z9qjLFlQ1zxHlvwcEwr`_b#EEKqSik$IJ98|ivq|2fJ(o<9cZ~HBGQEx@ZqijVQ7Sg zHXJt4=B8_7L}(f5;2XQ8O_8paerz22@P`Ct0lV_;m<}rDrnq2?`T^r>aF0rY)2pz( ztsnG&vi;CHzpUK45u`Y%Ql(8uRbFgUS2iW0sh^?(bSb3^ja7MwE@8Tq(WRU&6^4<% zu7;ADV)S)$31TWJQ$;B~Ql<*ZR6&_4C{qPxs;Cf~g2hUX778Ipuo%?@i-T%uwJ0c9 zj7-5|WC|7|Q?Qsal@!y3-j-0N63SG9YJw%GCRjo_N+?GOI4p?)>g>sZ?&8yc6tS?auu2)h})>5rX_)S#0r9Q0P zsqi3`5u{p!RBMoG4Jt1vYf#HNjVcaN#UUy-M43XADMXnfL=X`ohzJoxgo-PqjS=8d1PLTUR91*UB19k&B9I6XNQ4L^ zLIe__5~?IXl>{gU0Yiv@Aw<9sB47v+FoXygLIeyU0)`L)Lx_MOM8FUtU#BTP9k=(tdha0PlBIdGvI7<7av2Mv0N z20es9$AxmxpoeJCLp10i8uSnidWZ%+M1vlpK@ZWOhiK44H0U83^biethz31GgC3$m z4`I-8p&Wz>LWBuIzy$4qvWPN20_EzA3Q$d98u~B|eOSW>fpT>^1*pC-0YI1lAWSGB zOt2KD@ekAZhiUx7H2z^4|1gbzn8rU$;~%E+57YREY5c=9{$U#bFpYnh#y?EsAExmS z)A)x2>a+~hXf3Q!=X{_hptiiGRJ*GaE>NR2wML!!ftoVyeYtiYFRw;>uGQ{!+Pz-8 zPgC!;TD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4s8qy5Z zY4z4=_10?v$(?k d0mRO}xo^G_%I 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 SdSQM28?+M|D(2r_;{O0|uQ74} literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.woff2 b/xhiveframework/public/css/fonts/fontawesome/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4d13fc60404b91e398a37200c4a77b645cfd9586 GIT binary patch literal 77160 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-Black.woff2 b/xhiveframework/public/css/fonts/inter/Inter-Black.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..18b35db75c504d429820dba1aa2c99987978be10 GIT binary patch literal 108748 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%0DK`vV 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�+RzKR*+A!ii=dYe3O_e%f9+g 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 abltyDS*FatO{}mcs$nKy(z{wiK>QyWJ6Ws% literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-BlackItalic.woff2 b/xhiveframework/public/css/fonts/inter/Inter-BlackItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..02c9d8ecc2fbff75cfb63e1f1d8916e52dbc5c1e GIT binary patch literal 115364 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 }ID2 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 z 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-BoldItalic.woff2 b/xhiveframework/public/css/fonts/inter/Inter-BoldItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..bc50f24c8731ec8cbf75effb9cdc2b6129b65dfb GIT binary patch literal 118392 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 Dr))=M literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-ExtraBold.woff2 b/xhiveframework/public/css/fonts/inter/Inter-ExtraBold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b1133688a43130b5c96bcd2307bb7f6127241412 GIT binary patch literal 111360 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%0AU7FeZW?Ge9b)cluU-6YLb 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�O|fplrW$xl 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 NfEqr4rf%_j@PK>nL zO4i(s$QUK$xL{3Tpx7sLq?&5&eE$82dbfkXFRIzSs9gB3G1mJ_A!vZuV&L4_F{BZk zQknfTN6>BC6|2x~8#s>Isg!+{|NsC0|NsC0|NsC0|Np-4o{42I;EQwSqO=qs|E68jnz zw%B1pw0+r>1_@jMOOlc|tB?yl7&x!$+gu<*_e4Ejd8nX1Zw~TS>Qfk>nHLTqej;J$;RL z6Xz!9%YC|y6^@kp93ayg#lb(G${lJD~L<6i4#*0IZN84 zoxnPMUne+({8T*Ua8S-bXhMwgpQn57u_y*{j@dC{MS?S#XUv!0V?$$m7aZ{t*HXij zNm-V-2>lpxUwu$E#mBFsV*ETPk5qA{D#T#1k?wv@D(8lr>U*rIG29&Cg@=%z_`3Du z;OUxeg4f`{ZamE)d=V67KJb6}I`MV*?LrplVZdD}D+5kMx%9j8hA&YG3fz$^=Jaqh z9^=nJGQqz*RE&BpcAP-CfyTZROA?cY`609-!sn6q*uRWdj5%Q&UV?OnHthxQ1Uu&M zndvjPUXq~X@tY$`vYMN^&0I7q49Pe2SmEEQUq44o1R1|O%hC`|+`(lr2CWxPRk@)m zE8@K-Hv_740#PZ`G^N(JmUw=`CJad)aACgg?b_SS`^-O~FXa9gedW&spZWHkuStd- z5fA5{#m9fxL%6=LbgZ!c|KJlJu4;!@Bn)24o_uK*(G)#Ifh*csk6z1AxaOfxhK869 z4!I>31zHBV)h9eFGAnI?vyFiWB*Kqg)Fb~k_xqu1k-hWpz>9B=zYShqWL6*$QBf5L zQHdoHV@1i)s0g)IsS&t|8iA#D(upDH%)T$_e|;j}L>NIspM*HN_mFzJS)e2pemg@u z+v#i)uZVdTWn>}si^aw{ly?p>vC}r%#$jy9>4@&w>)U5e1DMhm`c$tkX~_N--$eK} zzu|vo`zPXX7hJu5%Y6#oGk0176hb64XaWCuemc)R_rCWn2`N-k zPZgbcVIm@!P1F$gtxS>X5X`M3g{|4B=&`X57%RqLY{X!U$W#OcR8&B~h<-m%-*5Ge z5B2q{;e)^b|IM1U?|o-3P=AU7#3+iQD2k#eilQirq71~VCfRNe1|A|h1a>?7`RDgA z-ui$0&6P1ncI;%0BuTN$PG(t^6^*Hg(pXYxYDr@a!rbF#si%#ef%IViGb;j+p>vOGd#laXkY#?Jdhb=yCssF@P^ zoD9FRefxFs&o4*Ag^-$*+RHH+L*&qgY#|>B)|ZHNi%bf=6N-vb+mpW z%XjUM%0M|PE5b#EG(&pA|070=E8q?G=Ug~RL)-;wdW{RI<@9NWdr7u|lr|+}rqcfJ zo$K4Te=~GNk%0t)c_}I_CU@>lledJPQgls{%*Tx!2f=~ z_J?S`a{s<_-ikP4mS~U&Nh6UGD8VH9bZBNF@u#l*w_s(@fYOuBt0l!po{NLL$y^oS zDw7JZs8SJD09X?72f`hk(_PB|akY2>6#U=Z?&@8&nf)75C*HOQNfZ7$b*mXdvK0BT z&HA;|UQXt$Ds3slEHhL`{EDLpz3XO+P;J)Nxq#x?M-cAX`onmrNM=7?5 zTc$JjJL!rp8q+xr`s4deA7Of^-J1>a+fp2HHhE;eft%q4+D84*M}dr(VUO> z6gLzH8b^V|D<)RQmld}SRG4f=`wIjDA=hm0Ii`3Fgo;ap6hp4>(q&qT0&7Jbzej=#+W7ZNODNp(2vc@

      g<-@Pk?MO5a6su8U)53{?M!~ofznpPcl)ICNT z?HQj03zlbvhAiOo&)r!M-~aDdZ%x0dT{nQj(DIR4kzoa>30!;>p+=#;kYQ3$(F8(u z_sp5r^uL{08kUsphkHh_AN%37cL$Ycn1|pZ=I4bPD7KYsg`>wS1sLP-`)swEo?huW z_1+V=Lc9U)2G9r?UH{XpGqQZ&46#Fp0Wsg-?ka~6vTPh6kzpDf0HgQa z%g$IGYamzxi%L;JMP(XHZZXf3fDVmh*>TXU0Nt1^1nvS!Pe(WTXG&zdc3whUv& zQres`JUqXAP5O?N@+VbJ0P$ln^9O9V(=a$?NJGQJ|L^y!ik{v6ONY{+N1CO7FmGmw+tY(2iA91Di_@m$Fn{mVYL^rE+d6Ed z!^~mqja*FAJ5|f((&wrWVEhzL#+g~+uu*#<1fD-A0MdDpSDRvWO;e>%R9Bw=|2EV1 z-NpCKvYkW>r#vzi?JMBcdERNHTtM&v|>IeQAHBlK=lx zORBdzjqe8KCZxL~HD|bvus z+4<1A?1Jp-g7ADeadp+Bi`u{sKc;I6a$!6cJpJ!WjoSKk z+dfuR)&-Cdv;pLxAZYjd?|JE8t&I9tmezmS+L~;elwBKBH?oZ6AbS9E*aXkAvp_Nf zhv#vZ)*euc0@A4MV;IGUCx?g!|NpN2YbSF`vp-D=C{%@zfH|ePxAc-CF8{r4jn*Yt zE-=fw5Tzr8D1F5ZhRzdMd*0cs_%pdvuG0v-4Pf3)%#|W4B+jEST5JZ>aw{)Im9`1b z_kS^-?OhzOWC=S9QV5ka*(V61h$2WAV)RDml^i2hY0Y`urr_K)NcE-Ud0v~8^qxE` z<)R|!;oJc&*v<#}|8>^ezU!xV_Q|6`f1>%2WHtn~gVb@+rq+E|&sVY}ldJ`Myx>{# zGL8mtKZT)aQxjAK9TrWW-=%9~)wHA+l0(K1umOERL#CbHnW@t5NLAA5obKNtB&xk5 z$r=3<<}wd&H@@IblU86K0}kwACaty1MQ3#DFj1a;!%?slFhx-qQjW(YD%zyeh3GL<%t*fr8imIxLs)(qF zs^h4Nh^Wl`SF>2Z#p*#c->-_uynFY4nn4I7gb+puBZM%#UK?~MgL{H3^7skF4AuLO&|NlSdx3<1QK=kXa171at zBpOUXv2|jirdb-A*5=L6*UT4%=YMWI2iqR&?k*S>&4M7p|JnaZQh>++l9a6>219&+ z;G;oHNGTN&@+?^qGK9GZS;T^bv{^_(`?xqlA8%BIKGo6){jfC=`dRBE^qUSw=mgJ6 zh|6%S!6pg#CWF9~5KG$3*&^LWQlUG?OX=R9{3*rPNm+ z`xzTFk)#@WDfSu8HLhEtYUE#1-lnoke+E-o3RC=7So6SjN8ET}cfP{|cjD=}cy%7% zU&Vw|K7P!nFZk+}&5dkqZGZ%|g#rm3EeRU~$ssh!ITn(8*GS%Rk$mGJ`Nv0!Oq@iB zKw`xs36hX<%O&O4Nb0JAw5#K!rP@h*>msexOImN7bf$+Ow6(YYa3L<-;UAisQA<0( z;VFd@XtYQN0tiNtC>lb+*cfWq{@yfp3#bK<-F0Upd-N_z_Viti5CX15(Xs#7JhqPQ zW9LNn|L@c*e1T9Tmbm#E3w#ZVN-i-#q6ghht5=h7QOeDHuO0~6aWC-tnsp0#*IwH_ zX!W(T2HRYlbfX$Osd1XeK!I`&g7OLjkS|Pg_O8OFJdii;xq;>az#mUyz$=wlpU`h zJAhpYT+yioNAQ$Fx*|IZ^Kc$6LNl~NCp?F{(6b}A!!RZjq;a~yg8PVp2N9(6i^{z= zXJxN_-{|yu3UB+j{iPBze!Ebr8Rd&k?kPum0LX%nOQ}$aO0@@%eGWE*13L8^HErFT zB?t1lnG2EqUlswHkJkt>k}lzGPd19(-+4-;X*{4CqeNViWmYQIKmCCKM%Xbk- zZ~73z^bpz}x381w1y4b_E2v5YrRa&W)gX+pU_D?z^eB3ALW%%*6nbVE1MjSmuQ{8P zo7uu8KHJflvrEJ5)f^@w-cVS8h+Z#$DrWRLaB~<;bRD$xXFFh({Wu z7|qx+F1;Af1STGtR{8ZqX0@PN@zvENxixK+->ydbvIC+cJ^7hl{2G4yb&Uq~e9;E? zO56R>8}0RX2RdX#@f|g~q*9GrH)+!7LxJot1;rS_tYdv4-lrF5pK+(K>UGwhm=2nen5B zOw=UQw8@!MwQQ=Ut~SiIX&>lXi`4!JzcLSI;FKQc!bW|T4?m>Q#xbn;H#E1TG%{U# zX^=_i%vVM7c_qoL96AqZ))u8Qz?1IifoNH5ye)9Xf(D~0;5>qu!zYC!i?sxPXW-uEq$BE5CxLTGc zU;Y&(Tq#;k7$qSTaAc?Wq&mjIh+qO4lBFD<+~mu+po_==Kkib7tY2^?sFJQoSJPfi z>e5}&O*4o;-9q;nKX82-W!%#)Vw%!_GQmm%p$N)FipPLQ>W}e52H6OWg3~6C3C9Dfu=(T^EjHv&efb#h)w3*>ksO=q%k4$B=f1;_ zK`$bARzMlVvr^QnWm?rBGs|oToHCEhe{t)yEJw@5gikl+sz7sSq)_}W=M_EhA%Gkz zha5DHZ_uFgQPK|GU&m}$QGC}wJi`zCqo3rDi8H@Eu=4AG`b5|Y*g!B;a-apKx*raL zKouX#4&}{CVRdJN@zCNsU_W#ndOPlUX9@^0bHWZAlZ?F%bD6IZk|+#mY-UP;4pd+C zi6pti9Xgjx^zhUnABr2=69dBG8FLQ4nz4r;SR{mts3uWOm==U!axxt;F zf}@9#*masguRJb)Ifk~(v#+f{se+kz13SsD?vm}w4sKK>SI4HLNbThd~_7i zeK+=sMfllC+9X7};hr$0;oO*hpYro#nk=SSk>zmFcM>U`a`Mh;DyJ%Hz0Fgj`MPDf zn;||NO5AY@yyP9niOtc@)6s3~~mLsg=DG*;{a>fiM9G*AU31i<84b zVr@(cIM5G5R=QF?s~qKl{LCLgF_<4ANTuLWgj z<-2xt!ffxJd6>t@Y*XtF6OAkIs{*p7@^eC-YbWr9h&R3DBSg7k*o8I!9>l+{0t(vj zY4u3Y4qtGzG}8eg#E3Nq?eQhFsA&WtHl=))~*eRTeV7ol)@@e{N8Q*jB)~N^Pr6WU!;njlCYF1_Fnw_9YzSpUm)b5CJ1Vysf%#lek=F&t2WC#2cv)r`spKbl4io1IjqXet79OCFNVW^=xRq2|etmaeb zq-!}STs52KT-#3RMy|mlc8ilIlDjH@~!x;LqX|u?%CnbXS%yJTTAM+JE_-((zrcK^Y$|M))Biqy6>#x zI+6e2dAsnY1aKL{X0r^!Y2f7oT(*!*AXyT*AZ?H`U|iGn-8&c1SBzqY#SU<*U6o~C zp;zRho1P;rDR%4K4^lW%#`0}d!rcvL@DrqbUJ+fu&UTdM&5m=P@6X{aK@nz0m&8J>=UUzk$fK0c5DrE4}iP zR>1j9U2|h-^%{FQ&3@Hyrg*crgrjPQi{7dmvTTt=Vi}@Iar=QoV^G|moa?)z{)C2U zek$^&VP8b+4&WKvdg`3%?_F>2fq@*Ezk6T?UPDM%$D+1mg%?P1!_>jh*gAvDjbTG; zruY-S4H6;>mwLgRUm4IB_5Pv#V)$a(D$Xx9^D_vT@-H4&);~heUykTVjP!bLoN^~8 zqpU*p(It;-W34_K^mzcIJswDRb1)ubm<2GClvd=i^+e6B^AkG>oJ5|q40G=H&4a99 ziWyl&4k_`xseyEmQL;dG$pv}czzkdaVlz22mNP0(Ns*kD&xv?3oA0$|XWqFg!`U-G z16E~@k$Il;*DMwEGT#>B{6eoCE(3hU^(~_7`%3wIkG@!o=Vf!qrf^9D%L=%vuP~mY z|7!YbMTm~CZX<3vh)iU~`&Q~?b`^{%xs_G>eD88yts5fUuKV@=8WHztO^x>#>vg^7 zqSJ>YXPfxX2Ag7Aq*nG+L=fssfds^xc3|1_17i~=jQg@F8Ch4!Rc*L720Q&%Exc= z&AivG-;7?x7}qP7^taS==!I33*L)qj3H;iqy(LKGO>>#&z9{TR`GARnEq+h-O|H7! z{!n&cD;KvWkkj8XOIyc4yTv8@H<#=E7AOi~?MhmGxKFa3bJeo%hswpmMenoj2j}}@ zH(8+-OlSRW_r^1k6aUU}d>iiZelFgauh1pE?6+b?C>=I4^t`hJ4sM-(Sp3?=L*WwV z@E#GK+vgn#0uCT5RI2B|D!s~tC|TFqj_r(F^r7FQ4p4O`SiceIB=(SWqb#246hbaVNECMKEry)N^R-&kIg3&=Q<330}i}t#D z-q%Xf$4Imqf>sd(;)P;XC34tzVZb=vQtlIWii?%!9SG2KCteUJ^EB9TKLk|h!jORJv3%S2&(pt-zJ@6*IGIgSGlKvoy@^Ai-H!F%fSri4kv zbV$V&;d*7%*nZMOQ@!xLNN#`I$1y=u*yJhVX$n=PQ&yMcP$6)xG>G$5@szVZ!DG(Q z%%IEva-$0fM<{zdjr3LMll-F-Ii(QTvnUZvj(tDNNZv3CnE2PicM-7a37l!O>mUts z&9+w_#>g^WbcU=~x$B*Jz>>44NqZD%NLp}s$uTRe|2i*Qg2hRKkXr*y{x%6=PP9)B zQUbaJ0aF)HA)72rCil~0P?f}JUG8DA!-N0{{}&`IF_2_AlVGzbXRf*PTf|EfQhpMo zBX{w8!dKLoBhO)T2@F)AAUk1kNM~G}r^x!ivh-#P{Y#Pr28H^<|9n(chHeZz!{+q& zorkR;fzcPG&ud&$s1!77nl_`p?5fCG(@W}HAdrNOQ}!>iqH2J<16>%n8mV4zy#Slv zC}^wSc#X?7pV%zh8T3;R&vW9B+ZggXQS>Dm(|t#!`pTxx@%7ihkK5Tdc#mAuMA#JG zDiy?1yz@s9W^2|~!kRAOPqqrQ;*{W)Gk@!i91sB~FLhshQ{>JLYWt6Gs@vq__0v|e z^>)?UE>o7a(YCrR&6FVS+?_X^O?t~Mrj2%VGGDwcseGMOm=mB&dgyf7kGRB^N5@-2 zAl^7BvXMlD6;odWsw%Cm8R}&UY5ZGxg-ej{hfTJ3R$| zn(+Pbg>XP1j&(P}=2wt&S}Ds>a7 zsU~9+sP|B4D}^$(s#{^JIZU-+HM;CZ8FGMI8rJs4K5Sjtp(;|tE+|ty$6+1h>D(2}TxQkSGc#JW0?e%xE6*yti`yKr+|W!s+vxy8$Wl)HAz zVzIe?>lY^3@yLz}dFrK>V=jG0I?D7U>AP)PF8EP=Oh?l_l0JE}-D@SgE!ESwYfq;9 zTr8$i+0y826N-nn7YDIu3wZw|0oIDTomL=u%~s}8adaFjCs$GbKSgF-v+H|yD$ZPk zCZA4<*qII7VUm&*m)RnIB5dX45h#`4n&Bb&hnvIxkqVTp1BJ*}qbkvXgnw=Bmqyrk9$ZytxjpH#Mz*dDRbJ)4KX`_${a zr{GzwLjB%E2Zk;e%S!byxW!!E)={_?9mA4c2R^2$X!} ze|2h8E?%)3H<5Ai&DhzqS3d;+&xp1f!$E~B{~RyWAe=2BxSk_W#mDgFSgZAHcN>0& zo}E?zeBiL>#m4Bi9P$7{Hl-z-;PbJmWPd9-xo58< z6#-bgawUMJJ_+{`u2!KMTC5_aG|xu8K$*9)S+0sq$&dwcVCC8(tQ~*m>9wiKP=-IC z@S|N_M**0@ZHx6M&Uoq>kyPBf{N%B#Yg7ilc9LZC)ZX=?z*1ww0NsXAMM6D zGwg+8F_af!$4X(RygpqnuMgY*;-%}*v>PB*U| zq~`_~ag-`$mA=Rla#J_WULOH*F6RLoQ7g~|qs%5&>5|>Ido0c9f^viQ<|77u=?{kjuKu%e&`( z&X^NaZ+ue&kI}MYc`L<5GNNldv;)C2$Y~{d0bWK#O@hm3<#;4BMpq(dP*v1SQF-p% zo8|$uTOXe3g~2tQRpE9~!(fE6P&&1>yjc1Tub+|M{dds4YSmZY#4F!qVZey<`9V+0fE$Yan# zPiT%a*GhLKPkuzY47r*`Oi9-Phf**%Xj9wcd$y`jQ?!}N$6gqgn;Y4kxnc?49!hQt zFY;#y1JK*^Js6oQ53l!PY$3gCaBt0kGh2-By@MOKaFc^+zTLMDQ!oCQUe{hCFX>f6 z7T^p9+U{CBey7V=fgIg{)R81sdhisT5ikqgh*U5{_k-^dy4Gg8eivdFHm)u72)#NC z8hP9tLVORdMA!>Y)eW6a@`V-#^%C8PBRd(I_|YUJLGcBLLmB}c5f!w+(1B(YAL3pA zNnQd$pPs4fV=zO^J9thgcRLFe`PNZ5%E;zwxqI2M6O zL_9CjaY9xX1*c;qo!u9yJ>Ue5yd zWC$IZSK%Z3M7WW5(*`E1)+9D&7nT{&h{l00ECk$^cnHggIdhu&Ps+&?zk$VW_DlZO z=yHtts>MM!PDuAq{X;r~u2D=xTId2pQ>Cg=WW2GKj%375 zbWbWx?;t2XVFscgwe`vyrW^tpj-};BgosOJ@!%8RW1f^F#|YSiW9YWW&$?S)X;NJX z!|%nP>%TQ+_F467IDkO`Khx^=@p4Q&VYPjyVJ@dwBphuv#OTcj65}Zz zs!Ad|_1SX9ES6!Tci(&kMuc_n+d&x(sWFpJK!jNbG}_bsUFL1s7Y5}+LMj!jHp}y+ z)8wHZK)QOvqa;YJxW8QV&9=z|+t^{P{LuJM!oL?y6^x#^I-kf%VTY&mfo|FtxV26s zmBmh4xU}N82@~4py@rDm)P{!yV9RAj2Rpd;E(6oFkWLyLB!&deR zKn!h=@Dc=%8f_wQ3P0K%KCD2M#PS(Jo03d)vTP*IvHs<`n&@6Ea z!ZtAVLP5>~2+gCok@(%xrgWu3V?uC?m%kOMMrm}GK)`bdCSd$Kf4C%%KdD{vuMv2* z|9peLv)qpF9q;4Fl%4LqNf3NG_rmi5ikgvs?DT;j8#vCd;0Nd>Z+*{zoBz)Ak^s(O zKdQYJuxJTj(LGS%8C81$`x(K7f_r#|CggXF@uuxfhE8<~93jlKDmCtjX2TMjq)Ga$ z{Coj{c4`!5IMWjU)Giu_d@AyrKv#6EHCc0 zBnbYn8KuBzC<2ixaR?rk2#fwP4Uy-&gOXZrAz(YN3T|!+z$Xs|sY_3|8qL)ar55Zh zt&AIe;dKP}^AJq-D4dRknk9+Qph=eNJJ)0xub#7DFWtrsFqvvt%aX`B-r>~nHPi#y z22^`1036drVmF8d`nTbRh~sh1+iT>ZcdJwL+0N<@Yi7M}F91fp4n|w6?v`V9sN!iH zQhqwin-skI)jab>1%`eiw08+V2indeA2ej$IuA$$6z&<+*}HK#b4gJcPxF9q0s&7| zxqDHn{ela91m5HKqEd5F5Hre3xn;f5f;YxBY2AhkRn~Iem+|vd`s{nR0IEF#z2QL! zV>>4uub1X`l;&Q-&`YV_79E-v?d>@EuA#=*2l<(;ey;(fJ*{~BegHZ_LVBil-BuX6 zr|#>w#%4(bve{+*XN%q)A?%CO1lo!{VW4qCh8c^gB;DPnAAb|h@ziBlT(U>=i2E1h zt)ukTet#?=o`RKIqoz$t>f-7~y>;VnZzw{4HQbnmwnfoJU9eN0mCU!C6a9;yz$;;B zFsY4tvzJk-$5q__J~~twUe5pLA_mnJ zNItOWo!)AoSA-3C{o92|3j8X^z}UvyccHui;g%1o59+ zS~9r(B>C^g4j!dXg-7CiSNso43Ol{O@87~wK*fKrIMtLm9`V~&1{e*RzMmK6>uP|2 zsm>pSHfCFO1$5G+YgbTI%FLG$06}w?qwj|UwXOfkw&sny_vaU3Pracva)cxM-jC80 z{T3Pg!z0{dQR zrauE2(Hu$N%$p5eyn%doRo)LVO06}wS%}54e-@}KjH}dk3mZ(k1L>>eH1DI7RFbAY z-UcOW_r(8OFa33Rl1zJ_g`mE4txKLeLzZ|dgvqt38 z0pUXsHIY<2!r(ld$~Qfexm{&|m&1G;Q}tqoQ=)t!O?StHBW`o>o%wuKF+4vt{P*Kg zfEc+K(@g2<0yE|>x=nY!HBR75W88EF;ix3(NpFa&5Q*4T7t`r|WtX|EptWaiZQ5YZTAD>#-tBK2g8l`^B@K?G+Tj?(b zsgls|B~U)x%kl!>n5LXSNqmZ+OkyHtJTK2_WO%gLlIisK z5C~n9_jECcl2ozc^m!?mMEX@$qeA7y2$^|@-)Beg@f!(*mOpz{GsKfKNr(jTj#Gm5 zHpu+-ITUKCW<3qSP!+;bEFGqCaP-fg>?h8+hJEVJ1Pm5AHIknWqe0(lqSqKU zgrR}OkG1=Sm1;Jjo(#DU-DJ~t7z}@CVT&Hk<)I!*cAz-DH6o%fx(IkTRR+5o+U$iX znpbrNVf1Q8qa$w3pcc^Ptb|?xF$zqb&=dk(tL&Zokvj7a?#HB2AuCBz|Ai#jvNDv3 zg<2GyLg_Tc?O2Z#0;aR^a0S(u%P#;GY%!qFWppvnsi0MnJ26p#TuzZvg!Q|V2EfjX zs-m@hOmk5gJJ)~>*_4umx{Fo&!MX}lX-z+p7RMzNaaZFY;AVutX+2m_m~qhFZ>LSk zM^ZJM%rFk2%Hx;?YsZk$G#0Cb99$hUM#U|?n0p~_h=9`Jphmog>;DzXaQKCTL{!9z zz!y^sgG!-Mi0x4(0+EP);8)P%WQ7JT+JmGdTH+xNjUW9hMW5X8KLRWr&~EbsP;S`| z@rSo|gb;a@>hl*tPnjn$Y6xP*;fUJPK?G)ZU%7;4fAhL!$*uqQPk|pdLAptHbQaX? zA$&<@vNqG_89xGp-g+_fC80{1iGXUoY_GLNIvB0q1mi4U|2s)GnP#5l-u8$j1N}s% z*|L?0Ql{JrmCjn{e8Nt3A}il^r`#Yg)8x4p)J!Iw;mE$zEgwGbLiGOcF)s!5G--jo zNnJ#f$FGeI!KNai-{Q>Z>syjJ&AELrk43j^OIUI%@(mkoB{s6rHsYWow^4`uC%NsL z!yU7YInL?ZlrufJ;f?jEX*}jdGx(R+&EriUSi}VX^9`T+(zi?ugv1>;RqVh90asju zga6?H5lMJ(oZqg9l^;`axhI zx*Vjaub)Y_Z-fN_`R)b;O`LPP_Hji6UE_v(jEg&3n-E`n+QNILyJgr}VFgZBX~NlB z>u|NfM%?W*3{!`U!^{yA_{V1o+2`N>mrqop2v5iKAR=QjmV`{sWRmjw*TI>*`4Kpq z4?j9Lzpt_nR)R+rlkrVGPx3br@Dr72#7Rsd(twhTERdw2NLsQ`r8P)q(vw1#GaIY` z@iRoVQ9r_U6>2Rqw~n__YA}AyH;LoF{F7AJ!s=Si<*Zu;RDkXkR6%+at0Fy1Qkg-e ztkQ@osMg5RRc~CCwcLcNYL!V<-A0qExh>+_)iAHNr!m@gs0rTcc){N7OrbvPQsF-9 z{-U(+nRfWRx7*|Uep`_rW=;k}*zM-JblvA0(`}E((&ImB8EuJ8h~qEf0M3DqAdYl0 zbBa5OyWLGZ;z{BuPsw@O)5Np-Gb4=!F%A}^Nxp%Y>N`d=O@WwW2BUfAK`ge2(HiR* zZLkGmqiu{f+0iDBAcs(cPY|#XOmK-q(~6MAPmwm02z6TwU)P8r_ZT525%IXBHQ^(& zEfQBqPh5%D+6=2#j(K#2mWl22kk?MThV6794(`xAqCXZ=Ka@+^@MM>Q@|x0k@+yOn zUnPVhnn9^$5tI&UA#}7DV|S~A@TGc;jkO#?H!C1?*NCwxHbEI|IKn6+2pDY?A!7w1 zOc6}hT2rX8-c+(Sm`06_W{|a6C?P+ZP1aU(sIlE#vUZur87_;Y#uFBhwbvqY_FF>8 zN=wN)WEnM{vYf2LR+4kfdUBog#EM5aDT$CjWFlOWgK|Y40oRnF z+*3}@EoV`lu68?<1ZOx2?x?3UMl+!`Iw^y(qHL1L@-7$rl5;`nx!6pQ*0e-jG?KhY*Q~9VQ`V$;gp{QYRHPOGArv^zx-iM;S8Z%aV(1?DFL)L`8}n zR-zP@DQ7u2kw+z}Qq5}7s*{gjzBU)qbw2I3z^8Dbiyn()doI@PwM4e}YTaQMc-7oU z=iZ_||Caiqhw3YysXsL0cYKVmnWF2aU96uW8)o^&ExLb)9NhJ{@k#F0yJTLw+m47G zy%dAxIJ_01Uynp46h0LQxkzrMFjI?_+w8>Q*7JBJK0gf%iX$i;8RfCKVk*ruS*u*0 zmdbQ&Ty|ob&diggRlX+dk4h{A2%o^x87Nh<5v%7Q*32ozl}pzrx3gbfdA@vVm>4O+ zf>t4gY{QC3i5At3C?+pnTuq{cU35vE`&mK{bA%q}2|X!*cD2~C4!*2sby@;nEyFI$ zp=$%HR}*9NdgV8pjG8wq2W>HGeN{1JODS|)F??4ka$hm(Kq)#zIUE*xdn^=xDwG^^ zoQM-oOLC^C;D<76PKA7N4HjOrR&-=4yAoA>*_we&Z6HxM5^lbg=vo!%+u|A8hS(k1 zkv*-^nD)20)>uOOdlpaU;g16AY!S|t@j?au3h{CauC`g%I&i&*HwSQMB)AtT{5fJh z7@Pe#q5Lv4`(-Y8cBXKCB}h!!+6G8~dG8@e_!tV*Cujxp87Oey1>>L~x(5l_3n-`# zVW2yPh3y3#d=Uad_Ye__kdV5Ej9i3*(iwNuC{OfqUKlanm~lQ><^0I@m>hNkLFB81 zQtmqrZjbTsDn(K0QZ%(5)1=u@B7Q?jbQ(#a+gvK2AkHBxRUkzW7f`06qDn4bVGvi+ z2XT#(gi4i_D1o6Ym5k9?zMDFrK8}Q?j@;pUhx7XR=bze^9jluw?V7Z2xh^ z=2^phwIf>ZVYAP+j&M-L4o~9mX|o5j3`7LKmsBO%4#8SkcZf!4sokI+oH_e zfl`x$wNpL+5d`m;6xFlRw9kp<&r8$2B*ow|SNk0*IuES-RP`9q*M+i^@ZBb+RdxeX<3zStLpU7+tW!#1$voP>1S7E zP9;p^GC#;+fl}EMdiV{bmcX)>nYk5KSmQRg$?MLy{O)}x=+5`T?!6NA;H^<_{mWBnf@q)yi`-xxJgNup;1!RoktH7ZL8OB;NKPs8sHWZ+;#27k z#J6(1gKp&t5-d0!`raka(EH>V$x4>sLvoD!{~%D(mRbyx;`HfBpX`+uI&rCBCDuIU zP~a+J`}SR8wnAEhR6CQcNkH$Qr{I*{v|9NpDyh>TIYYt_)o&;|BB~&P?x*onX)Ezc zC|w%v6@>d#IrqsLi>DGzQ^(sb&8D==Gt5e)EpUu7(n>jvx)xBZr&v!hFOHtC^N3Z5 zo>O7^R%Qu41hJkGPYvx`ix^5g2u?Vgu9~`)AIg@xG8Pw9zk)Ltr%{og#J93tlRDog zo_iy^KhCuBPk$ihsTT+@k4s$Qe+eT+s9(uIl20?KPwNlV#ZQMs6^?{@E2&GrK{JSu z4B5{zST!fnDJWspYJo{1nbPmx)xuXl7SvVW4Etk;l#~Vy(YWlUuIu%#%7`tfZ$6ss z44GCJQ!u9tYmMxgyP8B1jWJSiN_^qMWDRz_?Knc}MdD5(uZm)ss9CIM zv5{p!CNPW5mM9{FFLPfomr&!jABdJi!(wF4rGqKI9h}HJ2&9*3oKG{AdCfM9EMB%~ z1TkFRhtToRXJEGku+%>Oa&@SPTb~owca}5s+8ji5y#tgiP17zqwr$(C?LD?_+qTU; zw(Z$t+qP})nYr_RIQKvIo|E0Hdu6R$SzXyrMLZE15%ou|C&})<3fZs*xx$pdbJ=s5 ztK*+nqjy<}_vY%m8~I0(?`Iy!6w(8Zs3B2FM?+{wL*P9XZ*sLUW)#Uh11qymF5xj( zHL{^@=>}iooZ)(6ibo5RCTrvLa2{XCHHlRLdU@!zZ@N313HTNr%^K;4!v-?Ki#V)> ziRmOQyww|H>++OSPm?ES*W+6hvdmozzbl59q!n1C=TvwbP1@}EXWfZ&ivwHfomf2M z(@DY&Jdt(se7%8i3t@PNP6}-Sew$N$XeJ`d?-C?;?h0hvOh32y7#^BK?h~f&?!Z+D zBkUCKtU!Ul7TiT2&PBqxjk-QiS3Bj`d}~tmVIZvVzVLm;66q*9+Vfz8z}He6NJ6&` zdd9=t^`ZpHtJ?OhuZzUYt0prnnNt&xQ$8YvqCEBF3(-F1+4rIxaZ|O&dEw%wb$y9Y z7zO&c{nIy19phq<{g#qhK%YE%h;v9s7KB}j+dc6B-q9ZSNViAO{vjiEg6b6c!0zl4 z1fbb`zffh->I zqh12Oz{r1d%cuFc<|Q+c0=={|@~w}?)t&X|eae`U97WIcyNk1-8>^@2s?wI7X{gG_ z^HJ?Kkg#bJ+W}`3q#%-r68BWWEYG((Lx&MAi!m455(ApN)@122_?EuD(-ya*b)S@- zXwir)d#NL+Q@T*@heo{`Gd?=uxNS#SmTKIfr=R!-_98?Y@Eqy zRx2Gguwc|kS^dGJeuzI9klI67QM6PK+cRwIHn&w~+O8U3+T2Pw{9Bd32_~rhWZG*p zZ6{8fZg=|2f$)IQ)G!eCedo9v)xptw= z(x;^d>(=tDV}kwo0QBlC{*u-q6Gt&%7;pbRdI*wGCCQ-{P+1(cZ;E&sXo+Q zh~JZyHv;PU*P~zK{;;W%u^TU28(YgRy`(LyU@=yg5Dv3|xggKLV!$G6TOqlC_K^C< z3)F;IA$bm1b;hAap}oX0;i#3mmxkz-VzFKL=qrGHX>CuH-_y%pdqGmo7#G7d%uY#InVF-6-27wGh&JB}8J+|lu$K*|XcZ#ah zHRp?vKyNW-hsYVQ1i(3OD*bKv?Cq`1Wde-e5XQhTuA>xAePpi_Zd5BP=2|w$$RBeq zjvd$%K4sYHBMR$5HUfzY*Faj&c`TvtUfmF!juf1cLWXp`b>OfWlP8WyT+)>p)4Ng5 zGv0@D%+vL9QX&*g;*){^123ukE*CF$PGgo_YzI4m`;~aCgKvp{TY^r$<2FpLXMHi6 zeldQUP)KYj$+u!|c=#=up;nPvrVC@16Z*!Kv}ED7*>mK-n>Pbj7qC5f2*{s@!E2G~ORUtAM#vauyga@jq2Uw>AYAIF#l-(!-}5G! zz)jqfMR5M{#);>n^T{JD$P9Bw)C8uuNQ^}ctNvZ6g0%(P1dPQ~Dz*s!_`F6wQ_vmejPD11n7IW$9zVfx6;L}XbK6XYf(W$;$0+7}1ov zK~R|gyx#_mgd3#=Sp3O(z1dZv8V8S5yONUW*sLH*eI`?t#jcltYYl&g@Ex-Bn>A|z zwfi%VPshD=*ngCeI_2!P=hDf7jV{X=NEvrA@Tla}#!}EYx z0I+QVqVQ%azD>jHzkaBl+HFO+A!Wof*dtY7{!j^AAq3%L>9@<6R|?28i|2pW~ zJ(+EArw==^MS7$f>F?9`zdf3Ap}&zGjo>sZXX}JOuUa}X%9;~c3w6S_;4Law>Ctrz-kQo+Cv$)^9@u>^OsoH=&^5{0Wab$%GZ&{YShw(it0$cI4}9=r)I zHj>IHp1o9r57!gZ2iD&V^*d`U)*?3wE>0&6hze|Vn_4l!1sc1U3|#rtW}Mg zYc!|i)3CB@@ku9K4@~axeR+|W%QaZ|+L4wCtK3<8(duGI`s$kL>KQE|>(YB!gwv6G z&UDFM9aT?%Hg!-wCv=E`o402s{Bf*5*|QrtoObI>v_nH!0U<)R@gzO=IlqYDY58u` z+oVprCGlsF<)5F56W`;yw3JBmqChdyLuF;A%G+^XQF(S=t8ql{$juOJmZ%FrII}sZ zL3Q(2DY>j4A!&sD-hu$QZYQCkz5p7vT)9v+=ba>%cPpcI+E$C176Ia)bV7o78jbkC zE6I13;4(4gYRhc1b6-VgbiEz;d_En7g!T?w)TIPQI*=@T7nCRIrh@2Lj}y8!d8EHM zf$Va*N3>y#^>vrbR^oZdJ$a+Lih2tX8jsaDy|Vl~Zi;N5bqpx~KJQCtqoqeJZ~F?W zNn7o@SA}|O47s4_T`)g|-92$kHDadbw1 zKPw3P_FG@Z#BORtNrq`E+TTs6*ZNW5DnKLX-<)2r={<6mycASfcxLu<#V8*M)6v`? zHALv6)k|$w`|QsV771u_G!#rGV_-?ZaD?bcR7dD6%{%$V%vzQGSvHc?Lu=$|rm`{r z)L!~egP^fk8O(vHQ$Axd9#4dYBXkQ^&G$T#4w$FdDWLM{Gf~_Ba0eHHAPh$MUmvk# zXU%x)@+|q-%&`Fx&24K?Jl;S;O!q}p)|Pfm)YGi(Q~B!9VbJFal|zgp&6W!_!lkz4 zioXQ8FfRvda|e8v-t{R1O8tgBio!48wn{#Kl6C4UHNXMjLF^4&Eq@;$fXLQ?avH8+ z-$gxk_A=u9S@z46$&*pCxGWwR>r=WliW_W6_)-q7lj#SsNvPG@jXHj_=1womT9S88 zeE4gdIB2Pau<|H3CCS$OYo9OZ!FtO+>;hWjc~gvEvmD=wcZm6|$9I#wU2M?`A3yP^ zC!P6YqkT)j10~lSUL*3Fl)wDIYfWidJE2;P`{59e$Ne4%a3$v>91I4O5gZc58Asc@ zfFyRst(zpHeV3{3ZmlYnL^82xp;$d8m_+hfGLZyVDgm3`>oaL^5>!n{D1ZPWkbp-S z0YtD)2>~RZ}gjG|X z$kJ%l*lFFdAP)Fe0uX?jqnS;-*_CZ{%l-Bc6auKnIXGy`50?N;lH!!3?Nvl_lOG%m z$Ag#fTefo>dB^ORi?|iE^LO60x;>d zC*or*ku6mw1Fo2eT@)`s#DMDh*Pu@T+Y&cYCqskSRYF2f!N~ZFx2A3u+8Os=u=IG^885yE0C@NnwzcyauA16tj8J zYc?IJd0Axobt8*I@QKZx`Zn|ctL?B#4gi)g2|$P`LXk;CnQ2C#3rMIdN*1v5a!3RS z#(D_~$2*(D{OY_qTBe4q;Qo?zJxzPzb2#mBU4g*;skx+0!PAl-OD4sZf#V3)mY{g6 z2Cq=lBMxVc3=fWO5J$BR&HI-W3W*7d3i~A>AvWzHxiq=Q*OmcsjbP@GQ?EVNP#oeS z$A%3wkxJ8PX|w7zGXa|gqg4cBsuQd~o?7)Mo~wedBhg98dvXu-W)mtbG$5vha$OSF zNqr%qsZaowdM-vEg)t%0VRv*4+phaz8+=;3&GBHG*e8277Gc#1Yvzj}NZO*`v6Tpg zLfUm`_+pl>NOUz!!sZ;&ha^UQr`(r^m;+FH^{Y zP*3L*qo1jZ{({1xF(lsnE-(xJVq2b%=L>edd4vOEyFa)jA}q`+aGA^mm|0m!FOCpZ zKS{DazGT+BDz6v1@a0kOmaVTo@ekfB1L(Ge96nFfk5YV#}TG z`AlI>KASOpjkY~cy5Y0i?Yqrc_?-HDIQfCY@>!yFpAqB{NkA=F4b|Zu0>zSzs|yye zqsbbFEE~DA>Dcc9f*{BxB2Cp}(M2Rxm1WswCd87ab)8WSnufI0SS*(FRj62CXmVM$yq-^I?E16u2E?}zVu%Ux3i5Ld4wJte zHf2b#=%?@pxEeibqZA8u^?L+K9aB)L;v$R8k^_y$snVD+l?I7YX-ZRK(irl$C#4hU zV}^K~Dhcmx>ky+GDNnj58?c+X;qiTrkD{`NXqt#R9ZymRPDLZYW~xtr)|oqOBpK|~ z1inr{G#;gBDcpc_5*kY(u{l!Cjv;drt#azxzGHI~5QZq5fHKp7Qx}0$LxFXRffn1G zi(3v^Zjd%VR>)PlTsED>WIcKF(zJEa>P)IN&m>9yHG4uY*chvAmlD$EpP|xNnXCQb26+}p!jfNQ+>gycBM@K2k zWHK1hAG88Y;D_w5q3JsJQ#Y>LE-1NYRzKiKL_1(K^waysC*uAH2nL1#0ZE3yEW$Op z7oG~z1EC5k#;X#C9x>3Hi%2e0adr2ZGig<^bq<_45`!~@+XohzhaR~LCL4<`IS({l zaNHYe zvEvTjmW<1`6l!}~-upn^we?`G?%E(}Ax!Y1KLM`lz?jb*D_{L{F5Ct-Wylha?H`!? z70upvJ=kI}PJ7kKHk5CAWgPj%zlaWYA?Rg>detH5A38=~kX7w+)I^(S73S0wo+w>v z5iD+}xDL)Tce`VinAR1r61|GO9=Rn|iYmM= z%mc{@^n@;_i>O;4)jn8DH>O}6eVTyygPalpfw4hRzeITG6H;=eIaHC6X131bVY#b3 zh!&{L$7&gsUo@*V-EM6*9afZFf4e*rv(ViwbKm&vPKH?dKPLn_V>WQ5cZ5-n<&1g+ z$h;M4RWd|E9i56C2K>)eK>#PS2$I-`gryb^%lAIyx>bmltqa8*C0wBCTHR$Y<=h((&_uP0qhM zn&;gc>ymfFT7`D1pLTE3Qf9B8!R%htHh1h)Fcl1F>1>c)wbJotprHV=eO#K`Y_<>X z4wett4$zOcd#<&CLACM42ytu8Nkuj6c&Se`JLo35S;Q9q7K4WML#4t&QfqcVQtM_x zsWxzym2Nk?F<$6_TVQF2r3ndl22?C$`Bx<13UHMia3SXKFgja$Xt~S*7QBvT~S>`Am&UVDGMxJ&%% z7KLsigJecBNzGw0S+-${QwM~msS0n7p}OMG@Zpw;YmOip=Nepjv>6}w?Cg1i<3B7l z3EE1US?>xOS}!QfsdI6-eZb`An`7;CZQOX?no;!aroE5j$|HvsL~1INmyy$+j2gK~ z+f8)pQ&b1DRWG?Hc9`(+@bnON_p}nd=fh_Rh~!}Ja|Z$E2+TTw-QNFXBnE!tZ-vBYc0!2G!=U@&W!NPq+%+e{ zU=)Zb6%5((RzG^TN%e=cEpuV>#_G;4EwBS%VkuZ0qot}YJA)++9ou_&0}`M_kg_Ri zWoYLsm$SM)!^KW#-9$MP&hYp#fK|^xxv705kOf zR3Q;n1>1t_hiY97S99~rSj85nG{@jd&cRIF1y+@1{;0q?`SjvynHu?uMJzAy5#QnR zl}duVzlbGdTyC{uMf^x=vB znk!E-5g>pBI4C|LjH`V})$?^Q+(?eW46#2M5xE3#96Y>!b1NESV_*rLE87+-j(KUeQZhIRr^2lp&iOGyc(|s%0f>yoWbOC8S z9E1k^eHS0ec=uNcpRd;BCda~087K?YDPGB%IS;WIaozMR{66kug)S zj4>C=6IDY(^3aA)xR(SQR3t}uX+|1Vg?|dkMjWnwH$R9bcL)l)0wODT$qF@QDW8@C zPme2V^RyUPm72E~LpUg%6czA)9R>e<>;KOV006{)y1oBB{A2e@@-}e)t9t(SxbEP~I6T;qmwN|C#_<+S^ zIxI}L(LZp@g3f2{R};8~A&+Usk57N)%p=OEC%@+^Tm(zoy10&IlqK!b3 zmfSgex)U#3c=vt=7@|L`bsc`&A2ze^HF!k(#?(%{8n01n#SU05o|GKs1ssaVM4MAh zY3?fix{D!QZjRYo;G=`73m-~Vx6C+FMWC*J$NsxpTQtlKcGB2} z?$vH2cZ7^pH>6<*v!?wzG|RUADz@{w{U#s`&*L(p^peNurX5|^^T-blhtuOKhW!68 zK{}|J#XsGazFn|49U3uBOC#?3Xwb3-j_yGHf2u33O4yh$de_7EJ zg#Sr$yPvTAlt{or0ShBf9A$BWw&E(xz-=7IJ)+JtM8ZDHI!VDQF&*e5C-|}J2}v*R zeMKpxL)L>Ol}e@ttf+mDY;3+d;A2SD=Vgtq}9U<+j)#b*A|M+v+40VxoX;8zsfX`^-*x(cS5;w2A&RY4AII4+ZS91eR-W^Y0y@_Qoss&yh` z(UJ5M9+X0~X!DkMR@rTiZ@j?j!s30AdV_NgxU95TOwkC*O-*)!F@#v+Q!)7+vE-a` zst@>J-c9COq9f;59F7UoBg@1s^}Md$uIr+4d_kRz#%uR)+0Dc0o7+t&Y8^){+}EDt z)6CC>*cUVIZ;p056MIuQaIul6V38>ECZj+>u=_5HAdw#7qWPe{#^u**)VmoO!UvxY zvv1V|JcB2QzOUlDlPC>DMBed=L`hFw*^tu5>nslb8!U^q2O;UC^NLnBv`cc}q+{}V{g|~vy=D3`bBR$|s-YUgQpsKY;}tdSww}J| z>GQNUe1FmB414U5fqdb!k2U9C##%1e54gZWV?>Tg>IpNDDS0H0#4`~bBZ;)VA#fB= z&Ei4z*B*Qc&MF41{ixWgKg7{yIyT~_CYuB$Mr>NYS*#T|o|rqA&sTH(s+XU?Y(;tJ zReecc(^}rBS#Q=O^x83GB1F(79Ak`IB^}a~<0Kk8RxJ*0)+rZsgLFO@YK@@%{?7PS zG=1Zl0dn2FNUZJCX;YxnMV!#5`2q+?eT!H3yf*NgfNU;4qomIGQl!l-$@p3()mZSp zoB_vG`{q+3*Dp`+jC+8rdDCY49wGe^AY$!W`l;FF8z9WsEef(=g!HwQn87SG)qLy6 zu`8-3#a}b)X>COop~gX6FwuY;r`wvEQ3e@~?zH7m48MJTz>fIGx1QPzOe%emW~%a( zKK`#iNXA@`oo?D3Ij8#__E}Hc1!33}5=FAN`8S@@Q+P*KW{p*6$wg!jIE>CU za2!X=c6djn*_p{tswmWfc1+o`>gO-iNCq5#LCl~jK!l%~DPaIa;e?=g{=P4*;Ma0xa#Q1hyO_te{r?NBv6I#EDLrzc& zwPD_R)bcd45OeclS0ls_5DQ0VCU4-r3{P3_fyi(-t7<6=ouOalu46!pYHKwr8e`?` z$^+)YBivK6Mg~61G2x0L?>YyV9D#Tc*dj$6ig-4BWs#U%W_C+MuBRSr69I{cjyV)zc)`}0pQVGf9a4hSx#+DZ_u@*9{M2cc7s!QfE({ue|Ojp8s^U$=^5k55dQ30^wk!n!Ia* zP>$_XG)R(Aijs@NVqNR%hL_~OzVX=Np3S7Gnx^ew)K!b-DJ8JgOi^y9GXP*r;e9X$ z6ayI3;oKSyZhfcJpSKuPU8L0#>+{PYEsp^@0jzk?^p@sYVyAoi_|em%B+GFl9gNHM z!LU_>Xyvjx=e5^LN5l6U^F4bCF7jxsP%^j@6e9B$pVX}Ywa#c&0+~J_7)cU`O-3cZ-;P&rkyArtMAHDmJ3AhFmzILX2`P?> zFcLI%83rxOh$xe;?KE+srmdp58rdgjsMUc($2LyQTHl<6F?Bs*?Md8rrl)^!adNYN zKHrG^$8!oI1yaGFEnJ2Sj=~Z&XKZT#DZ~(H$RRk7Df4eSAXKkhB46Ij)jen2vRd5| zFKOHfZ-_Z<1nQT&B?Hp!3ul)L!9HCUs*!IMCuG`^o<2xMXZJvNCn;fjpO_n13}361 zotRugb96Ri=zFZ@3!YnyPfCow{99b?>j2h|<0MeJh5!u&fdmCoIFW!dzDRI$t%WbfW0k1kfo4*Pru>@#}GIB{QSf+V?%ukqNejH#ZhCwuBNxDHSD4K?8 z$Vt^hqO9&iD6Xr=3?8`l1Num|{WxbD&mLwi2x%Uq76=SrSU4d?B)D3aH|8foR9PLO zkxU)*b1xqUKxHCivMdy3F&a${4AYq7!!ThQ&pl;cJdr#CZuVEDd~F!K?<+C^xd`Un zhc9h=4qV6U7=3@lF1uePj2ecL1&_#dD26Er4~Uo_P^@+y&u2{6C+ZQK!4o&Zkznq( zgEUP2AmT5e2$BRDsB&Y0P#|0|Wpf6Dz7TM1IB$?$!gyFjBq5RTgm_^(#rt?d@c<-L zOQ>0S3B>i>W@2=y-{#`eCtgyZDf(gGn>{9oJXVC@To7D$cargbs*O;HzGQzSR!jWD zc-E#0Aa?l~K|~IE>>WHh)9D6xMRArduZkVUbG0Ex+*L9StZF%FNrID-Z?*6rA6Zj` zosoi)F%OxzeodmoyESiQMEJNbbz6@;uSOEb#Km_DI>IV{rF4M~osF-rDBj`83zg(~ z>;5`RJ$m$93kx=tU00%3?QR3>oW}s7{H=}aEFDtbmA@*#;|ySU{e~kubd^)_s4BXs z)+S|zdN29KrBGabu7*Z2?g^yJ?WCFIK}xneqC~UGKXmNl@wGXeBdot=S4g-*M{OiEY# z^n;Cl!;W``*uZ7)q_g1P^iS!`4!vasT*pt|e?UY7x)~xO1BQGBeSbIaw*zAfVX|zf zd+3|T3}Xt-@zeNwZ@+8o_2m`&gIm_E`h1p_=9Vc z3Q|!dG~V;KZTKs%Ea?vDp9T}?m6McP6a{5fl7n}ZkBK$Fp02(y!qI@8zJsznQLMT2 zC7HnIyifBbfJpT$Q|0u2Wo2w@&-11L_Bmr!r&&|m2aR~TDD_Rn)aJmH)5b_uxrVT# zMD_<;@AwIw1F_1rVx6^^t&T09{`{i2YCGc4%Lv9SCKG7xdT61Sg3Z+l_lR`-TN-1N z!}h2dmtfmsBh79P_uFW8{ks5~?WiGab#HW|8_QIv zrh5Vn$^-XB``ihPaPUc_XU)#`W@enO(g4@LiD2iags}Y{(T%k!3ysLiK@%go4+xyr z_Z8mn!LN^N59mc{wbuIb3?zssK&*C7C{Uu2nsuy^K%$`q-WI_3;p!6R7!*k0^oj90 zv1Id%IM|~L)QSbsK~~vP4nbHhnN7DA5)Nw-qN4DaI)8^l1;~m!MO^3#=yT#GMKW(8 zp(;k`#6|`O`g(_62AhLLes@UHF+!HtZi&&&G}7RuT2i9OkC&DxiWD+5{-LwiWyI}P zBppv?E|a$a#~V4i1@X4(;G7x{v>D5zTB6F&RrcB4RTKEHZMyQbXU>bEh@M}QUtS#3 zhZK*P{+$)378QcB#}%N`n(^C-X2RPyzDD3 z;oDenCq~JqUW>Fm4Aq^&AVzW0kMyzjYzJY-@u1V+8Vb_}>wNl;(QBWL(#lcCMy(<- zNT`D~iebi;NA^pF&Qs-?hk!z^9}jELVh#%VK~ChGTd22NBwH&KBw^1WOKRF|OeJo% z`Y*jG?adT71HmJ5WjIr#LB;`;bZI$rozQZ+<9yFf3#|^%K<{AF@ON?%EH*Si5S<|^ z(CZ`>XhC_|B*1PGUKD=<=QC=P;s}A!6k*Q8foa6ZB}1F*;}qb7hJ9^IEz>_n6N|ol z*`&$C&5Ny@&znb`4;;C?_{Dd$)zwX(u1nY4wq|dIER*xwo49RO&%1XV;CxcZx}I7# zVb`I&o_sk=@G~B1$EvG*osKU!D~<3%xF?1Ip!8`RlTJimFq!A{W(@2ZnVgMh8oI!C z{BuM4f-XY@(27L2*=r5`U5&x$Y%_|E`K4r9plf7W3M2dc5UoD5ERIFcs0KYlshSqe zF3YthrW&_{N`>xMN;@A((oKYjfUkqr2bj@@0Sxb2+(GC#8rV6j32mCkO!g3wRTh>D zNG5+*e@$5_MD4_f!5+p`XeD9H{BpA4izbv|OQod^1hVQltEQP=&;RrgV;bfcb|n*y zusoVzBad2RIW#=Q`{ooRG73tU7`vR)%Kezzxu0y54|;OtN>Nx~tKU<=I^!8;2MPdX zLp+h3Z8@8AnDwc@2ZhaQ4MN}p!5fqppoe0?sN3&{hY1LV%i9Hsh=w&JvOrq0O<+=~ zSh*QGo2plo&Uu;=s}fSLV#QH=-)# zf<~z*p=pE>q@-tdHqXx!BBZprFfUgTRjCQVy2f>^e_pmX>2TTVxV}P8*_>c#o9cK# zD>bt7SbGXIaY;nV53(f76K0YUH{RBz(Hm;gBbU$#N zR~1HboYyr@bez{zR<@isbY6HLSCwYEAJ?^Rd>+@-knw@AvxM{^P{70q6wJ|7oyoE$ zz(tVCW%B1&xw$06K1w#OJ8tqvetTXQuWWnX5a<3>cka25x%sPyCBsg zK2`MaRE8}rPDn_Cib{znCUZ!WlPQK@-;`fzU^~Ue(6Rj&N>DrF#;f=tws#W_TA}lsYNii;9F;tyN2Z6MhV0V${ z{=4AI5!pFa7MYrh*EGyIn|*yosTUy!%s2z-1R9%A&!B+5e}!}kzvd47S> zkWjQ(ZGl7NL!ojC|E>cKREV-#KA4K@cuSU|OxSWZ;fg6j6>EtUJVn$n<)=BaX6)zL z+Q`hXuX&p*`M=-^(&NbKsPQlkJ|!%|@4840fEM^7t1{`&q*KxtA)z_;Sgz(vML$&` z-&INxxLsmM?3ci&`?CYwV09+PJjk8*nvh2nDu=h2uwwY?cjG~@n}n(b(21<~B!jRV z$0F$LLB8b?_r-ddtd(&7>_Cx z2(t6>1JZ&`uG+`W%nS!$;|uQIy}b#@5bnx{BA_3j*lVLz)d8;E0csO5VP)!v9ZWP4 z>L&oUg)OwEzY;iVMl0R%n|Qwr~DvExzRn&%U3+7b)G3(GK! zxIW97b0VN9R;vQT;FvF$D8>0=pjhtIr$^6=qza>FzLTL{m+r@9uC1aZsVcAV(dF=~ zEiIyFY&e#QVmMqKHg+@|(7eyKn5&0KQGsUmQ|3ofDb}ACd6xNBWC9#h6QGt$aXeW_ zS#izh^uRplpU`&i`H@ySTgXVSFQjb47;_k9Ov?2C!tBRMLj!3GLJJ#|v=SZdpU^}v zCYbQSJ-t*l!*6@6sI({C9!P_H^|v|1)tQwFvZvEYo%N9?eX5|f>*&KTQiAwa5`^97 zR4U`p!@ykbS(iqC{LOz~L8E6CRejEat-QW!foq!cizeoEEm_Tl+^VAc_oneBUu-o* zNt*C^+3|D5ZH2OQc)sRHt-t5WPU6(X-}&qthYf^`PeLq+G5yg{p#az=d-IEgS$t*3 zU?QksB&lE~s$eL~U`qyxNt~&4J4|*ovFNaj1_|t{c{{RxDA$aPodLWy`|vUQg^LD< z(LPG_w60x+c;{keH>t;B89yqRl1-b?Q%0HG;tE7fjfZ4YN+G9Hs*r{gaD zKIv5Nfk?Z7%+?I0Jw$^Waj1oT3g93j9p3Cx(k=eLgx`<4e{mvb=;mwniGV+;qC3M0 zg!h!$YmAj0VxnIdOTG5-nUJT;bHmUL79ZcIQry;04zk1{PmtLVIc+lEYv#-<-Q3$` zf*=$r7F*urB7$t3=@<V$3(8 zc2$CW(lYX_9tHlcv($rpsjwdE`je#+!6_(x`7^#KL6u9%Vo4xdsF}28wY!b?)`M}X zqmdGx$znSy7=~$yh+qay*7w`GN{H4C&=D0>?TuHv`s2DIA|Aykn&b>1qDHgrc#5wA zy2JQ_;J*J=i>VI87fkd0Zk%N!jMJY)o~dXZ8WVRPUisa!N;ZPumu&W1!K@Kk{kP+F z`t;fK&yL&F*^KvdYAbZSFt;aQ!1{)1Z&2=6a@h6H69m5VgxxVQcuUaIKPygnk4Ojk ze4!(nW>24L9>he(*|=EF!e)%x*K6pmV@p3zq5o@uq-W7$pJx2H8c02EkRW&OY86TVY+8%Kt4xCJQ z)m4)|iFOAXM0PD#)2$P5V+gzi*q!aqUoA4otbcYqfp)3tCCb?hJ{-VED?OXj8y8+R zLvL2TVcw3oWHoVw`;=1ilJ{)SVrMjQdy=49biOgp>;K0va9(b%C z|BfGMyi~{4f!`j?vK^=Os|xe=7of77LLM8ASy>lJOVckB4ix9d7mBkH&h%i1f#4pi4x{VYs2cOJ9{gxKQ@^C{+M0y zO)+3nkj{T;cZbD7O-o0MVFP6qo~%=nPvM&4dc;CXaLTtj*Ok%LH@6)LiMBY+PxIMq z{nS+@owAdx+|XKU07La2fze^jhU66DVWE=0ThAt0;zu|iMBJkA2DR7 z?oANV=+2FnHlp~H=a;rQscOL$!m2?tVZ!R8vW}JV;4PXy2eDjm8b~xg=7a#E9-fd_ z5)WmGIZCJMnZCU%yZz2IWypt(ir=$Y@$_j#4M7V7g~i3@LA7YXP{{a(Bu)<;PlyvB zVnneBx^&)}uujE7rRgbwf)!QBD3GBz4}~E>nU^e2MZtW&PuWWAY}u_k+NI`_Qp=fl;Y%sSSKOrd;~;Kv&UC5Fu>Wr>-F%c!{NeuqpV<2+RF zIRt6eu_{DmRKvhpbg6$-CL;&eex#x<8p9d}t0tcF#D+s?FsY1HZ*MKG@Lkw=ll;GXLfcqcM5$s zfCr%reB}6K?yE}L_q4075kz^^JpTOc@?CT-<(CbhBrcQ*ZqoR^Y*iU$ct8Lc^E(C) z3i8Yh9=$sNh8jlQ(BoWL4BX8j9l%~8&oGwCoSNkn#VL!o(IZvgj-43O10Q~3PTX7a z%-C4kq1>>@uas(yvtFuM%w?Wk63wb^?xrZ&mQ~-+$DYgj8T_9B;a)^P>L(`@E#OZ?IywbDbBj?3U5Y=yxw;0*&%Gx@}VS}yFNnUf$+Qhb2 z6d3qHJiCpTyl$6Dyk|&=V}-o~Gei-Hf!i<;3h(*M+c7zi8?Frz9)v7{79dDNZy_NB zY+pMv&ikFulqc+?D1j{BPeEY8A|g2Nghf^c!7(p{wO@Ov+XFikRKC*@l|gE+KsPfY z65w{zkmR|gLuP>=ikhQH-22W^1OY@LUw-eOY8-O4h?05kS-XNji6oE!0QP$;rCmLP z-g65T)Rp^P@Kd@R;`uFfp-$7Bi6o?wKmfgylhZt@lIWI-n3?*-M#%rUcg$hX!pIRM zjiFLZZ4E3;WX%{ftl`o|&;f)J)D4N7Qz(-ixxG>}4|dhXJI-H|{%3sckE1QjSe}SN z*?=Q|`6o=qy!t2jw#o$!Dp((62r}A0GV(b4&gbmcg(G;;AFox#(m0@JmmtLZ7!k+! zP?Fg!MkkqTEJKDClOmOgfNE8x*SQy~Q$rI?!S)@!qni3yt@?SATMV1WLU>vKV~`R# zx7ydev+7mC-;L?u&y*IXN?Fa$R&i-c&c>+G)5h|^F7qyQ>e^XiyST3+)ApmK!C3uz z!ePgGw{^{C_S{%9=8W(NRrJ}P$$2_shq$bT<)s zr5%s8h~G5tQ;pp9gR0IIqHH7P(S7zZ;ym)KUFoLJr4yQVM5mpXSrDgK@z4C%%<$6`5}I6h zI)+y2NaaqokhqFDv~>8t*-h%*q&4fIhWb2 zE{^xfC;w&#R{*7E_}WyIrYo2dk!!G;aN#|?O=J~~cv7WuzKFkBva?VPM|q^G*J==+ z#-P8Ly=Sa)b(U@hLHUeG`ILRY(n}UT0y1i)aa((tXEbC7S!hf<2?Mx=eZ;n>Gx?{ZCsKG@S@o=|r%?ZA-Hhj`dCrkAs{6Y$Eun+2NkU};@mXtznF zRj8}A4SpnF-N!$qSh>{Ss}w^!Cpo&bTYCGjj84PrBnZSs*}Ew7{gef9Rm#uF99LaR zQHEHouo_hhXjOu9Nj$f8@|UfD(!@8{Xu5IXQw?r-Jo-9HH9XSdH?yz0!M{ZP+2D^2yFRU^=b)2g^I0ORtTj6-0oN*Nibu`oUj#90ElytHM^i%z({M zlSS-AU?snZLC53R|gj8*9?V_w?*Zn+F!UbH}})NFwb#p)CGv8A1e# zTq+c&G(FrIu>%s^z_Mi&4{WkylPk{adbcACYuaE{Y!}*b>ybYbTqq-5WZG1Bo5`Yv zOzOdI&Ra+ELaBfz^ALxZzAy#Fk)%w|mia*Ux{%Jf3EJRD0Q>7x=27SCHDaA}9WLi)4j zH#B;Mczh#$;O84aptIvTV^f zEd(U=QRpu+yw@cp$?+XEF;oA!udpirv+791zAa;xw!`XYk^O3Ay7gd8KTGNrg+(<#y5W;=X zc+orPR~3VBKNpyFVM&;vSh{|K(bu^i3}^BZ3j^B=uZ0FIF7LpG1aj;$>toep4gE*w zxMhbAd(EY#+ec@gnAeMU?gRe`es&o8&iflmg<>fv8s2R%y7sNgQrQ`H`)>OdPbsc7 z&xVmfXZZgET0o`0P8t8oirPw|g^ChINk%z)8?+JEo~|KctC5q-HJ1&$U+GEeksQ?!zmp?nPi0jMB5-Y|kGSb)Tz=8}Sr zok&CQ#Y6L%xn)XdLlmLPs0^W`GNZtcle5TxvK?xE#+33@R|f{`?Da1$7yET01S&~6 zNg^@v!E5o_Re*y|>yQ4bwE?W}4LAV%BD&t?x3>7hETOURc+EXDCKs#l=3ZXS$i&aP z!sEsH+U36pwciA4Usc`P#ve!k8a@bW`0ec^_cay$9 zyqZJ+AMbcA&QHuo0N-Y6gUv8f5C9;XgV44_KM+6Adf56wxj*al_Sl(P)7q;T9gp#o z?rE2PTD(UIg@ZGdK*Mgf=`?Ekq+~esj+(M&%?6Jlsy)tw;X?`Tv?Y z5AqMUg_>$6Av@`}T~ZqqA^LJOnPibojsnf3aahUp5Wmu))+CLY<-18^$+VS#*$b zCbG_CHkit0z3Q*S?l(O+{jZfyf@XxyL3sb@TZQAU`0en&nn};tRVkyU zqm4essF6#&{({%%t_#%qyE4=;+g$ExDPI`QS0iXNk|v{QHoDJ)q-kVDsGm$dr*|fO zjh&~tuaz9Hb-1Nyk6-&aS3;%?$oc~&yZVbFn$!NWh|paHLeW*Uu9P30f;==Yb4Bvd zZE~VE@~G-`tHBv#Xf>7;AF*5e>yPs4b>EJJO%;iD<40D~O(|Th5T3SjvJg2%sGKTH zmO9j)%6!w6$y8H2a-!i#bHckmpKi3L4xL+DgFB@G=lcYElS<`zWq*wejZ*AhCF@wX zh+>Kz-Y56u9cfqVHFxKno7=?48{UX!pTOQ|ce{4jFUu>Z@=A8J%F!(+dNGK8Y=^FF zQ3+9Q!*I*;l}bZ9%;l6>=u?aGgB+aRXDn6M!kQxwQb$*H*L%r6-%7CVAeUg%=J#c{X0;!YTZL8 z8o3jBw=F8AOjI}oBt&>;q;4?t-~Z=bZi|Pfm&4n~7ZM5@rdN7daAmIv1p$=RP&5ag z`>p>_>DlEie+9Q;Hv2E{-KkyIa}S#=8iBJTDBc{=8x^^G{?bSN$-*&I(JD&R^ zZ~Td3)|_P4sT^zZ2pc214A~3TM2Eg7o-$y|&KrT7Ox9AkE?dR;5hujI(B;Wq90gv&>OfxL|NaB`}h+~xWW~M-S);Bd)m+b z4sf7@9O^KK-`%uzbm8}&zmTYX`hOyYHU}~1n0q{Hc=oh-0+FGS7*Z&W;c44y+A*K= zR}8z}sy3aY@zOtk z+PTG!)>A~iMb=kT{bOaibB}p3i{|#+d+yAA4#`t}US>DY?(_{#!kp^f`Ki)bRX#7e zuc4tZ7Y_!>!*#nJI2;gPPjtkLE57(fN@%pi#-`@H8f_&HgHlHF5;DA+9BECJ)b#RX z5f^Qd7JZR>xS5=)&Di#doTEdSPLH;uq$lFjKI04F5K+;XVPNB&Rra(Q1PU{TBakSx zM+p{(ClEP&ITH7io_DN&SC#VJ#g za&uxZHv_t7+BezfyS!<9-ZCMh49VLjW|HX{ZwmCDfx+it^eiU6jj0#d>qX3bhl5_{ zkT*E&O-}hMGT!Efzj4z$7zmfsMaUT<^1f`0jObL`LhKl*G*9xFYK~yV*yZ{fB zrqa9^m8D8~Oe#{XGBv7FYknSy%6#)N5sj~Q_=&+^ECJdRr~^Sd608#;Iuoi3VY(8o z8*?VoV-mdr=@Uf1$qWc)PzZSOuqD886t2X}$1Qw6ewqYypIf6iN~1g~qdID%{?yN@ zVK72)7`3QFJsR*8jc9V{giZKFOyopO^u(-VJ*1GGshQfToBC;(uhTe9>;F1kXX|`j ztjl$^h$I>}m1A>LH7+;R6MIv$_;2c_@TPv6Z@!X79O99HL?j^@DRv#VaUYNI9Ix>n zA8lwy2RhM(ZuEf34;tkPrAn z-L_>N-_s|2Vgtq|3rx*c%X;|PJ<*fcPiJh5Kr&h97vv~V61q-N_B_|E6R%SNBwo4d z#L3LY>yQ0H{fxzwURA%YcktDYS3F=;;(u&{{H}v3YhGZm#~lQm88i?$8*n(wOlBLL0?vdZP&ge(I1N~A z;V3v9&Wbgh9nZzkV!m4+)lJJBWAf*~PYl!Sux+OW7MgB`S!N3}N4N;{M2a%sl+t>s zKJS=Zr+w{=pZ&4aV!Ii9f${Hyz{Uofz-RVpBQI-21&&suLh|9On?bNhPeEJ2AkS_OWmp5CacDG{k z&v|6ao^NWvE^FLkO^q)LiRvVl16g{W=hK&`iLp$WJU!G~vSIno4YoLCsukj7J=>Fs z`X^r3_o?&y#2?J*V4{;T*n_Ai#!IO1r#+Iq>y&)wDeOD#eCgy%JW}|0*m2+f?e+X) z+>$P+{CDz`0cyItZJ>4~AFEU61@-Eo8l;)z-vemu?v{airNE_P3<(k}Plym(LWQ~~ zOqf~W!qtipVNRq-hee6O5G$UH1j*#3NO4K3R9B@*vmjl%Wf?Lo%9JS!4o(@elZAQU zWd#8>vI&An5hQ&+zGhHYVsvx}3=AYpOtM&5IM~=sad2ed;>yLtlaG(@ zAOV49LP95rh+HQoHcvugS&keJ<;wMWq}Hl{Jb8|iktrf4_kn`K+fjO~#{dAT$LtRP zApe-lT%azL6)3=`p<%61p=d>lq|nmJQ><7G9i3r%dJ{^NxTsXA7s`}*tz5aEV{k0E z7gQ7)Dple$GO|&niXRh`w10{-YSfritJWoT>g1lV?sAtU4yn6!7dddxBE`M#73Dtn zS*Q_`{{~=WcfJFo77(M22J^btC3(Xepx*SR)Ot(u5CFWLd?SE&w!0Vade^!4yyu(u zy>H8hK6FqYNxl`p$K9O`e6nKBr_X06Onn~7*8%tzT&5Ha&%Z0L<8On`S<<2OV?Fp=qwUG-_UO1M}CWWx=z!lv!elg<2X(cL6L*Y7Ag` zA}xRwNiG4b?1T@jTCiDdH41C2fwtCKoYq-K+;P1^_ z^Uw2Nx$?jNo!er|Pqx}>wziecyRiLgeQ7|oN$LZjJ?S?99f`sKI+G3q0umAc1d_-A z2y{XU0D-w`BpvA?@bw}kMhFPUKsItir79pGn8b-wB|!oU6qFhm7&b|g)Jc&d0taU> zh*+CXAR%cF%7OyW&>Uc3XkcME!oewohv$TVpa>C>GZGRlGBOtw6r-r9($Ub2p`**d zz%Y)9DH99J1ZmQ;$dIvzELj!g$T^6OZ9jSPKA=EB14W8Hq(n(8Wy(In!O@0`YYPug z2Nf#rQ>E(2ub)TFYx5Iz>P|%iSZNMp(E?fgVd`l6+8!Al$V+!KzX^bu9!lv?%*Oy~ z84kCOk=N!o#*E#F39K?5F2xL9nGYXhfl65ppJIh-Sr4CMgLc^t?_!6|vfnHO05Ef4 zTaJTZoUkkBK`1Vym+PPuHwwyqFp39-9M=5*{vWHq)P5H7UyGXm2`v6j*i#U^(_BvuzGE5X$oLd8^Qpaw*q2|+iUBV zGXdNDTH9M52kf|}Z#O%i_+i)2?%yh%J>HoPd+lX#_PJ_#v;RTU0S9bARn@*-PO7vg<+Z4T!U^g(ragX%QNb zgT}9b9Dc*eA!s-kK#neI-17H{R~B{H}!s(l2Z6r ziG>tJ2*Bcm*TIsNclmwFo(lbL^$u;fx1wuBwyacxuAdGzF1np2?Az36`1vbzjEHN- ziRb{E^au@`u83jAjBuIj#131mO@t*&ajaMoWzE`sHf&rtwwww(Z=3y;%m6!B&GB;D zotCqtWxy_fb>M8jF}vM9Kc8}!SP|GGB{aaEDNh4?rLus%Q|$%&By9oMH*qqsU#b9L z|4Ad`AT+`9%n{rXAQfS&B(j2XQ!7bC`Nu}|EKs<+;1^3FT$ z;>BC2#7MdZFu6xegDI;pOO@)nG--IGOUErk2CB-G{1U+TDUt=4)y-%ydxggNNPT?L z-EzKqgD<||*tyogb@I@ReA;*6JERmTB38vFwE|ee<1T%}rc61xU11IEDo@n~syep6 zni@59s#fy!0PD6-|Ec#zSAz!H^;M6w1{)WgyU7~3HD4_+*HNuf+zzlUxk`ZTJMMOe z6@5B&>eZ!Nhn}nN6`kqV@3aOa-wbeY`*|I8;R z+bWkSQsnYoZp9TL)w<=n^%ZTY z+e(uCf!_6vzUFTC8eR8EZX(e8x187`9`U6|J?fWwOhPTt$G1~YNVo^+lL;RKeJWuQ z&}X__G4#1LtUb@OwY`w+exNUYQNv4Ka)g(kSNOBNns7JJ*OIgV?LXlopaX1f0}~bk z9h9UL=-`AVphLR!8ai}wz!}D0_wdi3wKIah?~!lrJ)`;Oe*Mkc^@d5-0DaTH_gkMI z>Cdrup8u-7o3I$@dr8&-rAB#2VYFfmhALIESEWj@YSlidQA1v>2Kv5gWUEQDH`OAU zleTUyR&Ag0_W0^3o9mrUuIqZbw`4s}|Bzw8fbwIkPDGr1PL=ihv=v>>IAiWPi$*(_ z#=Ics)7Ti~!tTt)=Tcee@^cjt*IcvCb=R$S!woCkeeN>HJ@+j4(I;!_^DEg-$d}|D zLB4h>N4~AHZhyDpXZ@0FDxU!BuT+m<{nO|B*ZTMR|A(93U|eJJZ7>I~-OKy*>6yIH z)^Gj9@jCPJVyEY{UC^_Yhp~Im&Ze6$V9tH(P0l;-+jD_C?c(HZVJ^AQE+-!Xb7i{u z1m>#C?b_s9VXnIpH(YBsQ@sImYpS;=C1YS5{h~Oi7#M_eaFV`+X`WX^3zTG!Izld{_F|y=LH2F#YpkOj?nGHn? zq2y;%o+=nrEH~9u>7cf=;i$9HG^Wl3O{=%ouQ&S-Z8k#37G1aP(sRe3xNF}%4;*`V zJxZ|+k5j^e{-jTM;;cR0y+H<-;VYG8zcE{s*J z2;<6?6*q3esq?tQ+B~KT1Wz}^3$b~3tp`4f2)=yTl%G`55WLOo5G%5T3Uw^(ih505 z1d9~qMn#*Pi;<3jZ?W5KI&r95d~$*!oiMrPkxsNVlJqrTH;;z~tq3^Dm}LUdKy&Y`CYK~yu18cR{jEb2(2-apYm z8jbd~rmZe~nmH9MmhaYgxI;sWfq~@#2PgKlVG!+d(A3@k#_0?&*9JGvdxpR_?1nSU zQX5IpE5Jrm^bW9ZlL!JfX0eSY{swF!VHW`Vo_HNFT*9sZHkrr)*i^y+05+Y-1K3Q$ z69D#OqCH@OT@tN1rt-!VewzEtu#(@&PSTt4RC1Q z+AiSZ0}&ATUP9eHI<#ovQ)1mD4=q{vl9B>%nKgVRCkH8o?H;+6R#2#@j8jvinlOQ3 z(j;0<>E_$e(~A@{W;`-$mcKc3zMD5M^(?S07WKEKzYh84f5UGp$#_RvlT2C^F;AKn z$h?#yCQ*g$H@ZI8cj2a+Hn`=sP42sItA`%i>5)hDJn@79d-e=H_rf0k`=6>((qjbLE5Xwqcgpsi^D6K&}wXxA=QhaMokdYS8kmx@5hf`|x=nAmj^({e1) zM9F^(@K8R0tk$D;M^^jL^tF!qSKG03h5$}TLw!D{B1WmYH^T|Vpr8!Fz(~Ns89_kE zfrMlX1tk|6nh860q%bf{VPTc9XU~HJ2c;Z2^5n!x8E4MCxNuRncZCbr zC_;o=B1Nj3D8~$aJ)(;lF=AlEigjL`I9T!G%}S7fCQ+i>k|d!^mTWL!B-hpNdB{GW zN!MWj;DMki2*QV9(+EO-I)&$3C z<9Xi&LANNvNs=B}Hl--~Rn@en8PMtc*6U3e42~&4k|bh4Kr=u8{cvLx!)DshG2o%{tC#vK4bKvamu00J1qnZ2`c-52Ya>Y@b|7 zMcd}}rZIFOAr(VGSu0<@5(NsZQ>aj>B1P6KR;)~k5^IzyRX%0b(NL~j#aE$Tv{b5O zzbX~ls#QyVzaQxuO^H`ekMq*O~=?d;Us6L;!J{2XPp)4oO4pO ze$KxRWHuKR+cN-oUQCG7_Po>);IhknX*4d{m-g4GOOWp9S#({mKHv1~_k9Lj?3HqLeDie{6b%5BSUm20mK41XfW&g~9HYca zdRWtCd4Ygl>%~9?df9#+JJ+fn42(Kh_8P`P+J4$zj*UoPIkod?fJ?hF%8iH9Jb4=D z#oIeRd^GXp>)7~}W4nI6{Ht6PAixEI0==3@Ykixjjvo5Obll4s@tw$|q)z$`FC=%$ zc2lZUo6@97m)_~*$dF+}W@pA5I5;n5$+89yZ&i*Vyoyx|0AAT?9lO1V9fgOaqZk>* zUQ|?>XlPQ0?pgyF4|6*{tVr9p+l$@hH4d(K!*d}__>XY=Fybgy699NA(t2Tg8M??% zMuiF}RjMd4sm4*Gj*5DXv^8m_p+yV4R;}E%Y2&F~2XCD^`RURvK#xAb`VERUWLUUS zqx6g!V`SX8T_#L$nMudi{CZ7Qn40b##loyvHg=k0KM6Z0p5_7bW$i|;Tt)Kasgy5Y zMu7r3g$flYQY2QaSh*4<(n^&ooifkoa4_Y^;7&!6QmIO@8Z{ENYIRkoUKb4-6l&5W z(yUpiMGL1*n=7og-#b>2qKXvY)I40XYE%W(hdB zk0Bs@gcPZzL$9<7uuLV3!J>=*0(<>F&%lAh(>Q}``9E&l{u_Sh+Y<`mm1Rdv$I6j% zNY5?Bt-#*Pe7N^vhk0Jtcs=r134KpwdCJ5m&zLJwBmFiHMH2I*r>dhShq5|#l+>%IszC!qEn409CRD7$ z!Pzm@Q{~97{*8uJYt(GmAS}g%fsdu6h_G9(up)w*s`Pz<-Ob0**{XwNwkgaA?CwjE zd-qTSwP+DyPD%#$?hRJA0q}?=OLy6_b4OaFXRud>3^_7o#=gHgn1YK02^DZ~Jj|GJ z@ZqC|p8##bgz*InrYcGlb#dZo7&Ass>1SLCbOEU6xiKBf$6*Wi_4|;`Y?lneRzptY zmVg96L+N#GrB*G(sndZuUv;1~YJ}7BYSH>?)us(xyLM<@x*&DyhSm4#)Box>U;y2q zLChI;y*MLA5NBKo2mmy!U)RhZf5P&k5$Hh%9D+PWilkYwBFl~)IXQ9y|IwXZBMA*n ziU9)*wrsHlixxhA6hzc-fuldyRsQ;G%Rm1R{P*96uG^he#lH$C+l&7cPM0!+_E~L& zY_r`p2OOX?CzR9@IKNrc6gaID9hLHYXLTwtl%O-4yfuS7}(@88W1K8L`aO7DUnN@A%9dT5oV~&Y) z+;Is`I4Q{~r=&RTj5KGRm&r#D8TG~w3z98b6zZp+!u|4FBs{zrOP0l1u_oTSb?uJg z_{2khD-eniq{xh-Ky3^ydN^pzrg7o+120~4gb0}@9I_cWlOr#f0wrOT8OUYGM4M;K zb;~d|if0Y5qW8LTaPfQjbNE!Ze2shH1n>@Y=Xuf%a zMHb4@QM{9rE@x-`0AdhD4PdxYf;P@D##q)Q7a{!;yWge_JpF?u*|JUf2xre^Pcmpq z=J>S|p7~u)a9Q|gDIxZ`r545a8NlFw-t}JO)ABFOKKHZ2pLH;Iz_Z@7W0-Jm>eH`t zODBH*`nlg1nDdN-4u1POkmd))>?}>mSu_Ik%K!7%fAid5bMsq&V*CHeT=lmc`6uA; zZ@x%=8TcoKG*@z2cXh#ObkG0)`{L!Kwwd42Y?2N z=7jP5lr~m1uG2X;K41HW)+CqeSi9QdumqD{&Zagppmz?^L#wbvfvzyfvMS+)@HdnP zkcS!+v@7rEJkykAW#NHii&#wo;y8^2U1<-;L?~s&C)ti4dgq|Svu@nHbrV4ssaZ`j z@CiE|p~!wK)vUZ#l198q>ud1EgqrU5EM%^y0)kGk2`&#xyH{@65w{JoQZI5=vS@Ar zoBC$VvyWxcW(MT~|29`OD(fYQOc(Zo8v;a5ZgQhj)RuNrb=mOAcx~d`#XM}DTiThI zTy7X&Ld1yhWbBQ>GteqPpeUpDe{j{SAotyA7^7K}=~ocvvPfL#?719R)A!T|e6yt6 zclW~NCmBt`fQ!P(ppxarMgwPyTuWsaHFy(ubqzB})?wtr4>hk^cRZE$+IjrDmoAi) z&Tm)ZjKc>d+t@0a?zuh7D$dtSh=Bo;d7EmCS3pomdJR4hR=#*CM5yTquNO5VDx(XG zuO!bU$nte!MveJXy}&*ZyT`|CVpI3z{Dp&Ru(vN{Q-P#Ss`!{W&g0kR$@Tc$oPzhj z%rPAfo7ToLrgudMxu-RM!Udby*x%KV7N*|-D0EmE29G!(?q8?gHAbWe57I@SKl?Yk z_3P~bl-M=m%IDbz-Z*I8XoKFgn3|KUDx%65)Z6Q;aw>D0T5YakeW-LhWXkkyFmvI9 z5-q#_$h`ATg>R;v|KL&YN{Uf;>$J@$fAjX4UJm$zD{XH;_YpF^)rvO~%2P>ndye z++%Af&YeR65EuYJEm_L(e!3G{)GV_}rO81`PN^e@21iV|_H5<$L8s~zZvpSRp z0K|h(P``C1V^q{Oshq-cxqCG&Gb^=_4@Lz)O5Z~ z<6^G8^ReBtQvL(iy|Nzca%$Qbn)Qiflq2l$h4Z%Etc&g* z+v6MOqQe^Zg(lK&uImd^d_K8wka9V6QUxD_ifIzaeMy}y0GT2b)HNyuLIA8SQ!hd@ z6sS7rZ#2cW*Xjgor#ed@Ys2U%94;eqc_F!3n(DV`W;4@jK$xH>%$L=ZZCcBWSI?@7 z5anbCxJ>3c5M_zsvmB9`s1|n9?*dA!Jy98LW!NF?3Xv+%_ice4t@x`re<@lsV^G6= z1%`It(32lcg+$(X>+icsMHwkvPv#sK(P7;c_2@8NPn;xUx!{ql&qx4+Mk< zk${77J_szd4^4@rmu_T8w?L(FOer;P%1aB}=>DuUt|%ci_`o2-*n!pQiy#2XbZ}s< zP~BtwSE>r5Xy&8_)j6;xOv9O1Lx$)+0<1EpL5KmznC;yrY!(|vi7;T!w()ApwSUoR z(=hjr%^R&g9+e)&SMt%T*<7&*q`e5vL5xtf$Sp)QtK~_9{yf!Qltr4*D^D@%Q8^hj z1gUR-wi)cg&Sa6Kd>dKse!o?G7^6&3Vo@pVfX+Cp_zFFFg5cb#v^mSy!eMc%s#H3# ztcJ6zY}k3fzAI!7BOwS(Aioh=Lns-rV8}R<2FB)C_MUvr>_&LY)FhR$n4-fVDvf0Z z1;4#ufBPg3#jYf5QqKJtmpQ{}N@B<(bp>L+s=y+unLwOmbt7)sJAsu#+FJ$7f}(Pc znH=)U>85bH+FX{Xp;>RG)238K_rSABF4bpPu4!`I=g!!0#+R|ttD+{qFH z_=AGUm%vqC90{%alC=>nZ}j>ULpwBm(@fv;{W{=M9Pe?I#C|lSmmRc$YAbeFtVzY* zYPBFkk2erXt)eJ=L*#(kWPs<{dO1u|3f^jDd{aD`cP4>n!Bf1tUZ!8@K)J?)d&h3} z&T_SO6U+(}E66>{vszaJ2qhMfIzH4aqaL9*aE+e!ypciEed#96>eba!#>GRMp8DsXtonzZihco#Xs zb1Ooc03(!g8$E1NkUTsyV#!>AAl7gmu+f zOO|JtCmKmT*zGJ)u!^$V*HI+KgdGdx1av0x#inIg1r7#fA(H`p5t5J$J!;DRKKH5> zvOa)Z6wHyRs}`A+qAzVnn^eIwBXU(85E&EhNfRPDT;(Dnm@|m%9_gM=-r$5Llwd2N zdC7`e%~qut_5t*bzTPFV>v{SAU})Zs2=mcBB{0qj10A(-3$yOY9VtG3wBap}DN6|~ ztFDQ{B)vA(g}?C?1{qM^6fIWbBx@chta;ubpfh80t%2%aeNxoI*cc?Rz3|#HsoO6GDo-6eji@B-zs)kI^fhR z+C0%7V;?>`|Kw?#v3kGR9TA>vg|(OBc>|nQ8pagU+YW-GgU9&T$Y723Mj#N+#t%D4 zKcAVTr4>q8b#+;OqbAJ3eqNI3plzS7$1)ysjAkuu(g1p3C^3=v8YJgbpb9%2)b0lb zd1=`(GkH9?DQhqpI`_iONRoA?DJj4{o}nrPvp1yTmI z<(`*OzQ9h*`XPWuFtb{{pBdQ&>U;(QP*5)Qw~xaf*X~T{npVE0S*3xa0QSxaQ1Lv?Sol^nVX>N9?pI;cwdaKoqiREHkbx(moa(1dC0BfuH6gQU<(s!m) z+eX-(s$fS#{R9|(wjVmqAkYk`IujVroQ!gF_zJS}n~`mIr+!lXFLA!Qij1#VFl|98 zPybR?`&<-B%~C|>LfK+LT}nh?_X5I&4Vs9P#n6wKri0Xa5mg>cTe_NWG$Njx8Wglh z#=3n>fl#aT0mrLnod3QC{f3gOi)C}EIb8j>#r-fq7aN{}EeC@BXg=ADO~xwZTH*#IaW4>S1(X zo&PGAjksR`Bl)(%V!jSib-oUV2TJAJpv4?5`?v{vT1VYrh8A$Z9Z~B`AnFr5VS^}w zjhU4=C(zr0p}R&v&#i1_8jo%<;}#73eL+kYIS;`#jMuA>g7EVckQUu)wtpq|SruhIM?f?ex~h zXw64?(I<_qY)G=%wE1Lg?@Fny0-aRcCt3X-EetM~$y6I~@{KXucKroeow)A6&Sj1s zZhzy-96&Zk>h86#dZWjQlcp0E~5Bn}av3o@Ex;U0VIgPPoN6 zjl7g6FQO-Ci1Nf|)gsGoC zgkIaAW^D`=$e><S}=uOwP!vY`@28I{(350z!!bhal=pSANokz|6#3Qy zk1?qA%qu%>5lj@MGXxATSa-7DhYqu{10+AG&9`}EE_(R|{;|F5mqQ>${QGrM{;#r+ z>bx-hZ)qMDJIa>&$-m>X&GeFmj&ZYtA-g0JN`^ImcgdbWw@yhD;2W9OeE-)C6040N zP>*S{9a`f6lW|C-Jfm5cvadAK>@lQp?xk^!yKvBA47gY z#MwAl z7PLL8aS4B$(q%&<*hTewqqwDDlt5XZWJb~8UzH~quEQVY?M1p_#h^3_J*7yq3CmZ= zwCq5Rl%mOIz3Tu2JVg)!f?$6;N1lxl+~QN9!?EaNHQhIQ&4){>3%S!5R$FiX>T1qD zsuS%`ZI{{vF*RXzkHQx$TF<+JqO8(bHWs@#;3k&4%%p9Qyi{2Yo4Z}JSQAQxxOMli zfDA{E;_4v``zotdP5%|6p_y9bSRi<0WtSxJ7FaRvjy z%iyTQ4hGGcl1<=*z9~73VRdEGHpuD=v~$?#n%xbTa&~spOUh^yNym$ksX3s@k@nrl z`c>&e_IYSge!(;s)@@Tc9JXGtCc2$El+jus?G&a`;l6ag)ZF7|&1%jD zHsC=p^`R4oMOg~t6Xs{t!6A?za{%_PQ=LS}e|~&p)NVGY$4KJW>wL~lX!?HE1BiAH zZFRXFQtlV^CL|C8j)_w5{6cb6=UdNdUqd*|CNa$q>D_7$||O0$J+jw_avJY!dY^XFmw7l$ME(!3UE$Q6)&=m zP-GSo7IM7)r+)^FM6OSQqLYvltSQoz0I7ke9IXC=^}(Xh5@8_O>GP-uO~Rw(A`+b_ zw#7-54R$n9(sqQMI|D8Y-b@*3@!o`Nko}Y>W#60i>443jZPw2aBUkgZ{fSV%h>bGT|pP(RdHI{!33C~ z92T5k$eEgvDNQC2Izh$G#IA3(4BQY8vB`CLG7-0zHZ#e{9amTX+On}IA1Ov3-QwjX zcm5Q%c^|AKmvxj@#)34V++Q(qAcQST2fRqgKtCKQnQ&WB5oUr{2-lm(5HEpv$?C1h za7P(CHN>I`Imv`XVMSoI3e%;}WzGVqqVFtPIoaR>iJ;(RB*e^gI;J40hS_}^m9+y)tvJcF*R5~Y$-3mz2d9cf>Q2!_Ujt4WZ3 z31|5bUUv4hehur2?k&57OdyTbNP*?{->}a3aHSwOF^KhpNxk8@MKAzZ1uY;qpQFXA z6-i?LK=LS5eOhl2^o2ua5m6&Le{;ZulBxS*MLCi56D3_reP6VrFHgBUkOhd0Oq*1F zHvyshjg`;^Y&!M+jaTACr0U&K)UFX)_8Co1;e@m61h*Dl$PI0ps@-0SipT|1q;;y(&S;r82zEtCklRY1By^tF9U}ixd$|QkQ{t)1OeMH8pU2; zu;eilG*q})BaO%_$15DuQKfp^reFEX!HULS*(y{!tSx(dcp>tVSLQC;(2O)B&hP6T zVm<|mxRpFH#xEtn-ohieiVv9v?{V+-Z^qUa?JV40yshzz6XqP7-dgWCmX}_Haf`ZG z0H(7Yd%9vSK?v11DB!JGrA#!@F)fa~0&SOmyG*@x!73NQrbt z*1P#>9o|(}<%HIJ+PPrseiu{pQFLDY>GvkdPF%DTyp>kUB~9%6vI3j%ZY%uTe^e8j zHGOW-3o=q#6Byhu10o5Sr5UG6#Hg>x@+HFSGwfwS_V{R>2Kl1vD;$Idx7ruLNwLpz z4MoV(uJMYH@yl2=8IE(D(@VQ3z$6d9wDJwayaL4RZP53SXLKW07ynq^F|dtWFJ~Vz zRpiAam5G1Vh1U5wM}+opy0;=VEM4#h^LV)X@_7oY_y9wwkKXml(%WWL=u-* z*tu_Q<6f@$hW!RW=jyC1RrP$dg7QOQDg$IkYRyFeF{EcJO{bYoU) zZs$&{lb*S|_wSX|Io35O{mj0sJjJP1isT$+ zeo1v5$*QO`CAF1~im78^fk(A@K}m0hY6D?(%kap~2*?x_Ew)f6@ew2m{nk zNV%9xwCm(+)wEY5t4{6i1L#$$d&lfUp5@PPz5P+=HlnrZPZV?^MnNb+lUm-9RA?s# z{8x>d%=AO7+#mIkHbv^M(d?jI0$y8)zXq9p!%5=Dx`6$_>;B40>LWAJnSAGvyYOT* zLkiXbD8j0+tVlmcKSv$JQH#=51j0|4FCXp2pE!hpw_386Z}BQ|vG~x8o&E`}aC0Nr z-Ghw2-NVK6kE*iO-{xVNVA2ikmnfOo{wbpXeZeHvg?#|#E$|?ZJNF&wE5k~+23tZ( z#om>GD}v=P#MKUxRl{ByYIUfDTv?qK93OfIp{YkgqA#(8&%+on<$%U<Cw2Dz$Ockn|rQ*|3wtW@}!3R%YsMZH6dM zHz7-X>9$wNgutD5Tn3h6?p@wdqYpRw3;ek|J`uc^WRbUcblmr|ero~-`lMq8L`>HO+f&nxC3cTIziS@|K$ISl`by~G$6;ijf18P$!t29Yhb6U*=H9Obw>wW z!h=0Z|LTe4p9SbP)WU;m=pka$Vr_Pz>VO5!kwO7x41r@&=x2?d(!1uR;gtxdyAm&yoKINP zl=^D}Iq<=DaM8OK7h8b^cQ>UHmg^mSvr9IG!A95F+~^ckSs08uujj^ zd8owIFbInSbFM{^)$bQ9rWO(qccaDjxEq_S<=I56$JV~I^{;5a@AtI7#Iy4x^`f?$ zCk6mZK(xRAdpt3`18S}F!aMM1)c}DpxMS149ADXy#r3QoU!gM}>-0o@EtJKlxdg3D zf0orKtibRI=myScEeI3nx?kmLG~#3ZXPD+h(MDPSNn5gdjk_zgl-8#)D;9jV8qy$pe3 zW4E=ne|=24p-Blw!?!35CEMP}NyDDQj+hGePQ>;Xxwu~*EprVBiS`70{*sNzuYwDT zU#FUPpye*7_PjF1?$Q%)ncL603>P2vicm43E7_r1 zj!91(%{L`DedB2>MrvwNy?rHKf6QLnfhZMxNs#7CE>TUt>8Fc#m#9 zRGU2mF7bneTB53spuw^sHvWUe;wI>!3;Xz19kbiHqIgEMWuuOAj@<4@kpb;vJ)w8X zergAalD5rcRrhDamfl^NFu3$@O%Pn*_-a;|nmxJo8ss-rB(u=Ya>bj3t=ks%q4xna zBgF3tH^8^79~)K(AP!iqn}zmV)vC0f6Nk>xHDst-nl%|>X})FnK$vmi>9>S?5q9I_ zfa@<+>lrZ({=lzn;jM&cUUEQ<3G;Wo$b`m&Q@Uv#`)$a1K?U?*X!PQ_0X&%O45Imp zU+!gtc%H3}J3bYlVgSd*)Xze922$V~bif`l4B9|r?ftV$ZScHl)y6mPfd+Y!phi$; zvajjG3OhNRU$nC781(tZA;IrcWJujI))H6F84;EJ-MXT`Pr zM$71Lr;hNa)Nn^0iFd)eyw3SKkR*p;;zp3AG68t&`tHuL`c_FtQJ^A*cnTNbRn%v3cQ9oCLnA?| zZXlc*tuTdna;2A!D~-N+VQ&@@Ny4IZ|2E3?0D>jQn|`E%TJbw?+hwOn{{;3-W7a7r zol}$NAAiC11Y!k|@@uk6yXs|OqB{8hQIIGbr+fr|&n`Ho>b)XbA{eOUBer)L*+ z)*Q8#Lodj&I%JLU*!#tqB@g1^>@n_Gptjdt(el8eJdoz4T{2xnWcGh zd!)eE=JCmW_?IGFDOoph6_wsTYOi|tYObm`5AnC?b*Yr@VT*h39>Zev_9ctjA+H}m z9KLyRc_53=mISwdHBx9>OYJrvMvH>2GT&9lvu?Tke#$F(nVIAc@A-fUxT!D@&9}#* zEq#`uthQY$!|iOCF4Q*vmK!{2|B4b)UU1G829P|jIqQMmA!jfHODK#Y zY||ZRBy29vkS;*vryw{C2^dNJ1sb$JCw#8B;hUaP6s9m>YO#$&#NSY>b7o!PQ4OZ~ zYCfAEW^n;UPvtj5oJEQK!+u+_h|q;`I3 zp;*Okg;KRtxD`5SjHE`2GZ^X^@& zN$k+OIPF{vpVk|6l8!sGz;&CI_eyqVdYSYbr}y$IzOD zuW8fMfM&cnm-c=mo--V#PBj7&Fa~i%74=2jk$|uJ2@pO?lFKl#pjNR8Hf3}b zO7!Wd$GGHrvx)q}htz9&JL2cvgDtC6^O7&Jxf5nlH0zztk(EX6@zIY+kgiwxp*_@X z$8lM8ky|4dD%l290knk*rad;QLF=+f@!QE@q8PzpRlwY?wAe)Kcen5(!b`Fo+Fj4m zXsk!w5%x5l(W5$M1k(J&;xvRYc=?fj#eyFrU!vhl{n569ygG?!e4y!+G`|Qi9P}~N zIKcyIl=aiu{=}0eBDC63`Gl?lZtjFr{0LuwfM{B6tR7!an;3{8e*1TD7#RnYRJGk_ z^!U~c?qYYhwGASAHcD*`b}&747onw^hSM~SMfhb74bJiXs%*S!+6QC+8LEgKIst={z!uRA0*XPZu%u3 zBZVmzS41CtHiHX~X6+d!@sy*&zz`Y}DpXolXJlz|EoTg?^R^Hnrn7)VL)vr-E1pX} zNNx?N;v?PDH}$;|sX3^MVTpj70xiztJS^*##Hue-9o3AKLt)tB9O zSbionjNJToFA)gPgYE}Uy53(mNoidwT#=6VCi~AXkB=#SGL8(;1T}}eA$gOBx^ouJ21D|Ce>P503pw}PU>;9 z9N4SUY%YNXKo@_!pf*}1w_bVBNLqxr=v}*Sm&`UTo4V50kkrCT$?F=`K*;~ngehdr z?h17XBqI~I0*>e&o_>foIB-SL)*%}Fy|Gyp@N1eZSu=;?M%W!&u;=?nZSeGvm^*&v zFsF5mKSNbsVK%!#V)?iyn^UK0qKgYkX)#qVpF(m~sMLowKlaI)V@x!=eiL-@^e!xY zV500zd6H9!@h7zZb}B`kh%4sk8^93DjPkH-@u1&Rz`buO*Pp7ou&dY~m`$FiY!)fy zypiWJtH6yz6^A9NQq)S42ii-jVQ5{(U!$5gI5}f@VPfBdEq$E$IP$Zr2p@eh%!D0@7M^3x>}4#pCZ@1fzByT+2~+O*Mh;l@NVqRgyAJ+(ymhI{LX zEbO&e=VD*aCu-8xws|z(d|+}F&r>kl6EvsmliS)K;Mjl_9B=R^jyQlX#WU^M`Z3T1 z^~F_fhpV=YlS;?q?hy27!|AbJ!Q+mzM_Hn%@bwThb7kWWhmD;nf0E1GiQ?vRGolkXsGE`B?y z4AK4YxIwk&M64?R#&1?E>2c)4w4^?==oEU+IzZ&$DM^;Ct4@RaNA^n4kuxPU#@)BmEsHRKoiIu z)v)V-RLUTKz{l2L6wIYt2(?vR#kK=!Ijhp)@}{B)ruEvDT#h|5UQcBGP?9g5?%SEGtj)ax7H&=Kf#sJVNXO(Y#cEqD3g#P2_2y#WQ#Ddg zk5P2$xi0!K#T@ffE^%@v6%-oF&%?uDf9~U%tEPtaMiTpUAy7CFcT+tnSENZXuR&3! zH)tQTzJ))zDco0=Iw*>lZ!L1j-=awe(OccM+Gvdi^O(2qWgbk#bGzg=2_@kS5%KO7> z@^(cg{`)DR+O`^#Gq#iNfUkdE^e2b-?-f{x3bmqu2;&4{uyNI;V?Z8D;e{MtO@~0- zw8FnS79-0G*FNxLHa?r1uF=F|<1@F_3~KN5rE;Tr_n22qMz=dj6!$lKcLL#C6g-VP zoZOn&(17f64eu#={|U+B@ZAwm8~X-Bp(3ugLDX8Prw-E6tA8C4PqoCHdRc;-{ZH;&a3{df2xfih?ejmWr}ozUod+%THGVgPXN|#UKK&>gK|t6jYcdV281m z|7M@|j=|QR(-*=!QSQ`9eOL_qc#-W?IAAG>fV4O z%v^d!0s&@aqgmVI|AB}8L>hDHkajze$eintdz_S4gTCYl)Az&RJOLutwSl|LBxfIN zHgS8x;~fR*lY}*d1!AcC^vN@*P}upJPz}b~XssZ6>)K)ew7urz_n4N{8=h(0!+T1z3;9*k1^ykvF-8iQ&cOk%8CQAXr$9y47s5&*^pZK((l^w> zw5uMJtgzFS-YvTVLNS}Kg5plg?lf#xXX_0}$CCe!Xn~w?ryU^ZLE-=0bq<(Jtqc*b z3-2K|HL)oo{OqPNB%ueIdGSo6PZu#N;b4Gi)4bt%+p+Fj!RXoxreCX99G^P`4jYAs zK^ctw>x2bL%1M8^s+*wV+J0_JCVd?x@!ikU?__KI{lUVu{ktfheTyb~H4trk>i4m@ z(yI2TYC9GEd}*_lL{J~PHBa2I$We&A%W)Kkb)*-hEDAe_#fZsdq-Bpw=v<(d+W`$ zo!)H7FtvtPIu$^LsyTH`UhWx}3#oDPL&EwgaFE)MeM{iUzD2xzFdS2zzcMC@`5uI) z)JI~lhaWiU18DYC8kDiS5RBsWt7%`s3HEZLlF_)f&XBvfZAh9<19JHHMp`aKc*{1~>Q4az)i0_YXmG2oCm#+*2#u~+whz>@7_>ax3~d-y(l zTrr9=wz;>-PchRQO>5&wp?s|3mE!e;cOZU6d6e?Ct_P&L%AtW557%zJDyeBoy%5F% zg@ZbJ#Z~$xQh9!TRJlx0@9bA4BZ$XRK>MmMmUq_)h*^xTAG(>C9=KAqPll$*En@Vm ze=eAX_E>zGE31#%8>8vIAr1WSrszUDW2px)LHh%%UlWc4)YsexkkpnmfG~hblq~Ms zpe?Ly7XuT(GAig#QEcUw#P+;W(f1>ZvFnL&EmICvC}96R84pLAKH1v|3ugsZeHQ?| zh!Rh8t7xNY!>@O1kv!n@g^eOTc?Lv%KC#r==kjyYjz=vr)r`P7zahEZGFU#vDD;)?sg~=egD{KA+x3WV& zqBp3oh)vmIbi?S|HkS3UhP0Pmi7h0&m=%QViBMhOwjL@}{0n1-c>sx-z|)>^2NWgA zV)KsVN9r}b-SawSmURfNVg~qCMKw9kxZZT}kw5Bq8ox;Jg5%9Wx-ZQ>SdqW2{*%S&@G&+`B|1g~c@cFa#s=5k&TS^`qF4t>GG9k)NGAwu zDAeVsG>5`TZORkhv5EG|s=zRc3OP8c_AH3NAd%>Wtv+-6lko;=8co-%J9|-Nr|+4g7=z{wA!-D3twvIjDnQ zC>DpM?4>~c^_PW1Rb3)wT6tSt)fjA|eHW5rf!%nTv~~M_80yh4Y?|r70C3n-QC(;Z zi!D`FEE$vwwP49$#k)oBy8!BT0mN6dR->a=tHtd>F!3_EMxp7(|G(UCFpNKpH&hu~ zW;xK3uC1R{KmXXtA;aM|W+DAuGECK~V3yClL63?=>cG*ejIPv*_f^VfS+fy_VCulh zpdO`ZHFQ!syBL1VLiL}hQYmmCLM&ow4gp>Y6i+)yRixLm-c9p{>_na}pPIy+90UY6 zhwGK8`5vV7@`N+B;RMKMY!*7|-OAag(UqiyDkUQ}=_5wyyb=GI%$|5|>weldBuaPG z`T_UG8qRG3@Hk4;_5HN2oZ>S_9#-0wUhX0Yixikz_{)Sw-tq^`Nrxc1WH zi2U@6%*6EIpteBXl5iLG-pef?kIk%);M!xf4F4ZJQZRvpWCj+ss{yJ#0D!E;ZPQ-I z`8uhTbKIfjo)%Pebij2hR5|_&a=;Fvzrdnz8;wPGIrYgMq~x@guro~E$|{%CX+W!C zqk*<~ejXcQEoIK=5wcqD7d=Fpw&=Lhl?C_szm<1riKP*(pWVI2R$rD~Fw{Hd_}1ms z=k?^FujjL8!;lHGXJld=rE1b1TxSnW!bx!saQXbxc6g|-f7)7Txat^15+}`1`P68T zbLL89@YJ7iptxBrOuI1#fa|qIMFT@0_E?-F)zHuN5Y_i`2g0qw$P^e+Exg(5E|8La zthOt{gOUVgwt#83g+3Qw#GomysE;Y$~6bC^;{rA!y=C@O42A}ylEa=tr*Av{ zl*_nqsFu)D-uAXRu%a?SDfCO3CTwY!>64-*g>QJJqqnrv@}UTz|NHD(O*Xk@kX3|M zz}RwL7!!pD_Cz8LKbQnUgmulIBkB=rizwn4N#h~2aA)~ za}U;8pH(*XQh|yFTlXo<{*YV%bE@~3ua;=&^CkRz#~y_61eL@ql@*V4D5rUmN(C|K z&wEf1@N3gzxX*xVq^G5A$GAwOXK{q5m925yGSU-lVIJ?yeI;xoBV{UtkP90acF-!2 zO2`959^{&h|8w~t4Id7>(@GO#VTKkOb7Xp|q>#jC_4%1zJ~rC{%3)Q<`grc&1<^)- zuvu;SxcyfRLBFq1@3EfKXfPg+EBE?E40%ocJ|y*PY3I2=qjj14+@IDh(47_5c_blj zJ(BY$opOZI~*PclFI;zXr{YYB} zW5s?)jK|kC9?Re&7CONl%qV8K5qa%gGVRlUDoHaVsNrVUyA;|d|5TIuHLF%j3`Y|l zMo$gY=clHv=fsbrrUvWLCvM^I1R}GosM|DFY&VAc5mm3-$mn*J)GC3RdNmz4dvUCY zzQt?S4oI8WC7EIVT6N4VseIm3E=3=dxNwEgolOn=Md07|9q#_ix3+fSW2+*ZtKtY{ zNnfPdc1&NWe!R^7$yT6#tJq7~nl>Q^zRdUghERX;xPKkiy!yKx8kz9lXnd0rL{o^+ z?rvy00jSG0Y>JA*y{n)1RFy8p={uY;L2K6Dh!yeRS>8OgMW^qKG;*!#r>&ve9+OXN zR_p2cycW$dFHSg=ZJ$jl2*|w$&9xC^)JWLn8Gmu=RUPR8E(w4;Vc zrol!u3aK2qQuQ-PF(P#9yGcqR>&tInWV*C1TOBoNYeM_nO5L51I#nI&Gb+w#zD4~R=aHx0xfOqpDb(nSE z`Ypg=99-YBmZ`(2PoMLOsal728wg=T$S&3o=3>|ar@o#=iK?}gAIY`vM^v!RBLFrk>Qn@O_-r3}d{D$%Gk`G(fp3XAg`QcI%=!i-Wrf-7WDGiM>EWZO zsxGrO{vzdc;MRF*xlJtE(_?eb-Y^{W?t+kJ(AU;D?3H^S?6A8G9jy7ATqu73c(Co& z4V1|=U=)qf6wNS}ZO8Ut2fr8Pl~D%%ZECy7hN~(L5|7?%16!!0OO+x0=r$q}GLbTo zgidLR=`Z6>EdiyiX{%IbTsY6{V>2Dx79LQ|`B~vPbi62XTr?Maq(eXv{wBsd_VfP_ z59Ebghx9i@xZpUDyX7SxXYoBZK-kpJ=tq@NUooxy8f=o-4at<_Uo|=3BfJ#e*-!Cf zdMi=DOqI!aO96kpT%E3tSG8QDd1Ewt$02UTb)?X0gnPN9ecq0bvBYk1*@Uaks>>!n3a%GzQ9e{k?LG zX*mBYD2`5F@=0@J_hV4b-2oq7q-|e5V>CY2^^2{2S3Xk|F3Pz8@Ohm-fcG5HdyB4q z`+Mh9re#%=UGMY*uu1yE&X&PX-Xa?dN1WB`%RqxQ{;hkm%L z{p5bv49ZAQVY8)X`afzg-l@!PpR!o`Ppq8?{tl93Yg>)8l}!}#N32gbh(jp5j3Zl zmbiW>umR5t_hq2N-0D3@)xCf<9Uv#zZ>x(5I!b{NRqq7`c)tf1*6j_7^#1Cr|0lhZ z*4%dSJE)J!t~-}80vF+wXK&Y+!xW8;bpHZQ!*e@*=ot{TNQSxTX_gmjBYdYnB*MGF z|M9={K8}`l;xDM5+ODU7F$x>ykZ*6(_k}4I7wwu4lo&Q(ut&iU4_mtr-M%DFTDcv8 zWYA*G4AbQHtWqwql{3Zk8KscTYPY?ftDPX%B0URyzsz-b$u>VuD{fzFq0TLOAk9(S zI0DIhrqjUD9~prnE83-5i^ai)g=x459A19go;2Wy#@*D@$*J2ehx0g3{7R#MLH@lO z>a!$IgR02{ynE*Nqdcv%lF|Om5OcpQeEt<&i8G{}{0c6Pi2hGZBRr?ct;|3S~-phbL zgs3drz&lRRK=wmQNlCOgV&o;20?sefH5x+xer04cj2cnDIi3;@zL+av`+}^=Q8+B+ z>(GB;kmb>nwQTIS1dBbSQ?nNl=nb;Nn{lS+1HA{XY5x89Jyw<;ihrc^1A>n3iG!hp zX-wB@c}izUw>p;rB?pgWf1P9dNcKaF;(&SDandqN z(9c=wONV#V-VdfQ_9=SYhr_F$U^VnM({i&1L%Qg;E7kP~KFRiVLIA-hX)o*cW%_#$ zT~lBCLBds~OZc8Lb#oea?0!)v5emsV_LwdVp9 zWv-oGKC3|HOA0u7B#QS8y`;@&o&idCj@Wyb9Wu5%9Ur9qd+G2%Mv%NAi=;kW%{639 z-uaMa_9PZ=O}=&eC52q=Ghk}MP&3JT8oADI$jpc#!+@*cw6G`9U8sRh?>hAhp;|`% zyAyAA($~D>5Fa>g^m+}It<0EKvCOVWTk|Q(GIq@Ts#M_k!lQ|h**f6{LLJEwiB&jPPFA6cIH2}Y8a%_?zOo$xUUX0e zu!=>3h7wTQ@LUJ$MO#+MD{5ty1f=Q}`>@W2aL)d4N)Hi2WPi6fj8@VWVr#Y&0J0p# zJ+tDSy23~*(}4jVXY|c`9lh%JzS6kD5So0}%xRc9fGcO`HP#}PMU^sJW6wjV1-coz zWbZ)m4$O>}{;SfvjAi4i$L2VAy#0(v;6}yj^;Y70bO2mJV6_HOSR-rZ_G<^Ja zkR0xC%`fsA2m73hz3LZvm4gG4BR5)_knTa(e(daEMV&Q~n|QXNN#5%9+RgH8 z`8|MEB}baJ{K{k6uz2_h^q=(u_AS+psCmFY-lHu*Dglvp=4gIs?2PUH2w<{Be1y(2 z%bw4ZI90BXG^6X`eBHLcB}K#b)*GY_U=6BgBrMq73cu@ ztuzM>asu}Cp1@DS^YRD#9hjNCgmS`8nepsl!N4DDz3)nj*Z~gE-|BMEDHo2`5f|it z=G#cphu|W7U3t8XQ4j!~l;zo6B^`-y066Ggc^2k<)4F47`6mhjq`?O%rKLNRgmpVp z!%P3lbvPf#Nat1R;hd6jcI6QmGlP}Es?ov4C4+$RbUc!b4f-h*>x{2%LMRKhx2#HL z@oG)k@@yTrDanKPcR~G2|B7`uKa-L1mdVpunHvYdX4u#%I51PlLkBxu6o!MvCf&33HhhNIOZA>+m`re zkCSmtZ-ELBYE%)RU7@mOxNiZC&0ou10FvXwm%~}^>!bjfV&;tp+|&JRe)1F+Z$rfN zOG&f(S@Vi>P7liQk|(hQ8v9~RiyfXz`@tZ$mf5qYnxFn(bk2}P+`tn7H%{+LYc zcMpj~h`Jk7Tvker{p10O@+h^_PFqpl!F2g`!|u{aOYAWvvbzR_p?-;}s_O%P9sWF7 z{VT19K0Cf6SUm<-043c1HLh%q1HbH8yTQRlM>|QwSJZ?dxH`KkJCjQ_DIf}P7vaBw z9G{Egp^W9$T!U_RRpC>cmaW*5RI36PEjuBQN?jz|8=v$l<$WJQ6t#+#EGF2~sqIat zJxBZoV({VXmJ(_K>(`oKgveFnl66UYUKC_-1Cqh72ltdPxohCEmh#j*L-#!ETgfKnHzfIym?&dIXjo-xzx#XH)dN$P2O>rA9EZ|`w%$Pe8{?|cJaB2iM2Yy zy|aMMF3m;AOGZG%qSc}~Gr&s`Svsee5~{pjXP&CD2DDnOvLr6HzQ<*M&)mB~n?M;G z7v*6)YO-2n=}9t;j!qGV9U8QXq0(4%(>qG4e!xLrLztI^ahK2~ZX4vSXN5vRe->|G zn=lPW!JnA1|R0944UDcLdVXp^wPym4axN7tbTZ+am8R@QMY zFnwn*-(dmkw)5r!TOrxzON*AJ!$1n$FO+Bt$VV-J89kR{P5E9JQ`5(`&JTu#_*tPp zF%s8kyP{~@JPD<7jpVhjdVRGZze&XIaw9ebq5ZSWc}PGV?jhQsVL?mGbE|9bCdtc+ z=7Nk{vXvw#MaPaM(aSPRg*Fj;Rj+(F`oK?n(5^D@2iJ_x+1Bu%OzK%NnUtbzOW+7C zAYxnFCIkxh%xw8I9&_kLmiH}0ID3skx;`Q`W4oHh-usPyz{L&uH|D~aa8|Lr*brhx zBNZqiL>QH?%#k(}AViRB5a8X3hx~$n47GIhd#3vKMfJSD;t-|=%1l)!3w!zB(~%^; zp9wBR7BnCDHhi7A!?~#De}wUAu{{kuQ6@BQWKl0G{NXZ!5TI1NGE1~lML%_6F~ckGoLLVo+C`z*ys{(YWmMlm7}Xs`4fje?|x4+ zLf#C)Hz{+$I!_Y=}?AKxiO^C|Gn9mdUT<|vc#)nq3nB&bU~pYvjUBsXia~uSHaZY$&pjK0XppF6A#dZBWpo?3S_%i-;hTrn z>C-7kEKX%Cok-AFf>ddnWJzHI^ZrrU^U{9kc0Xhq`96XMyn^THis?vOyAI1}??fKU5vd}T*K3dQF~ zm3NMo(xHKM+B9iz4}Pe}bTTh%vI;*b!%rHAOi1OCj(Z>k@$2`0JhO2-WyPIH1z#%= zRBNrM>&)}gIF58K3YOD1=t9>{DfgmimnHW%>Z*_`8oAJvHxT!6wbRP=81tsy~st;vk#{=2ud(+02TyHj_|c$B;l$u1EXcUK+o{_mC}{jvt5N(?Os;x#nU!>VLznB z0VnTU(T&#clxeohd||A${viwjmYgu{FiJForpkaM=^wy4!>5Pp9?~!14p?SW_76_M zbct)7qn;ro`Q*CowR>mfH^OG@IdWX>uu7{k(91f}y(`gnMUHfkyl#}zX&4^Cd7O|Fi|P&-RI7~A`lg7Gau>sfzz@%)7)97 zEIa%No&ERYyN%LQG_!eg=2u)gD4{zJT$Df^Ne}V3NWbt{$>zvS35NkEhq(k9<^uiV zBSl*PwFg^-W(LbXI{T2gp9ZWr*nzwQ^qE7-chQ+% zH`mpC4{a^;1}fFOZc@iz!k$8{uKtW&jnh=<^T^ONts=q`Zd=OM zn7JP%%+&mS+Kkui+LAq8r~@9;Zj7BnWm9Z8P%g^ay5g7aIko}pG?UYy18T9pUyR6n;UJ5VBfGZz@`W7Wc(>zt zsB-zQeCB9}A2S!FrN5=mOr!nAC*`fH4{VZlP!=;z^#H6Q36LT@gBXe&`bWy6!;7f9 zghrj@p~$Gq*?%?bIsBN&34vM;=Ydhf!sNJS-CEUR!m zm&S;bYss*YUY(K$~*gy(C6R95mL2OwVfhA$-EJ;k?KdS6^(N`L3OTI6JsX$T}%1 zS$!J|%Sm#=gg#U$%d3D(gnJM}A)#XvKN7AQnfmwmz!>LoFqpiPmgsu@zxi#qoUiB7 zm~QjZZn%vG{9qY_ z{VZMhUd(U{B1Iii>w&2ck2>`5NIdBDAZJDXQ-NPGM}M9^JAA)e=WGnGOO-!jzVPL0 z{f$b@tvi~jhIMHQ71b^KSKePSs`b@gH#~VpdIBzngU+M!?mFtn%;~br_hA2JZw@QU z6=vI}uNT}CoW1AZD0-N)BKJw)FPP&a;j_boH6*!xN5}#D*WDhWw2o@ui5c#a6efI* z38^l2#az_;E_bz@nSnx8`D0zN5%?MFHq!j?kko&3Pz1TRsAQ%{*+uu${S;diBg`xJ!Ql*%%`x$OStce)2f+d{Ec*q z8Md{V%cA#2R+zpAjPMNAsMMpZ$YIKXta^?fGPNL)L>9FC9lw8ve51a*7gFb@;5EY8 zwh5XViesE}?FI6}LniL7DE-3K<)o0ihficaD;ydx23ho1p>;15yhk|N76mLqU2>jd zr0|{_eSiDn}db+>;X-VKa;%EG5-P@P@_xpo(d^F01TRcHZ z*Q_9>pT^{-r~@K4A1Ms%@$O^#dlrGQ-MDT{Gk>X~yl)=Uis|&+3(W9)9v$?yblae8 z3<}x|?k%Wt$!HZLe00zUy_AoTj6qzapyd47(X1itDM`;RrI=X zK=`7RrYsZ|1$#aBG_L3hCJ)m*-Jjvz;MdnioWXR|TzVAOQr}#FmE?2rN9D~02oXXe z(MVOkpBg%q=D?URCM%aCG$`O4X$T@mS`Pdd`CP=Y^~C+tM*^GzJ5TaOz|L{`Rd&H^ z(h1V;)~nw_c35gLE2>fnoQZAX&Pj}6@UiM9v|VC}A!UlpMa~*|TclRf^6&?EQw=7k zr_P}Z(;3i`Bk5~#=*D&h)qr>Cj5Qm>7^(;<8|%q+FLqNkA*ATS`QgvIMn8DoD6%6p z+LxQg5*Ff1OGQbXz1SuIwce{L%iiAUhAS*6AUKl!qB8bN90b3kV;@*Fb}3s9^kzFr zUDai5X)AlXuisa3sVRRXJp5JU76$;-nlA1l(ZU1?GD(FrYtUu8{=+G+Vroo~ zAePR8^UT0Q%-V8~*n#*c3X7oDYH^)CUPRSCJEIw#x0hR-+*HEGi&zEe_~7WI9f;Y5 z>W90^hEatcXFlqtx@&q7GPHJa^p_35Vw|6`c^=^ysvoxh-mcQDWf4j~RA26@#U7;0 zJx45zZrgUs;XDSfIHgoi#|x_m_Y&u+M{MSrHM-2s2JdVY!_EjKW~Nr-lKJwY^><$O zId#05@{YEbK(}=>LJw{P^y;E>{9LbD_ zvX*)d+XOwPlR+&55ORF6P}oxfd>z-G_qrwfe2gn+J)F{US{)cz?R87>eTXlYel)G) zcyJ%)v0>)L^g6ZTl78mnRnTE|jl&wE4wY^DS?C%`kTvu+*2@1_Fw~4B@Gih6`u_L1 z9hJ5F^g-4>s4)jc69*FzxBR|+YO%+SAE_cObF3j`#E1pmg&se`Ae;GsBoFruDCMDD z!PE6ClyjKwy1FIvQd`BgJyE$DxCE&`Rx;+C1`o074|rD&zlPl6*{NXJIii+rA=~Vm zcH_`01Usj=D)q>~VcIF##~=%3eWA0CxZoT3H|m*`Ve?G7KiK>y1?VZE6+*-gw1T}Y z`Ojul5I7yPu|Pw*7h$mfg}>gJsy{i&7TDyY}Jwusse-nu5Ls zTpzB_bst#saGHYBnwU}6f5{7(xviZRv>P3|zqKNCoEC2`W-6GP-aYsc{D|9lVM5Is zkFy6;?{NY+V>e$``u$JoC$F6PPk&%&NYPvg7%nd>I)_HxY)h1i8ba{2$9d>Ey{?`^n&OVBqv{r)B8;QET+T|JTQ zL$~6%m=^6SvCZ;>ttiF^=(MT}_u_XfJviv|FS&?vXi6k%`wekif7$Ly$-NtdNpx-M zv~zrFmZx?n_+-&|aO346Uvt;hDjH6K^lF?zf!@#DZB@H_S_m^b1hNLV>Oi4R)43l% zfFH15fliFNg6)->YjZZ85 z|2F!D=8PInaTwh^5cE`ADYqcY%){}Eh(a4VU_AV$y=HFoorhI{F0e^8Ec$jsaRpbC zhd*1v%DrPRKJ^0;Jt+38kyJR0N7&xG-_#7_i|JG$L%Sg!0ILU-a? z-6u}G`t0fbr%i+jNzr^`hy7m2-~yt^XoD)Ummq0OAoWyY2@;bJrA zK}Kgl81#(Iecm-)vyf{6k-o{MbqmF9S}7%8C^Tm3qQ~TSA;>&Qhs{y$dcKg6Kf+pO4u0H3zKl(RJT*~$AnX84ZDqoIT;IhTJr zTKfLfX7>NWzaz|S|7jvuJqhyc%$Jg&wB1t`MJ66Iy{P4LG^Alq7$ybT-VlEzEfW$MWCq%c-qs)ue5|9}^2i2qVEXfSfb zFGOoQQ|Y?&eE{eay@^X1?f%Avl!%sDmX-|@!pfZ~KHEOa8o-6i3L&wyWqiq^Y^qTJ zJdKWbH`g9e#?Yq8%@094f||Coyx{+a^<-Inb5=yE_vel$-Q9QzPnZpCRleL-Hga3K z!`4b@-0SX5CGlLtwcRaF4yNAfOKgqfuHBU7*TdeTL2>{}K()Uva&6dQ`xqup+$C(+ z;d$@s;xc8BvckHuMzB6rBz*lAdTtz{LTe|_f^V>I-uxhav$V+Jsc4*LgSRfs z^Q@v)qGK}+DklNSxu^a5PQvWQTD7l*>k4IMNDwoYOQBNYfeHDhrCc(ElUFO0XCW|Ok@l-bM? z&35%A*64!%lHBJYd>>C7;El0=2$J_sVbC)v>NxxWja#%*EqsWtBy2zP9#MQOyiS4S zjldIP+J;ZbC#5+0!gEBtM)N>T_LZW7P1=~ z+T8j^9-jTf3*FK}%k}E{kL0yA)f9{>TAup6^C}mXTe*fEmPKFi84OZ%Hu%~9FmdJyl zwk?5vE*6pC``ootZO|7r+D~!ETy5p~dKom4rEz*5peHdMt=Ipg?Xq@T&u}|j1N)#( zc^#F*hYZi+W7A1!{<$;E_cT8UZ*yC>f8+|)a_73NiG1e_VT30Wl0)z8u_S`0Zv7Xr z5C2>WpnqRGvz9is&GkTr+v*r<>yF@FYOEPOJg;7zn=h0)5Rww&4@*A0A>n$Edn!~! zam(!s*>B5?RRQl8OKC8FnC8^&d-OS)l+4}pGF8Q29_^p%eOU)COYyk;^9mMN@w|Ye zjoqNH$7v}%?xdmzSQrv#% z!Lo%Z>A)w4St!kfkMGkX(jitL$s-x!`+eO-Q!^IB^{L<`!QGh zC7}{OJ$jFrSZDtx8+2*h$p~e$+Ui4Vp5&UmH=56X(JSlx0!#fpFks<(UFkcu~3Fwnj(fu}V z*Y*CBszxEwy|ydE`^HDNO3Lq#4eqx{NTOKKQkRc>A1wg)Y#;#=KSH?69f&$eVZ#qP z$8q7cjc{CEa!G*l=Of;qAxP8K=`Y~l+sHk_fH(dDZ^j5ScAUT5=8qxeIJh}Z6SvSB z9aUdX^E!z5M$ro1ola{&>#?09UBZk}vvQLKBcRDOAISSTnG=Y7X`DQJF=I<#)zm41 z^=F2dnQ7sCxEWoak(FTxFYLg%2PiK)-%@h1>$4&^BQ-neUj7`Go}6A}fMoNks#&1h zc)kliR*cjw+8TGletzF7dTWB273!qYsGYXT)>>cTjqNnXXL}hr6gnfQ0j&%AGke;g zNU<{9#4{EpDhqN(d?1pGBtR5ZNUd0!} zwZHMXZ0**ewDlY}22S@*#efdfDc}vI9C315!er$Idzd4rwwAFR`plh+?Sfl-1 z%3KM4P6jn9k#DyJDGMp(-&YI)qQ#dB^~x!=f)qAamug2Vi2ayw2`l>S3c1_epSscP z^>N>4$x-JLMdS_Q?B>T;Gla>gAj-v2nXgWo;?I{uNWEE6Af)36Zj9}^UryL^UKBiD zr#rsb4P4xmi=Xqc;%Ac_&-QQha}&?v98a8g%5@o655ArQ5xRIW8MeagJ=R{l3((qM z4}=nf6Yrv9O2a_%Wy+9%U0@dcL?!XTKl!v#VYfpWZbvKOU$yUjATm&hT0Ta|Y$18S zPoFw3%BEGliPXzdoqAB#4_D@jWQ7O7F23B$X{K1uu> zpRCv}vT`KIO1yriw#Pv7FoS9G0})pOOM&(>WvCtY%aLx;Q^(_pAO4d`9~W^wl;L)` z0seJ&#!Io?8Z)J;K?=3ycPQ)z(mZlB-p$9};yTBmjTryC73k4a<-w?H!9$JHKtNE; zz^0qL#Z{#+4qDqBn>E0UBhG$#7ZW#g4L6)A2>eliuw}qKe!byy;`FERIdLqitgos9 zjDe3omjA?7q;=CGG&d;3eo(?+*hAoj^6h}-B+7ZmjD^$8w3W+_NejXM!DGgAMEi;J zR?a)mTR7R7(@jr9{WDG3*C6vlO!Rd6K;r(Snk${nm=4asj&1cE@hZVm&UX^c+BF$m#dN!d-H|o{pQU zs}WLeL7wrrx3iu*vp%_IWD>qB{h>?kLrVXvoA-do3pl&%!;fg+GK-bPaOH`T=*GMF z8qz)omj&9HA%@wRS!Rd@H^g$t=jw!$r~7G;Wp{GWbXeu;qQVca^O^nhc}%_6-RzgQ zL2d|)YXBjz;lMPm|EHMTpr5$3KhP!a9K%kVh81Df4_J3ZfMIW*> zi^JnlYB`jQsRvP~>8Ev(rY#-n>2#0N4Ybo4o)@lRhF6F!Ss0h`pS=;wG1~2W|IG&F zE5HO$BvxE^T}in6+rR1qmKwbu!r`-HE` z=I>6Hw)^Vd+|Tiq#O*`xkj0>c;3K1oyu@a+?RD##QFvt%+^wepQ~$BU_BB0c@4M_B ztX0^B>84ldHFrfDr52Q2D!u>Y7W0~TX2G4rhVH9-r61$=6KDJ1!#Z{$vTM?LCD3eC zp}Mb7yV#G=AT^pkK#0MvLQX}xM@D+ZWFH1n%(`%C=LK{vBCle0e}n>IQ|;Jz5>t;c zE*$?8IXC|ys_vMb5zdwO5Bc+uHWEZvA#Nv9imBbWfGyqeSJ*^3LIM}-E7Ynf$1{Xj zTk+8Wj}9%24P)o2#iBMfoKsbmBEWIj`609IJ<+B-3yPe{=s)s_i51UOf0fwKb2^Lj z2v<(nF|>s!=>_!<9H-WGT)gkdf9taKxhQfKrk?LZXi(~sfx=4cg%|0Crtb|q*W@0Q z;qgV}p6kyp{%3`sSEMdLfMUZw9a9&FJ0P1su2e4zy~gJo5oYbrp2qi3C868s6}fWt zfEKAl@Z}A)cV(X^_}LUhx+6pW!@lUYJXJzR;{8)ww zdzRyHR{oDWdJv4+-ukloDX9Zp+womO$6+D)htl#i+n^5~ZA;G*o5xKy{>JZTi+ z0+2N>YXAU%(wVC@NUh^`>`3_Xgu@Akq5!j6nF5*bb|EFExLmAqNz8E(h|$F+xASl{ zPF@#iR{u?O6I!oXI$lWK`K8}QR@#k zB{8>i+mR*of+`RmYbw?tOB`mfBjNid9ZxubZCvjCH2F<*2BvQRo`7Kb#<{gDr@8Q34?Yh$u&Cdz`4=wfJ~7*<(}Gpn`g3)JzM(*+-RQg7u!(13_^fp zu{c?HqX@yIIM38yc;VMO_1fQb%)TAd)^Ku9T5hyTi&D{Fu6gCE;aChM$43OIVq%&Y z_VrvCo6r)uEgmrCgvy#!m)e{D)%F^D_N3=VsftlbMkVotuoy{MF*xtsaa{}P`HIwp z@L~r@%Ao`ZQOJ2>3aUg17TIOGXYQka-{eYw=D2+urn7Y@DP0wxqe14f20n9#J_^4* zpnHSYWQMdQ2=`4@oDN3}94E5|yHl9t>J@0#(g_J0wR@+I)XLG^j>9!5C2u2@THU{TG^t0NcH5MEJ@uo+i7S{)Odan^ ze_~bb{lt;W=qzk4>&ie9NFzB#75ru3%U6ek);j~*aGVdnI=@4g( zNlLigvXNb`U)1jp;00nT}4t5NdR8R32WP&%U?lV&UXn9i=J>Iw^8~HK^V?Ao)5=O01fS&w2%7K z9(W%udXKk{1d<&&p0(7qTHt~mz$GkHIX!l?R4D!e~dU^$-q4faVW4Gev^RYmf+cp!qysLA3A%mrAMFs^fWB zh!%rIi;p=preUj5#oZEoNzG_%jPPp8X!Js=DblNv0<8RRtBhTQ7tmW!x2Jc|j(uEU zXHWZeg~_3ddGymlTPz*0@Q;-wF=6n~9AQyCOa#j|DoB72RJAanEwNk$$D{~>x*jfo zkN^^kR@cCV5OSpwT$xt3s0)0{H2R!Sizb|dA@}xqNFDm3Y>~K-L9U3pvkSja4qWDh zJ#XGvZ2|9+qo_E%m5;$`^wOHeVk)oz*&=V-Wv7-o&x?58y78qOti_5~hy2tycuCE%rf$JLIyxF6-Z zalI@B0?rd>5Wk7vBUXRxQyUMLCE{$W+n6$uEt&<8pxi35cutZ9kAM11GP?8uDjEyh+QvXDExbbWW=V&WACXrQEc_H1u<# zU;E^+d%j^`3F}1RAUni);KVg@TF9~anz1WAh1pW7?7$e(5@t$%I{Is%IwnUvz}^l% z>DKqA`XV$9vHuK!R0k6vgl$DRMNYGd7ThOIk)pYGE+DOA!oWG)dD7c)-9&Umv>|q! z;yKR}Uz+dUgRc3+G_#km8-!k+@YDnAW9&G`WtPOxa1D38FQPA%!B{)DI|DEKM8BH2 zX@3|#uMt8Y;pdwb6^jw#oxIb2?(c-2B~H6T#nOvVa!(Ej>zMF%Y{b@lCn-9MDL;)= zev3{=5Oy!+%6)b?r%zh!euPQ~o)Z)Ljw3m*@4l@O*v}j7ICSC-*zK?!xIEJ>mue67%@+(35yzXhEsq0%ZPdV_9+y zGxAm@QZDU_W*GNvnHkb76z$m8R9N|z)gapbdCe-Ev2`H%rh2KD8o!&xxoy?4y zxJ+7mg_sr(BNRp5A*KQszoWKyK0ydhUt@|Sd~i(+0WbpNv_p@Z*<0o(11bcLl1l+^ zVb|Upi`ye!em|4<09%6Vs$bg4+J{AIwm@{CVbAfD0gCL}!pNt9vbguLMYx{2<(E33>yAF{AwrjOL--AXM2K)}$7SSsuM(g`lmV(=SjVw1KeoIzmLkbYdyaFeqZOn zOx*kbeGk-MwMluBG~9W0pZWnR57oSI|3hx&eX;BuB?WBiBX!4%=7w`?wW@?oYf!Cb zUoy8Ph}BeZsVaHzAI`UMLXIN(Lu+7Ef(T3OYLEsoGugPWv?i3P7a;aAC%}r5V(qJO>cCIJ zj$M7dfeXU_-<`?61@`>2mnaN9)l02uI`-gi`5Rj-pHFv`C;&Y=lYL=Hh30<-S!h?& z@WI*URj(TDq3qYQ9Y*v^ovO5evv4i8df`P&El<6xi#JHP^r2JM9pv0v7zM`m%sNzl zwxMpVY=v${16?F(tt|!40!81D-mqEUkv7@aY`|Rg+&|60MQx(iWF-%ys_fh1hX(oh z90j}53fp8>+zC)6)^AO?n2VX%PF!zBy+aSK3j|H1idB^Nq%al#LiP~M64PxUMN~;@ zMF_Q2lj_rF*U-W`chQJMa1*;nScfK&Q+)&h5l(l~@jm&jgSW!hcvN_yb+4 z+eJPp#YY$zu`MKtobDqE4D$y}U%hHRLtdY&EIfQAsgK_m#h0WP*)pGtT3E*Rng&tr z_dmyw#n<;ds0+!rbcMwfM85`uB~H(G^c&YKATqd^ey42g_?6eMH9Z)Xg~_F1-K;H5Y8A((JGYWlN0F`szQ;W)xP6j zbS<{w+w$o3WKxQkC@}hWVL3{Mk0dbC7tkzUrp5xB4#IER=4Rf6N|n+y(2vL`>@-W& zXrmM3sNq1utWMVniaViK;`DL3Wx~vu*o>^EAwe2Iy!Z!7#t^b%`GRM@tNRH|iqiXc zoQxhWS1!zqjr!Y+DZm4WS)I)uf>~BC{L4FZd6#R^{`H?R$p?VXZ*4Y53Baa<03c>{ z`p&O~+C%B4R2+>$M=KJGMhQeibrVuXJ_Isr`NMvZgm8Xp@o$FS&}?ojE>e-I2kyW8 zQV0Y3&TruhL7q61RL(OJ;>#gf_zAQ0vicib9ZCXV`j#(m)D?#2t)7b>-gBzHFigI3 zCaU|CK{nk@m-^`Nb=2PB> zCCS$Ibtd2-CG9dB5PzgfAVmOwR+z$$l-)q35pf{)?f>S7Itb&}fBE=NOeoZgT11TV zM*4keQ;SNICbz^hfoH6NHR7+9KxkH$z)ZjlJa@Y4loCWhW8n1VW=tMj#{x9KDpc+X zm(9(EZ)2I6e}>I&_P~f*0qk{rHMRg=NCMfsYVENYfL=ZT9EQ@|j--cLCA|x-QQNK0 zyQ2atK#3Nq4#)7^j<^C>OqHpvAE749f@F<0DlwKCE=Vl_#hu?F+eEzyP5HpFBXduu zaI(v&m?kYXs9B zBN?Yr_)hDPO&?38C(>8>efTEWCS7O67)#t1#C?Oc>`+IN=E+>^|9i8wMN;XPc+>Hi z5PYem6c|^GYi5SFAEWz*dW&F5E`xp;s1+?#wXsBk?_YfpoHw3Q9L_-liH|y;Ac^Ock(&oI&)t1@!cDfFPJwN*F_%hU#yef*?8}J(3}Ouz{e4N zhYm@ub3bw9r*Y8pz>=(pq$Fyf_E&81T6Ndx$U&5`CBA59^)AwwV>jbA@V=T*xIa6$ z9?G_1W%;M1FoXGme{_8u`qJYlVSH|PQX<2L@jHxFSA9IIcrvJ{%tB9vHdtAiHg z!DujgedT2%z_KVe>?aE9p)4EEEWgAigh>07S%cHmvvBK0@4L4sqXtX+MfP{0%HhHe z;}u;175alxv4*t}W2@ZOQ?SjyVFClz6#(@24k^S=6ZTyK#QCkvqgBK0O@h7PhhYMI zClU-xM0O3F`kxuuFE&$>(A+|f#|1FFhL=rhFSdB!J)?CVa;VEh(9uzaGTC>2K4E#E zpE(p%EDzs@VTE*wT{ctLvSr*zBajsm<{0ES;YTb{5?pM?aVOXiN9}BsL0hDJqSqop8 zqeeEqzMb0ssquBuEwA0(>M|EA1K^`9QzxE$g%6jj?Bc0_) zy&x>Fv%ex{uXC#VArNB28I9u?!RSv>Gs+=vGBlfULZ=z`P+GJ|^BJ2*2+sNlO4F3j zWQe<_{JC8pG zo{wN>_ZVZzv9SzFyD6RoT<$wCyOXGO2sMO6>5RwyqR{u7`#Moy*2*a5V6GS^^DAIXgNqoSo+@d95-L3SI07Owg|C7!TV3Tr zInDVk!Pl3{VX?@GEP#gZyk+02*b=tJG~qIWiK493+>(NMb(6BNo5w!iTD8Vdu<-5% ziP-PIJXJECN`KvC5CnV#ugzIfTO0&eCM6KqQiwBUsq}6Z2DZ`ICKMC_V!D>y(j(3o z++R0k=T@4H5;cA!Gyl0#SzibJkLrRmUIYW%OFzu$#BWOmv}86tOpr~}f@A|^}Zf|@6qB?ghc?mNtpPR==i9@xALWsPCWmZ5}+c0sUp$3!>LU1T$ z{aJkKgzZ?C0WL!FA6qLgk$CmFBqa7R1>UuVCRuz5hY-FDSh^oSrbyohFFX#v!eHFH z8C(L&z?dp^FQRY?sj1kv8T3Skx?W6M|MF55sL$L8?ch4y#Z1LGUg4b+8f!m^amm zk3dU{bNmgv1vCzxd;P?R5@vm6K0IFWz7u^roK{*zm_jwS1<}78;hQvc02S>LfnKy- zD-aI$cpAs_Uo>-{74**3&}IR!BL(d!0Jrt1?9;DLWH33Ds^cI z(@$OwmUt0>&T=8~&-@s;G?k6amjfj}giKYMG|Hoyq&Q%-2D?@fi%sAJLIanFKzQXqj$T4y;7E?g zul|vR9@P@=HDM3?%`*5kNERWXsFy~Rv~Rcvw(BZuAAX<2atKOlr{O>%vOA~&%)=u# zZ2l=6F0|_457t%Gs7hZO?$7)xMOUp?d9N{{Jjvp{fVPB1y9iIFGKB#OC{uubYaRcP z-tZus`0K&nzDtEzot20!&uo-}R^6()_cgIRe>$4d@i&Y=Bp)*47@LQFbu^1OHHOi= zMC#)^o$Nwk_Sh2J3ZS3bBVlP3AI3E4$HI`f!USJh7&gG zcq!U^vZ2pa(EAV^jXPV0OWjyrz57!ur43w2!=IM9i0OVUiMsZ?O#ZKMlhFNM7IOn= zMw~Am*sV(dNOxDnjw8fCgyk7Co~1;ay&$NWUdK}AyB8pim%+i&Fe9;jO-L_AU7$`K z5by4Ag-^r`^GP^d zF22vP(% zf?vtuOknbF6Eep8!1mpdLwenC-aR>bGnUHr>MMmMDozR+hnbj*WySw*NlpO?8+AS!T_J1KC|KDm!($BD$G(fvN5n5V-~CQ)sOh~R z@%jbeFLw~&%Xy)<`_;jxN>ATOOoHc*``5u0&!8b0VIHYKDr$DE1t@!6ckd4z$|LMT zc$T&Ehy0bw;T-K6?0Z@5@#HL`UnLsY;Jko28pO1r%Qw5E`y=j|nR(p1U2*s6$*_%Tm8@EL^f=x#~ z?}bM14%AFeBly#(cV<0EP)EP9w^iG4kFa6RTYAj~j`e**zGvCXiweIeyxGtBLH}dr zjlRy;dY!3G2~jnJ0AbiqDPDgjbG#_6(;Fp9(IiX(G)ZQOr4U4VTri4FCe>B$WR1>k*4?hNB^Y`E`Cca8#D-olFVyf3To=d^zxFzHoq zxH7xcdsRd7(IZB?<43Q*MngzJs{un>V$$4e&!_$lWE9{h_w6%6&ld9CvnT z6Dvw@qFL=-yE8Ggg%hW?P`?BL)S4{=Jc!=9slBQ3$nvcOH*w{FvLhs|iFySrqjdzS z*ZN*9oyBgx*F)>X39wjWTFWLfp<7MDVfolCyf|eKXF;<;juof1(5&>m1Ra|}V?R7Jl*ERq(&xG;9LniW0;@chz}Aamay=NvtZ1!FbqIGkJ^ zj4g5(1e?CY?W+l_Epg1YieL!};4B!Ysi-ehW3a1mS)6(c(D!vz38!rP59JrYz+?+P znB+CXvhPVn;EI$LoK>Y^M}VbPRuY(6^!NK#d6*jsW&rNV(lX;5xU<|2@F)xllYg5c+5mW1{-_oN#@w&u z7%%cI=ky4m`qG`qi`-y&U#cG?)lE05NZg2Z>42t{qr{pWE!&mBzw=;z&tY5u=jn@W{b$A`xcy9|>5o$_1TPep$>3NwgMq z1F9KiCemls-pu=yu9`WBd-zcLjy)(4IYGbi@;T$S3jPM7;=Z)tB`&kPZyp~borHk< zsOk~ophlw=OS#FE9R!qM>wGlmGWTwUf60S^{LlL!IDU(AqM0E>k_#9HA52g%@uW;? zm*7YkgBg~c?x>&#j#Sx$;f8-+E)U*maC`*_TQL=j#nMcfoPxhVQRm zeTI(5#g9*O`i@Otrr(RQ9dkdKR!f`k0i%gje3+=Xp%#FH>DPa_dEa3CiH(_WtYjd2 zZP(ku5P+`j?t;4P&e?Exxy|s$m7G~BlZxx*FglD?l3y?;c%JByVMob$K<_F=B0|i`UmbO^X+W7KRY#|7=iGu zd6yE@5(Pxk3gD*p6t~FwLFN>4S@l317*aUxeNOR6Vt=&46-T*2hUEPFr{|>4wDuML zi^Ws*utNbCfF10^v9x$0ojBTt=<(q9XeZ_LoL%ntK*>q!7rcs*OzDh4cH zrxX)1_Y8!)*QKy{f!p|MHA&I(P7++TH=k>S(a>c|O&L%E6MZ8O+puyLQoP3s{i*C4 zD$#HupUa6NFJmfw?rU4TTa=Rtx1*+xi+J6@7HoY>1`^DSN`MyNN05i}-WVJ(39L?_ zBlAFj-{TcM`{hSNKLhY-Nj<;NgDFhp)Th1Ha8Vy04xB>}aGxm#o3Q_F%3&)hm44~KOgiHY*O3sL*wC-0_@*TThcp3YDWB$z`x zndPs@y%N3;{UNar(V5D&=H{+;bgeWM&ReD1TU?Dq^U;)B=N>Z_{Zm~JPQIgM0Yq1c z_g#OK=B+S*t@+)foH{#GqUm6~m|?e1-!; zrWPC}JuaLR+jdq2hkSHKKg{auTh&T-QcO*Sja4dKn~@s)=_HI`*5h9$!wZ?CvFjjv zY{3;N{18f~$}2!Es3*X(XXu|ia4x~QkFhv?A4C;g#g&0b%m>7{#(01QoXJa`Q>(3Fatt`lc|ghoQwe@ zL#bp2IDu^K%fQkhZpVC}lX9uYMMxNZwXG=ay!ahRJ-G zv2GJw#&pc5g%U5E&1#Cmm(G-U=bUpKQ20ld`|Z$5t&@e*Qnmdd zCwj!qNT0p4By`(hOx_<9LdJ6g&|*K=YnomjNiAg?)~mk}KlO>CcIIFY5KeHjQ$nKz z$wZ-^m(~LIA9Y#w>vkkHA;k7wpBs;5oW&Dk9k;<)B_-t-gWm262PGAI{SjsO6)%c67CK_t-ps4?xAqM?Ek4nYa5^ub_)j zh5bFZ+^d~U&azZN3p*^6)Rctcq##6t2R_8_e*9*dFoWxR)v?A7MOVgdkHtmdtUNAA zT%7{h+}K=6c$G_$;{hjeJ4%I+cFjEr9D59&UdK1vm8Rm^X*uzR34M5x-8q@#jrb|T z2VSL}(~2yGQbBVDlS410Lb6h^jXO`Qhjwl*R@wYc%0iTSoL<16&`P9p zIWqIwV`|}AqVNDr zn6{0jO~sRBQ9%3Q5(m4a*68AKfjCc?ZTSI4*DEeY_$RD6D}B&4mGFfWm1 z)|MvZ?HAbqWjt|DOpA-xIdqBZ{IQ?^5)Lh`y;<&6JuB@#+9-*#LHjkB8IeA{PyBKdc68v+I`gd7| zb{J>KADAn|&C*+7&6|B&bTS8O%Juc9djE0_eR7fY6D#uEYGKdIJRL?6#d zKaMPN967r}Nm&~J57?O`k1dT|4L}WgjfeucO0O%u#QvC_?gF>5*hT{C2LZ^B0T+fH zjq-8m5=beG#C4XQ#djWBIJoO~;zzdY$}wRuzL~NtqSD)A`5K9POfsNfmRyfwCGGtlHb3Xtqsq3Q*6$y@{i}7gQuP@&k8$stbZFyc=;~K7 zbsH!(QXg4Q#uZgK4A3t`dS1Zwn^fY-;(D|mRgkeh6$2<;sZaug!sdv%VI43=3N#z^ zrseX4Djiaq>b#?AVIyjAZK2xcKT@8q%hK^b0wae?FNWov0yCb8A=Z)RUwP;o7-@zf z%;g;R3+fO*WLt#!SMEmb_L=QP=&R}C7s>DQF}11ny?Iet&g#aW>hhwqobnqmHHjEw zQWFp-X5&d#_6Lj#px27nC~J@k>xrihaJkNYfH-Bt3P+~G1s)f~uU}QMgZ{2av@}n; zGHpJ`4Ensq<1F;%CGa5=_D#s&$rWrns&-a!z*9WX?eBH~SPQ7!#6-4pUQn% z{=p=+5)CdRH|BeYbG`vnir`2|@^{-t`ka#63Sgf9A3RTH_wuF0KPetS?=}ynSFQw` zeIG0UOW5mF?@9yb!SQpX1EgKBdsd+PORJh0OdT5v7X~o_7f3S?q%bpC=}a^rFN1_8 zch*Z-Abw}Mpz1_}U_04Ay8h!^YJd_nWOyLaB{`5^Xa%+8Wz{`ghatCJ<=jGEa__^W zB}4n@d|p}K0{FZ>o-DSTQhcE}$a^xxgANxyeK`eN!r8{Li4>eXMvuE@4*kH-Bq{hu zI`{d-ukO;#2bcbx=}=<9XI1rKc0qq61NcW2Sa7N(~{$GR}v9%lGG* z3+U9cC4LM(A#nv=<2*O|02#l}e(zR%*&|X5y2h}=kH^O(??F|2-z6+uRRXtYbO;5l zDG(YkQ2X-18iWF&P*<1PS9I4P6$pi_8dTZE#k%aP{(C>6(5pV(gj7UXR6TCAR<Ho`CfOcA`{`crzk!O8c4eK)()+b#D^~IfgEA{?!y5MWyqq#R4m;gzaHRM%R$CR^T zyG*MHDJa0?z@m%aWn6{qi@+dc!- z6Zq);swa%8Ru(!Kv=_SHEEbXveFn%UGNV^iPZ*KGVJ^h@bDQV&4uuuLjguS>^qY`> zLvs1@oQL$wPeDOfwdZ9KSm&_-*yOQ3Sk*Bl<5nK@D^gO00joVDM@qrpPy64xhj$$| zx_~TqneTo4Z_oCH7iA$kj8}M_)cxyNs^ElN#{Y%JM`@?l3SQ%Dkp=dR)WH%-gBGbk zDYJV(XttbL6M|dR26UD?PVDL%u`a9GGzzYuQ_xjYS0AUF6j4Hj*-ft+;b4#U`s+*-H3ZE`&&&afH3OMp{C&K zYGTW9<*QvA>npqTm7Xm<=nC?_tQKAJM|9TT8wK$wBRrQjLmjfESGeaWlYsmmMZkGg zG01*+uj92hN0rpOj3*rzHA)@ln&&=xO-V&;LyCX&TYc#toDu&2DWS3YYg92JWvg8v`V)nEgc&T%*R7zZz zlh(oM{<6Lp;wHLCW>;B%=1N@a;kISkD6+ubDoe$rdzJgW7tsCFXwKK~xn{FOTvm22 z8~j#TWux}{PIlwK;D+dzKG!v~A=z+ddDCIanHsmlEolJZC5TA@J_EsTwQ!@6AVX;R zoekelxAEcZ@-|~+HpnKRJ-*syAEvcqcXBFb0(kp>N~m7XOe4274ZAJ5{rOgBKQccw zmlRD281eS_Rvc64y6;G8GqBIHFe<&?+g_&Ae;;rMg5HWe0)-%5VD-vI?ROSdbGDLz zQv#nCr{MmZ6L70d5P5wV{j3A7-jjzp+6GrIN}Y&DasUQx_7+eV7McJvbZ)-VWaY z|1bv;L=>bmDZw8Ug8*1Sr@uqdG6u)E{O3WJTh;JGv5fHk)exinZ^su>{Iy)~CgpV6 zaMp`Mw#P&o=|14<3>v_-9WrHq)L{Nrb3pyh4O&x9otXh`2!Y0*Xh)tRmlRJ24 z)<(pQ5ang&WL0r^i%by};YPN4(-}0$G81Y@wxqM5L{*$zC$v>m-;_t6 z%F1OoobefJYE6jip~_2gGtJ4ps|(*XqKzYzFZ~kvnjh8IJC#t7{9eJ=gN)(Wz_xY} zm8&ju(LGdM^PqQcK6}=6mMr3@)sNMBcD*K45_nD%s}a=|)x8{7em4~`^T)ie zVnJkH8JpJd;xp*#>|5g8+Z~AQCq1_By#}wEtvN&II@gIrM{-k33tYJ13Qlk%sU@1&tSgz(bM&7&~fi&#%0=! zwdV!6oXU}v(uBLQq~GYFpoZ*;Lkp(xNnVMv1G^tFRMQfXp8rUw0Xc;q2V$6%k?!tGTmh|G^V% ztK!)Ipt1JA>u+qx_TujkPl%?+U;M>>T|58y#D24<`)}~`H)|d(NpdP|kAm-`m(}e| z<={@jG-1kfQTcx(z$Bc+l2U@xN^ymW^wtS)*~oOy|3(;Vi;h|zz9-;O_;kf&1T6mP z2dmLEcC5BOqA$XA*a6rDoua0CPVZWu>%H^BYw4Whk zjqUx=gW8#)P{QiIy>`P&p0+f|OO`iA|FK^BTgtas%L>JQEBKM)=0=u-E037hQa@@u zc^NnRomlLm8ZYpTsMFPAYGP6bwY%HS3cka>eW@Yf8OY*!ha*B%Y&AbNCdo?FSuB?hCzJ7Fg|19H4j15(-GJL-0PEu>_Z@H8EI24 z*gSAl^xP|;asTLjaP@!hTiBxGE5CF1V7~T9{en=k*h3P>ab}m-B&TZQfQtpq&y9dQ zy$e4!^jN5FdiHLP>~q`!<~?23Jt?`wC=}FHA{E`&ly2duH{397W#?~4H=kVlz`8B> zK}~EKgd$P-MBt6C(z@*fjaa$g(-!((;s)0{$Z2cu4!w^uOL<=3c>g*{&C`9uWAD7& z)7clkN1W$yLuz|>;WQnXI?t=&o>wthDecBo?Oumr!W{iDSk;si&qDC5&;;xR)Bn|a zxJ^h=RZ4V$c{3zCE>k!4>yh1&rM5UqSSIo?FYwj(2P~mLxFcXa`tSOyBmT*SUS>1MGT;&mx0r7qUiYuiO1O;N|oIsIbo%jefOB(Hei{lUoGZ-NA}`n7 zyBVe{St;N=Bz5DvNXxa4Zbs8?1Q+$%*J-hx7EP$zQFbjEElQcT8SHuE3U978H6B@f zA#)Fv=<^`-$*H2ld6aryz}WaZV#Tx~raqF2D);*QhJoJ7jj?a}#&3WKr4R_D8m$kM zn~#Jr-oAQOW~JmkPwU0}Y~0{{+0~{$QO-N4w1@0}LqRp@c)^RO|4JA_^m^O1X$1Hk zW)1>!-l13ZWYRzGlf^E=oBM%kZq);oVc-b4y0RNh)hA^{6h-!mJ+X?W{#+GzYw13J z-3}@zbY1I`rvVFzZ>R2B&uPPV?9p!MpSXc=_ zTPG!9E<`RX-%XJOS1nKna0to@c2+*Pj9O$NmhbR1?0F@N7|_9;5M$f^>qyt%xT?SA z&K?CsK6?lB1+%>t)$Gex%k9UB+OmAEQw=3^6`Tc`xR+^3T!6XKF&PnI5%FwFAY2o^ zFcltQ9`R&SFid?8f~ywsc{xG|&L+zw;Q=1V5oflVLU1M#pPMVR#o0bexpqz+g%XTn z0beY(Ztt!Z^7tYl6dMA;SBm+73vxsd0t5%xqEL|t+}Xb2)y7lYak`KxHtDzy&~Z@c zVLiD1y$UEC53L?4mBop~fB%V)8lrMaX;kxC2??B4`^v(qM@#eK#Cr2T1n!NGft%IR zskx=)(OtfO|27cI;z#1trnU(n>hO?%WRrO_NT-}l>vrf)W6(NcK6U!s8MozD2E^0Z z#DK3ek}}Bcu|S1W6}z*^wzQ>05v9%f^M{YXkfbf|tT;Ra1d9XTs}H@Ltm>2d0Uq#f zxT1CP#=vh5^_MDYFt@Rst8J^1k#@RjqKi_@{R~G@jKO;)n#u%qRXPfvrd6$D`r? zPo5vKOjth%@b}9EX5!7D0bj2kv`pAIBJ*Si2If-*C#*kyJpP6_BJspl4FL@U9#^5> zSDWPjm=^j0MuSXGI`v_$JL_SK=qI3Ri^sUB0?=L5BTdYkP1KWjsb`v4Hyf!(fS|Lb zW3!NwRO9(EmF@%#h>0HOhw0}Z{-m~@rk(>aLFgx~DNouAknZQ};NVXMrZr?4z;o6e zC!O=+MW$a9)RubY}tz{ePyl8m_G4G1o`xIKePE6Ip2hTtyd z6nA7qH%HLZ8%jzdxFOw83dBukpb#>*zLrkR;s#Oz696)l?rudwNxY_7W_Cz6>3_wn ztXVWb$vBx$)Nyt%vqk{JGuHQ`6*{)ga|EaZYVW^ulUEqn3CEEVrOD%F#!${~@FB^x z({@K*QVrc#`3|CL%S`w^zIJwW3|jV zN+n}?_etb{2tlW*DHfMg0Ovz#k@AePFzXj~QN^zE3wCvGn`=|Yn_CC4J)qO}v(IGL z#DrEd$YEU`ENd{}`|n@c&(CeK?$Iw0DF2gAir~_^y!LR^?*BIFqH`$Cx~*?)CJoL7 zP(Sv!5&^GZ{;!~{Z-LYd77p#m8s7adOYsPIfO?!Tc;X(YS$OgK4*H&>85_qEstqP& zuEz<>oB%Bc@ zRiZ&#mXeML9~)&9l%@dcvGsEfehFNm6iR~$CRIHkjbTG#f_Fz(1z_|Y#T6P;0aE4n zq=4Gjm*n(ZPm@;`@OO@H-UqDDgee^b~P|;&4IviAp@h8o$T2%9hmKJX&nP~i~CQlZ3%ZC6V;q?)j{ z%F(kjzmx=GD8r-^2jtnjt*l=V5}rE5E2IPcX^4Fj%TbUFiM?${GESQ z^jygaoCe6WPbfWw<3U*f zu8v{f)rai@L4qU7@S`LOAUF{EDMuW`0jv6n?$M;FBiG?3_oL=ZHA_bqs4++o(c*Go z#{ z_sXne%b|1*PZSoTVK+!;KuIis15+ykQ2K1Kp;SYU%C9qw*f&GSn$SHA4&CK~iNc~O zm1dP3XW%6Kofz`n36)EUXTxFqTyTj3ocxxYbQxV^p)@vEB!zXh;8ccfVAo0%{y9S3 zUAYHif?{e8?27FkH4yBC&3Ow)NA)E;p#2`gi!JGK6&(9lt%|4LaDhVAi*t^C@x1V@ zZA)T7I5gNrtDJV-4m?T?dll@c73vjA`&Y-;(o4W2?qJKMyA_#bBqe|c+JB_3`cXcG zq{1+#?TTY)c*59KI^dg_N@<+-oL?h#`yrKu6BDk&HqbYUEJBwp4CHdeFrH3c8S+($ zQWlM{+7gc+-E=d+Iq3a34}*^}#DD1>@`-GNR36E$E&4aIKIL!IuuF$EsBWVJe8196 z3BKW5>Ue_(9-+`Y>kUu!}j^O*dN9Crae&D%HvIeqV&JHr>F+;*@B^z z0k#16Jm|>=eO(A0rU|V(-jxANph3Q^Xw`ZkEDoPu>d?&cNQip*T7p`N);k2gWt8#( zjr_jldS6%T<##W-EUtv!&m%4ML1NtioqVGgVu4)&=;PYd=?C)pTOfA%c+Z7GKWweWU^_FwcSdwkk|bGclh&5J5|?d&_rnZ^*Y#d~ zTl(vsdNquX(o{qe&>oxyg*g(jjcmO^%0RhUl*5_h-L`>0-wmJ`C?KFPEUj2C^<#<3 z@{z)Ba`tx~cO_OeunC_HjnBw5GvNBGZp2SIa4||vgV7+w!QxU-0Zauj=IV%@=X-Ld z9wb;kTFQn}q2#Ij3SLoCh^S$K{N#|W8E|_r+#jk!gJ2s+7GptZ&*%8*RL!<*uWe>#X22%rcR{-%XUR>w z2t-H1M@XjURh(8#Qtq1V4krO{+qP5=S~SQ4Oi5{ca~P%*p&c`n0zZn~Gxq1w+RqTt z>#e9f!1f4XbZ}!r0Ko!+R+&`a9)p2Q<$%V@k~V-2F`qJ)z4Xh z*sU#)oEGd=i_%A(Wa4c98brSA#cEIh3Uo-WNrXsB*bT*ek`Ci_Y6}a5a+AUh23h7c z*)#;FORoX>ps*OpGZwNE6Nd@cKBOakb+LwqE6x>LB}`K)cP9NT?FHrZ7OO$YgJKxR zC}buj%!OQHr^7jY#rZ;_`V*7l)a3Y`pzTegQkBZCh)BC)N2o)qqy)zSEANk8)#17w zsYLO9DbZm-oKVNaEEfch%%63JOk2*Ma4zUjq&)v+pvJrHu4bC?B`Pv2Yc|-V05({! zL`>7EfVbLG`ja63`Z1~8xnPpx)jgg`Rh3$FiD@`9~p>1@J=m{#io( zWooaY7NGpUx-n}*WNBSiE(5f1*FkL8IhAY=OpQ8(|IR;pk~f%&P#rS%DvVJFUSxvM zj~}t_C%7wiwx<<)vH0aqDQUO!TU9oOu!z#mnDkW@O+Z=dbHCRo5Wq2? zOTvZllAS%lDNd8T?tOW1K3q5_9<^}K{=7IxtQGv7dEHTAbn|<5#=`Svx`Ay&93hio z*2+tIdBfS@{*j`Sq5H@jznYW5mU}z&q5R#rae){#;&nWZqza=1CyT{{F3px)o`{Q-r~#QarS6`%~Ge zF^K)xoy3dW&GNZaKT52dZp}L%qXAoU7%Gwo(b4Q|LrYTj-szOL7sGpY!4J4B_hd`CPDUS~ zjoD(}gp!z(y7q=C&Rz5Er~>c1^7|K{+8f)Fe0BGtoH53|rsV0P_UMHd0{tSUu|qJc zaE(w<(27CyrZ_1vrq_$+gQs4sFy#dY#|h0QyrZI z-Z=%)f;fO$dLD8N16Wm+;PT(qe}1mIdnblcBs{)U1j&Nns>OW1SZusm-7 zFQU^AQ$=z<0`b{xgDg2gWCv$E{IjQu4FI(uZApH0{ML^fWgW%uGP+jI-S~a0w5xOr z{GU0ZXt}?YJoEiR`vr1cW)pDVyH~E$!`U#-XoVc53_ZTPXkQqBj6HfA98Y7jICSL; zOxc@(ey|EQ4n>}Ca3bP!B|->(!zes8F#tfiIZRwKUZ-nlE7Lbk)aU5O>vhd-#rj5I zd+88=~09;P}4t_HtiN0Wg?eIBMNuqlu}-Qh0PM zDMJxb`&vVdkeVa?QcW#84anZeS(fawJ%-z#JGTv(r_^5)Anr79y6m#Pvsn8mlz+*f zXW*ept613+`>mpRYxitCmH=$KwkQKbltUn);A*!;)T}#E+!bpfPq@Km|=9KrN2*Ez6Sff86jPxi+vLJV?vsw+aptzEv=5>A4*5 zfWLhHpXtsrem7VP_<1GLj{_X~{czx9*H`=qNF;vx*l~q63tGj@zuE@80zhLj=8cZ= zTM3*|%o9o^yKPSgSqVgdgv9$zB1#eI+>n#A9#N>9}Rjnr2(wlb`!c zb{zpY?2Yp}&G^~6Sr5x(ZrM>^FHl-c+A!D_dmd2cxhjIC8}_xnC_qE zPPv{EmVCMU?`ggyF&CY>@vC-w9J{c9zo~mFfAi<>#3wjETDzmmJ$#X(v$CdMohJ4IB-0aDfPnw=^=!`N0XKRi6gHVc_ot7EV!TuVX7D+EdA)Z*&pIN&DIA*FCh zO96#S=322SM}hCY!xk0cDH=E*N>>`kGKCX+PLOw^DzrO>gLWnpQDIF~hdHy_22?Rh zOO>RjDl#uX$@qv%vsU$1>fKZapq_oi27_JUdVS_lO5SePY-`4$G}Th5ky(M!^3H~Y zbYX~1xScpD1M77I4WtE2b)a$t+W@1gLnc)W6xT0|0xbqz^?EKI+IH}x^8(?gsBr5k@6n33K))mjVoB0bpq#&z~1^P-=&F421)~jbbmly z=sLHH9BH#H=%4b)ATWXvI`X9h&4FEo!2h?L>%S})(gZu9eIAm91${{$Y{f$eLJA+pQ06V`Y%QC2 zZjWUh54V!#I*HIAFvHdjcfTWood|hnQGsJotV2sa_rpLjQlJ3cBVCU=A8@9ALWk}w z3f}SQHgi7$QhpIDysaV0kLRx@@=;<2$a2qYT+0cZhLW;10N%QE8H9>H6QfVjO+hRZ zYxz05ibL5!8Hox6)N6)c5Le(omWmDtz;w_+)s+|2!WAP8od5DhA=`*))70p|EE^S(1ZaDOS47C3l@oq*zRR$?h=q2!u^{y10gKTH# zSEmuPxL$OxcprSE^BJ)&rn)MVlpx9(N;q%3odrnafj8Yd!5a^xyfD|r!_IXA*WJmR zIDicQyye#HQr!Pk`$P_=AM9>h znJyyz1mdFMp|F>>#z9VTGf3po!(bC}>0I7b+&CP7%cRskrV;`5DUHzN0gJo17lPfh zDzNIc9diznZ^6YvxUihV&~X;p8o@5wL6HtFhUW}b@PU0R!-maF zd3nME7y~{wc^;_JIPTOdtn)m&D5Qr>Vdp;4c|LjnAE6ID(|4V3rtOw%Azf6kCrhLn!t%R(8iyQ(6qE z@durug+3NMLdBQ!t!BX?Gp-XiWu3nx^kBK?@(j z;&#EHSJcz;huu2y!eoHFwl%Gjuq_!htnQnvK^1KXjW?r_;NPYNO3ubgZTuyu=zF%S zjUAQfA{fV1v6=T?(0f_yhm!t)u5)a!<~a>qXv=GL@68lv=%A!cIo%25z`^wh5w59J*LEJ<*ItP(gb*c$Kg^MQH6GgLcmCjBKqK`7SRUU;?P!Gy^!go) zmFzijc{@@wYiE?tHmkewHHn<8ieFX9=1D?zYoA6OJ$W?K%)FT#6HiZW&4!Zrm37(K z3B0hb@cgg@4Jera!K66d4iR|*k$a`Zi%+AOc)Ko3<A!C^5Oqu)tMjDshfae;Y&B|Eq)MJN>Vg(Fa0g%E`Oyl?&GYCitq zU>WqASX1}aM-yE#&lOmoUuMe-u(+fSFf!kH{2XOX1OGzJwEdQYeTe^Y*5}@MOOW72 zx}*f96r(m1+w2=jK~EfkItQr%W-Cn&(*yWvgCB*NCkOD8U_m`GS2dd_VAnK`HXiOt z2Mqv67Lyx+33T-!jT3`ar*h3O4Q4=B2(bNqH*JYau_%Hvbsc836DZ!@*r6doR&wUa(IUc%X`ZT&tlI^$FT8P zY}U;77#47y^y})cMiwI_i#4@Q`41zth@3sueP$;<#qG>>6sS`Zn};ZIanz)yMk?^~ z|2dhd2B|~lF4op!)`;9awR^W6&nL?*F(oxOC&xd~uK?&w)+Jz5nFZ@GiZ0@sf2GMqI4ug&!mmD4X7Hl5@e7hUt z(IU3o!RwNp{U8Ya#n^Sb_96n+tm}jeis9nD33{g;a#3EieNAl$YB7wlkO~ZH{GR6e zKLyqTA)s3m0)WGUihtTq&?tZ4u$@cJQ}^;SqL>~R2tmAnge-ohxM+Y+j;44tJNrvw zfT$Tm(}mty{F;*Ttzk;*o3a~{t>(D$*jB(L_GUA5x1&< zms7mA?gxM{LL#GKhdSFiisM43KUhy zNm=-8lEIiq!e^04e~n!HyLR+f2y5!5O-DKkI;Go*E6whZCb^vMsC(M<#S$^IdaC)T z8G-bY)BtLVcN-nGc?0JnBdljbuOv8sE<0Shq--ecgK5n_gXzIEJ)T~5n4xu-|C}4vr{dVR6v~Knm0kEG`!E7Ir7S*mjAsn z`jQ<4j`p+u7>zv#)6w{I@tm6o;PqfK-2Q{9ciE0Pe{=fnW?$a&i@jz(jv=tWre3JD zUzj4t$jkgsS7WnUiX4!tEl0ndyRw!f&hB3pZ;af)7h0Mp&K_JIFJ-fLRn8fd+c3dy z9i{~H?)oNXpU+P~7_Yy+Q&^ddxmZwT5{O)RQnm3vw~U}6{ZuHuZI>^o&<({Zm734d zVWXQkV5Lb8AjSaf23T3pRb^^&b5vJd^BV273esy(lDZ!6;wP&7`yKeh!`Gud2&^D9 z|E@z6tldMQ5oO2}*t#)k1ELXK6hbHV+pq%JaXJK8!PyrH@<~>8Y1_6j-F{n04x=V- z!t=~|1Tq1)?iSLPRh{AwGHqJ1%{1xj#k!1}@$Puf?p~`ynTd#f%qY88z}DE1oL=4w z*k^w8pQ~F~b&Hm6;nFR-H;s@p0ltfhsnU{buK_?YH zgw0^CFdQ4?<3q?NP#B2Hc3$fF7lQC!8W@aIZrK8i_tz+cFO_4Ra?At1o%Z*LMQ`3C z(GJd7fwHZn-)#fcfkzO`|0jD`jUx#u{f2c0m_vCg#u;aaM4dQ0ByJR^&ku3<4w{A8 ze{=r(zSilcF8R(r)3qUu&x%#zy!qdlICt|jr-?I(D-dV$uR@%8XC)dpUw%v!yfvrk+Qhy1xKoKIbPLVaRoxJFDpz7|{$- zO=NgmlC1aA`9JV%K|;bRPS%1mBdN~Vr*25v^c*`;rI|WFUbx1Bj>uQLl5xcqS6p$$ zwM)zTkglkP^Er3u{8aiw+OZARQ)1o`rk{4}e~JpE1<|D|40$D|%;-U^lV&J{rL23& zj#E{@=)&_L>e<5}u_^097}7$dbd&oG0oJQ)XL{7U7E3oBod+520U1qiwCE2hlUA0j zDIJ)Ie3(Gb*sF6uEV`swk>A1ImSc|ep(Zm91pyDEg`>Ie?T9jPlOS9m)Kgb{&YcJh zui!V+c`oQ?;4a5{i=myA%0P?RuCK3|8Gyv|LG{k4$gJ7-~bhbE2e5)zx}jae}QOp zaE1rUsi(mRn+aq+c~~`W0jMH4<_vk)^Fu@ex~P1e)LY%HFhcYvAKMW?i&GN>;*;v~ z)Rb=*D=q_V!)6yf-kK}vNV3AR(BD?UVHCbx=*wx-#-z2o>J{&1l~juuk912tcxG)c^}1=y$wN|R-ARVqBE$8Q;dUXal6XqNH!YY= zZ5AD6I>EtgfV*~Oo$o3Q5%Vr#r6OXV-KgH?>C%Nvp_~hOpYdZ5QaK3lRRg-5jwY=t z2i`JR5_IYU%U)qND+D)Od>v3_Gb!?a$1XZJ#LBHP*~KfCsCanF@|oI-1HtAHxsn40`P|fahb2XIO9Qf!$l`5_ z^3WkEQ}c5W403e09`AfA2C#}t+e$X8p&8F~64}u>Gl*O2{3?m4-MU-lrtp?vIj5a2 ze%((c5z{rKDq#y~1N`{#)P$|(HP?HjxVhEg6A4`(e5&T4A&=;y;x*WaZT!+V3N<-8 zZMwcJWkl?2>-Fc1)yv7(Jh(UA+!vr5H}PFCxz@O6Xv3NJ3(8yLQh)Ae#l!d9d9f=je|N zbvmj3x*{`%w(C+{2rQX-&>a+w_w4R{ErP%QgC;m4bNQTD<}IjC%wIu{zLw;tSlx~LuR+}d ze?2jWycSF^&E$YCjI@b5(P3%mU)pto)9A`Ke)~<=rs%c}{jJf%sB#~kuYo^w5Mq!Y z&P!TWgn}&12h}3^)!Ca%g{YhHo=$H@Gr0^9i#EM<2hvo%c%{3Fm@gi0T4n0;ap2bv z9IUHVyhp&~*WbJb$JDwd0yv}ilJ7jf+>exVUwOBXk)sn$8Z6yJ{q^F8c$$x;`CA6U zxDEb!TYRs@w-vC<^?6ZTOo-#X5a;m5{~2v?js<>5lHYas78R3wz@w15-#f=I0$}a` zR<)Lg6Pp?4!@@HyutJ1>I#%aYkPmLcFpHaEepJCa_KFI${s|RN+mB}QkP9~5G-U(2 zNMf7BXt^8Ce_3$6u1u`rJUuvH={m)E^mS{ESUbNXrYpV3m)X)5C|sfDt?gjJ)*CCN zp9E2B-E2`=a&cwd>DerI=}An$wfMrMs#lTHL=o3=V(lZ76X#y#iTrdj@HGSmf_76h zVta4q^u@Dy11Ewe`iR!t1NQK9xnX8sOG%dXQ`T+(ioS;xK|6p^UMDlD%49Gl)$Du{ zq@K!??L$%G-mK>I5e3Wt;wxo8)M$3ySG#2K@O!eOk! z9X$O)>zS)zFCFtmFMLrDAe3d5;x?ZVeoVQJ5^S#5rfO^Do)F8~#`nq0aF38PL&zy1 zsChTx18XG&4CzF*?kb{(iRkcaC!l(2 z_6hcXL!CA!u5z1t_3agZwLFEyY{$!CvXKraE3|FDg_TY;+dA;&T%dJVTDwaU@$Puf z?%u*CYXyzVZIjAH5K=DGnvL(LS9{PI%QS*(RTMFIi}(z17v! za&1UsdQQuniC;%zN`EP}h-_Mr>}JMijLrKbx&@(wvQ;3$Kz+bpWR@mK6jrtwSj|0< z$z^w&4D^lY<@jJqnUoYnDfmKVcFW~rvF7n{NP7u`TKCNh+0^PtdAZh#msh^*DyfD&O#fWT z=17%j_`5l%<|Tt~Rce=BurIn=OL+@WW#DcZ`b0E7-|8T9E|IuAqorAj|3N^XMx zdb`~J&r&d}i#XaY#$#kWR)xoCfJJHk4j+Ni6ry$h@9F!}XuL337 z=h>-2JMh<8Y>DyCrnX?`@77=^tUcJ^{id9QZ3+!~4gU|5NA7+%o(=isdzo+G*WPDs z?|zGJs ze8IanZLyyA?lEa4I4mcp0ePd9UAE^8m$y*<8E6$u#&*Lz^tT3MOc|@zIIG4hH=(FZ zEY&I1`k~eKK!rN>#;65Qj{!9S>e@hU2Gj@45~p%>Rql_FS+y@J2TahHwu{d|oDvPj zrorVAbQNL3abY{EV$h?@?~WMR^!_xy%A?0AkK(^1TL3GZ-R!1{sYlg-9$k%kjCnm) zlOAVVk5{WF6oZ~vTEkR3|Jisot|nQBLnWY7c8oZ{7s zvlS@s9-q_ZXHWL#KmNe?C_H%9qCQEY=ntnpPVxz6k}NiN^|*wz<8wXtwuGP6$P)iw zPfe(nlb1=v$+)$X;u6qLh0CS1aVf`zb1J1VEMFqmJ4M3czE92@nF2vH zYtds&>#^$eIBQzGiWEnUKnP2o!pok2|5%c5PduB_D4eQ?%Ws<2G^*bl-7&^gxv>>% zT(ucr;U?6&iOoExR(qD`|JQN3dL72p0_r(X6R2xKZJ@pbOMtn|Y~YwecGL1*C)Zb4 z`r>rI8y1eY9-bSolRG`w0B13Vxx=>b^uL=@U|sf1h^6ZVdp1@00dtS?R* zd4}^v{e(N|C<#oM|6ry*dO(KF*C0sd{M@nb$5dOuLZF_b|Lj}|cEf$s-+vi8bzY_m z4;)R?{>DBDH&4&Jg<@w83m)m|_vrg=9cEu%v&}sN@9gn^h-E;)weJfWx-UiVU(kOM zhOflvJurT`O&m54kOh_xi371xIDoG|%RfvbANob75&9(xI&gD@0PPH;nWa<}ZimOU`-#2xHNee{d3!Su^dJS({WfQMyCdP#+ruquhsC>lTzGzjdhze7 zm;SeU`Tz12Q&)Y>?d#yi|M;d&x48Xm?)-PWf4?JsT*xJo`g$?=oAG(+FTd=NzW#+A zAQ^Raa4mn-^}&toyPNi_x3Y)W`R$y>9^YY{$n?Yi<*(=MLVf?ltLDI8n=hcAGKQM@ z*?0wpVt5g0Lez+FMppy)qb~e?-k@mIRfft`@2`HS1n^8{!$)`ypWvDMYn-8+Wz%m%~HI6}p3B@xfjfVrT8ob!~XPpxon1uI^ zVv@MlNI0n`RckSWe#f!aR5UESdM~dbpJ=>t0UvT)X@>)xiF_-0GW04n{`Nn{PZiQh7!Zq6qlm7e(M_uWDM&zz=u?vm^>1 zMM$T2PhlUrWW;u#QKjeAh+#R*3mQ?9wTez}Fq+I3tIh6ky4*+u3?iH$DVkw9UJxZ& zQMEm%nH;lQrlyo!O)c4dTFh@{)Auq?Oi->ur7G2G)T&djL8B(kT7Y5eL|}FF$H^<8 z1OjG9IU@a~SK+$zx2reTjD({xaicVTHp`%PRZZLlSKmKs{(k+)t^CdUO`YSw;f|-1 z@rQ}UH5}dXcEbH1-uCyKCQH$rtMB6Y!|f}ox#MS9egB#RElZ@+FwmyD%EMxcHJ1dE z;j`CO*Y@3j`#nYR?-eNa^y=e%w{)O5OarpNFG9Dc7iGNf=IpdoE!DAJg^kzhnQZ41 z3p1>`$How{D7Nt*JxkCQtxj*Fjb@ag9bBEML z@&gTPL&t5jJrTbMK)zuf=(iqesi(Nl^KX@BMJJS8lh$4; z(=YThC_mcEo7ty79vlFMlN0%$jEI#Ha~_*hwF)SqWZ)DXQcywBoV@sG_X$t&T!e{c zAtl81?}OO2i)rjlBzIT03t6!e6>$|o1c`SdTr7(fF)2QaM>SuH7Ca~35(#nE{{RuU z{7ZO=iMWfOD2b1{fEr9iLtPp**olNXz6Lm_&~SqvYv?BGcps?)*?R`&yQjXPM}}sh1ER>X&$=CC6^zdFV|VpJ-&b z^ploN?=5OV0z{*_7_p2B)O)Yh9mr!oOo8VAVrf8h-8hJNb3H1xnj3+zoe{ zP>q6KnKxzsb0bYYOv~5So6gxGA5U4w$I4#Bnz0L?*XTB-G(IRViGp6b08H)tLj~3P zwhMB0c+AOwA?Gz#ffC?$KIj>oY=MYrKvx9l#1H%Q>(qdJZ9_W}n)!qj7e+%~_!~=XK?;#+qHi8`==$OP$siVWqkd z+Q~5h^9-oA#^=YsI)MdH94t}s@Hj)@v@c{EsODN?S8{0x$%!uYwY{hX=%I>u>`;Ld zVPc~cfn@oOS}2je zDxhQ!@Bu;#CCJhOJCy*P3jRM$DZGUKq0305!JRF!U_W7V$*z3hr{^~i0;0(D z^==toB^gb?@qtok8A3$VBb@eiro}wLLq_&6dSy)GSno{0i`W*)H_sw9550%s!$2%2 zN>TifTEmYQpR1{Y8BmSLfvp42c+h%F%cCr&0O<2N_(LP;3yaTUJ^cSu*p9kE5fdq| znid-PSuQHNR^s*rDRr&$tm3LQ!1Y2x1f9lgIpy)B6dBpWFw7#L?4t|73)mLPH_IY5 zSG|YfLqIGiN>Tif^lCBhGOhw3_1d$oV}b)cmV{o-6pJ*^EV*ZsLQ9FmK?y*RwCAA_ob&>FFvsKKKP&uyy#) zb3&9Qz?y)(6m;?+@V}oyK;cd;tg~t zyR$vL`_49E2MfW zPg;6=72_7!(>3nk8y<9K`={-RvRAQgd*5JvzK6cyA73M`8!d*qywx187AqgETe638 z^p!U!IUtMo>{~Y!y6ma@(ZfRl@L{7?wc66ML2tu`b#wn5-wIfy*M?S$*wVYH{9hHsy4JDvnE13l>ko~dHuPRRYL#PZpWDUEwRJ0b`hT1+#dmH`X>3`P zl±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 I_Drt;0L44Vc>n+a literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-ExtraLight.woff2 b/xhiveframework/public/css/fonts/inter/Inter-ExtraLight.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1d77ae8d0414b1672f29b617d179fddc66273b70 GIT binary patch literal 110176 zcmV)IK)kc61ONa400000000000000000000 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�*Y6VoCVmKuo?ElHB(EH<{~ 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

      5RqWHD5FkF8B{-?>?ICNjHf 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-ExtraLightItalic.woff2 b/xhiveframework/public/css/fonts/inter/Inter-ExtraLightItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8c6849209dccd480d11ee355c72b8e7a60ceb7ed GIT binary patch literal 116296 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ඝ%DKW#1YSnr2^$i0Hqq$^lachyB3hwkh3 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�yyV1_14g%832kC<$nS)4* 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�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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-Light.woff2 b/xhiveframework/public/css/fonts/inter/Inter-Light.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dbe61437a17faaaccc11a626f3655c3e497b61a1 GIT binary patch literal 109992 zcmV)NK)1hlPew8T0RR910j;P24FCWD1n}el0j)~_1ONa400000000000000000000 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

      Z5~8)F8~!)7G7rG9Eh(90U_ajf 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 z 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

      $wv4| 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-Medium.woff2 b/xhiveframework/public/css/fonts/inter/Inter-Medium.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0fd2ee7370b34dd5628eac3653b541ce0508f154 GIT binary patch literal 111380 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

      PpnM7rmcaRdv`(TIH`Wo&Af(jeZ4x_MBq+ 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 Go#c)i* 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-Thin.woff2 b/xhiveframework/public/css/fonts/inter/Inter-Thin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..07909608cd4a42ee15873c38c4b04233f2b9852c GIT binary patch literal 106620 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

      n3v*HjD06 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 BSNH$` literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-ThinItalic.woff2 b/xhiveframework/public/css/fonts/inter/Inter-ThinItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a7bf2138010ed291c3b6cf2abc21a3f704a9593d GIT binary patch literal 113384 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

      j2*lzMF$7|q6-@wZpQL{bn=0F#vb59BA}K%X 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� z(EAIbF^o6Wlxv=z?okl62+_8io&Re^J9g}<5Pb&75EM}V!e9_r4&aonAHwL);U|}G z`HsUNM01|)`BM4$9LvDGqB9dqU<74Ivcn8tf*hMk;sEb>SQuv4xtgkDSKsz{_h|%j zej^hrL9;xGo@U&sqL>R!qbV`w@p3JQ*NHg-?8L}syLb29WzWV^J{Na5v0_xz1n9U> z9;4-10iWi}N)HoB%y0=-cLW?vbD5#ix1l<$>{0(G;_)OhRSu}r@LoivVv&2vT$7R& zbVZ(4Z#J6<<-PtM9o`^ZlV@Y-j{**Fy3Dq}|Mw;28mR62R<3%bGf>h45-WcIR?6Bs z6-<=N#U=q5>#cnb{@Ywm9MOee1*p-C($I8<-`i1ZqhLmMWRJ@A!JjO&XC0Z**qO2) zK85O+G)LgzqNn_$_F;_kie}jalH@ zm+NAu(1?3|p4f4C0K^@A94I&n`QY8rT82SKC=T!TwZ>0CtiUOQ^4$Qx%rOatTDRQB z(EAM5mc=)D4$H%}nVULf4BS=2e4DEAaM%xRj}a`b?fHl~K~$O-nqE+8stbz42ud>9 z4Brl{61#;6rFJO_MPN7t1-;goE8BR;0;oitn^;DY(R1*AwCW5uJ@e_GMZRNL)xWDOHVnCKPNz;*daIv#YGY#sr0G#-;D z*7MrrI*#{u%6iyE<)5`2#{CB`#HpE~GFW9l&N&E50R@!wzr!;fV}>IIR4cv84=>qv zhYob|8tHE};u;=*@m%tvCIx}+tEcqeE6gqKFpXkVt3=Yz%h23L7z zuPk!_Fk6oW-&kt>B+NZtn0g_#+oVAl`GTd6Zhr_A4}c0w5QCW1>twI4grj6K=kxhA zI_KONA!cOqGUSwjRbxtj=_w}%V-x>?$ZjF11hQ~VLUl6Ou00ZJ2D~J}d}bL~1>LrG zAsM}Dvs+Y`aPU|pq+|~FDwl6jtSJAWgUJ8d$H_${#h-0IjPpv&qF0uVfipCkr6+0@ z*_PvNY|2q8o79r;!A-3v-?RyRma`9LukGBaU2DDCrSa%$XMCFxgeoRhG+ssly)ZEr zBZzme@Ax#+jPOT>uADP5g7#s`4h4L3NmoWvkK|n?o!(?mH8_74y+uAZASH{go?k!btNHc!~ zEYf>#R;!hf`;cuv9l91IhX^vsMtKr%bc%{1A{cq_8^+^x`PjN+SEWl(tC3ZFVEk5qj^Zxsi|m1Tts8AV0uHO z`5n*w(Bd--%B5{ab{D?jY#Q-)L-N6%)GDkmPZzOCJP)-MP1m*h^>h~@S}3%-Bb|1# z2Qxr_XO4DYdx|2$!#bmO!-TYo{d6O3|5PSN%;4J<-`F-vQryKZ$2PY8IGcMnaDYi_ z4W4MNm)3s$|Et?MbA_0kXA7j)DUm#&MigN5K|z@~e6mrd!5BUk!aFwe>hWr4L5j1~ zl8gBHLXkYDPH3yOvA!s3;RMGtnyY3($RKfnOaEfzl2Iwir))F?r7M!TfspO7G6V`^ zZ7k7ivTst2hZPhC38P7#wLQlx6jmG0oi?5!!P$$LHDv1RynxYQZ4k`P@dHGKruVtE zsL-1DIsIs2QEpMQhE;M}5#hfW9$`8tDlzb1DnZ`DUx7n*pC@@Vs#%NX#-NK}FnqC&v|Y)TtzU~r~6Kx9LYqsI_N#}0#e)o)C%(>+G#FyWE(vAt^Xo?c4A zqsVGoH5XG>oBNndv-0y7#gzE6YQR)t+O<0Jvgsx%T1 zU1(n3&S~tC%&eNW@X|V^G@Tn2>Ej$NtptH?b}$l@p@{zZ&OMd5ZzPJ|B-wN`^kd9vE$K@9`K;8M-B`{Tv{8Q|?c!JH$Y`4b z@kYV!`w5QCj^gm)(U)B~jz_C4<154a%lptEM=}Bts7-@u(0qYW*eOMzLOEj|>4pgv zvJEDjet|TtF}a7P^B0hQasWh{(k-HQf3UKYNi9RV_-P-6d(hqa2ln^V4VBt(sy&y36YTq z;>>3~^mB)9L#M(ti&2Jh#Kf=lrD5jwX9parAV7-@3Ihf++)9k;6u_(Q^XP+EE!tQG znh*$6UV36IF?%Ck4kkN8c)a3#b+6GWq+yZ3mqjL@V1T0~zExi9=99LkBv_}#tU;TwXvMs!K{yQH6kTSk~H(9 zFihvUtSIw@d+5H&MywtT9GrWKal#@j=l~&tj%rPWXZLOLA?`dZjzZ+W63O1_k2F|vG~-}%JyRQ)NzoKrSHbY6XbCBXUW!KFYbh96W=jEWn3 zj(NTsI1-v8V+0~)DMs8nuPLKp;+V(>0w-N^&TxWDIunX{?H@f+W^xXb!0r=^{eY4CzZRAhE4aIbm*Yc`eHUJ5!r z1G%Tv@-KX+RVzz0S(k~UcB=65RnI6ZGleHgo{gZF2E&q!&kPB%-I}V;-u7IZvQ9)} zGnFgCLfia@k_?iZ0zA76pjB<|LO6l#Gi}F==i^sQ?nbtoGN@ij)eSCm^ zB|Fvv+%@+2z$Y%TY4Jj_-GkmIxkvFRT4lO;D$enQ{qvA=RyviVw2G`!6czq*vy*MW zHurha|7`b$9iBhGr)$mDU+g~yUhn!XcwSHnX)1swK0S%sMpkluO zI$uosK)F%{Ta{gL6xz#c0I1i2_O2>dQ<9|dnGpI6D=7k7x@Z`X#5D}EnXGGD9xwJDHlZ z!*jQnk{d3);S^e55>{@z&sYPix=GOxnVlkl9=4~mg*)?z*#Rv4lbphReqmhP%jGB< z-?}SRei1na$~S*3ilWb25e?tf4{0n0HS~PvZEKwa{ou^PtusXtgmgYdh8m6aoMTx@ zGfe@j%Cro5?v_zp@39yk^vZ1&=4UiViqHW@XYkGSVjf7Zrty)|9>KmLdMd2AJT1py zb&*~GJ)Gqk^&C!zGm`;S^==co69MfmEmc<3>eE%`ro#i@dDu((+wG!&7_-@`3fl~@ z#&@;bI@)w9aTHj!ED6~!TTN^_l@n=@m9J@V6NZ_-RfW!0`Xj(M1)}#Ak&Oe^#d3&e zasipAcI?={V%rN-KY`rwOopTh^v5_PKQ9gGz(O-U;{efj)fiP)m-9e@4Tr*U$+*H-pd=739fxPXX}R zSt4mCJw|_kMMq*@LvTxZ0}+WGL=w`#z(sVoCf|9}U#+@s4!dg{s;$Xy%Fh30zT0^3 zy_5RXs@gHCFmERI8!B{!MmEN`Gha9b*tae<|Nm2%`9v9ZJ-q-a7HC^E#xL4L@VDu@ zsi-$iXP30??{<5t!&7%oP$~6o@Iw-1fOts$5}62Nl5P3HfcR z9*u68>~0EZJF(}uI&R*A*|RV=S6$n>)QsbKNk$iy<0~@sE6B-qHk9scvaa*?I5ilB zE)(3Fb6u-_B$uNq9jQ}WsY)$B%rMwOXIfb)g;{T!u1~|}FlPz0 zUYF8V(J{PxEyh)DCOb)69x$s)FkVmD~p8Y-Bh8vXPD9!vVd$_`d))m{d zHP6H-2m5@ALmA`o+~Oy{dB z#3~nUGVXe-N$OVd85fflReg$NZE~8vjq{yD6k9wGf=_0c2fntxPEWg3OywTyMLG#B z8NoJVla`((uL5ZRdxK`Wyz#n9#n#@FomQt7ZO%pG$)Jt&=QueKyf2eUr`^<fLKH2Um!#yQUfQW18FRL6K$;uc3`vttx`WX>&dj z@p~a ziIhTw_7a$cX}V)pDjTf!91BLlTh+}tsjP)XbUQvYibWf(=8tv5v4z82K+11TNh7C; zEL|$rI>W2I-E3t*Iqo8YpH5BNx&orWMVnixrq`101xiw=@ujp?cpCAGt>DNqJ#%KJItC>fAs|=d!O-!Xe-WXl$G-2H6>E*bg|JfhAz2AXrhQ(v*_q z5t`q0=B4Y5IE3>l7o1v_P=AiZ6jamQT346P^T@54@5Q~D4T|$L>qab>0n^klUX!T2 z4Ds+qFZqwk1@{3RffU#jNQa*87BdADUiU7rF>AH7oMtqNpp7hI}GW#&*yBUW;2`ZW762_waE zy4Iaz+$g+Wj)DnhWWt2dE~q@M$fZLm7+RuXAvmWQi;}qiDBuGFEKe&h-()HD9vv4! z%VK}}ZfDSZQ|sSHAsSye2-KC)S$|ckTusf@&*bcKfWKObW{*Gv^b7byV1e2@ zbD?Q-&L=9`|{PrTsme1hf2d15Jzr8 zG&W=J21ZSqfn*ywKmj`OdIk3}Yi{gC7kKx=n4XRe-D(cUOsEv;nmtZ>v`8Uh89@NM zxg<2Erp-$WK0F)$mzom}k58lK@>5rqaKb-ygvW$gnFZA|UBMMSBgL9OG4tGsd`U~% z`4fd6p6ujiz%VQII$mQCv*o}I7A0=Rg2ZXL0ymHvp65T`0)P8*Gzm-C@48Z?AiZqH zWAtR**n+-I2~G4t=n8(8wltwj-Sn)Z_X~< zy=D@X!%;f;obEC-#****4^pxAyjp?W-WTFM>`eMBcBj+&)qbKb9|Uu*St$f=-@|bw z3S7ib#O_O8k5SqW2cc1(hZhw|?z-6Ln|r_KL(N=iCu~}NJzEcwmJV*8!CGf6tKCme z&QpD}OWZGBGS>=)OZoCTmbKMbC<&<_9W*%ooq<4HYs*Pj^X2$YgH?evG$+SV!u;+8`J;311`_=sa+CLIizRrwyz~%@#NR zeDU7rTRvZ$I(;y5AQjRu;$$gaTXZn;=vST5M_W$on^TD zUbgqXF0R$5fBBbhlloQb*D2-M%k#0#&QY(f=lW|efAOX>l7*4}v9*H$_RD0xr@^>3 z*}U8Q@`v%?Z-dzbh-KlP?L2>hX9-@nMO4c)bB1-G?O-JIK}$`nwQtPJ8yO4*T~3AL z5C&o|;?*#tgNhLB}X_%03~i#lFC2x3D|OKle*=wjD23T-9b)?XR;-ec_X- z2!vPB!*aRefj&096Dt&~f6PTA^el&F`pi(guWNMr)Ss`Z#M4f=y%*_%0zB*gAi*gY zHfZB}(&8*dVah8nuiml6tqOAJopaVPkb<=1b`rTuc)|i%?(H>-8{6H1V2$|+7KVEvyrV;S zNse9&Ga)tZVj-B7VE@3$C6^?jRZ)8_1j>msRR_vE&+;-}GlZgE!MIV6_1g!xQ)}AzanAkC6^TaV&|5m; z4(QzYZLYYT$c<(^W&+P(D%f7|BC5rWK zt1|?v^|~(AG;Qb!t9g)*@0c{> z7kNf_i9>2!D~A>`@@_#0>MJ&V5}oXgTBTCFQk|*leVK%Yvh3Pp6eHeg?%PqbK-4e6 zlkx{-6u=wanoQ^a@%`)t!2tk_U;f=S3Ay@Ak$Wzj<{zMNphOn7#|zR9G+LdiEJKH@ zdP`OekAwuPnuu&L5xTh@%jIfhVy)Pz<#K^PM|!u)g+dvPdptDT;}2x=;+;S=^69E4 zedWuu-q0`lhg5xVei=J0Ro&S6KW;cMPkvQ;Tq1)7JA;%yGe;h>jdSMYw#IGV8o041 z#C+Hp7EJTOPUX;}co~(tdFa{fc!g~?N>5;#6mV!{>Hmi0OwDzI7uWE8`t6_A)b;YG ziqiV@oV-t`6tz zU$33-<82BEIpL0>RBVo|dM7T7v~d~oi^TN#d!V}jx%~HPDoLI7dNnt_^T=EO5~6o& zI;lRsa%-gnKZ-C0t{zHYJu7%1!D5z#`s9`85+@?sN&@k)`8+@)-k=v)CY5ag60D;V z)0DT#G^3^3ji?+WYb5fqCXX3moN|IEUYM9yLdi;pcG88ze%`ZBH_0s37;3*>s()6z zfl+NJzBtsqB%GXo;RN;vAl4@2%%sIS^54@BBpfm%yL_gt<+r~4Sprz1$?x4+uTBM( zZ3~I)e=_F+S0W$ih2plI-N(Qjfi`^M9kjy-gGlXF=v!A%6iNN1AM6h?x6c9d}YP75)yt$J*1c@GN{c+F4$UI|6|F- z4wh2J&@GQDvPGs4QOX!I$K){@{J}e%V9U| zySdTD6S^^6Fx-v0^ozTnREPEo4Zh{xT)1b|D7Fs-@!-+!eXjh?VyMA`NbY+La;?k# zWmUcJ-ikSmr_~JFOm9A}_iX9U8dl^6^)!*g!Gd7mbQbfIhbhKkd3?|p>`s6Rq=8}0 z5MrRBx83wTkQ4&D@8h_NcKtVWgN|3)_d%w2#C4r;!_i#(678{yZv_LSm+^}=vI=of zOa*H!9XEC^Y$vhnJ5!`oADq zXzZUe*Na{9WnWYGFRr$5#UoQxFqY*PMX6YC*I`DZ7KC>zVOnqrZpjZsA0j%}+jTd~ zhTf^)A_r_D@mZGJJ#K)Sxm9sA#p!XxpYU|dQ}e!$##~JMTDHKosBXHR>koj-vRCA4HGS_iru=c-T(=k9@j+t0zRAI}o0w z#tGoME#&scqAwqI**u2P<-W;qhGfMa{Koirj%Cw6){Ut{rO9RDl69h1>1_J1g%{6k zh_<>76^y9`{Us8PW}wfn?cbylfmG#WTaAPo2lJ{gqE4n7P$sUg?XfJ9+i={ zhEb{NzS%6&YfxQ$Ps@?70jv}9yPfu6iAzGvl^eO{ZGVGidlXMvn zs&7$w)Tc!iV&7P#<%I!VSLQZ(^%C8!E)iLuef5)NdL;bX0~-}&e9(Q|S3J2ewzrsGWJaWyOuM1O)q`#a9d z@t-KzxcYL~U{79{BKN@C-?S7HUhs#G->*l*M|qW@4Ubj5vNf0`hHH-Mm5GjH9-ARc zL*^`PAsIH}Z6=MjxNmJ8O|10J1PSC*J1N(CK?RSPKn2xrs`sA7P2(aR(h2bg^8|Xm zEij+P$@*|gCdU2|vHJA>TXQ#X*(za##|`v!TwOfG3$6?{n^o%bT3=`KGeoXs08c&MHku-n6amrX6pOPTZ&kzK6v7iytkX4e#~fM*&7O@@33}%b4|~1jW{BBt4~M^f&VONwLW%WaZJP^)8KT zb+NE-b!!arxjik2VgDHT^M!8+JhweE*g$hYP`J$M2djWr9#acN+H=tB&NLl@Axt51 z|Jw)Cu2fwIi5FE0&o;n=lvM<8M zR$bSrcP=eC6H=MWdW@z?#H1FM1FcDxdNjTM0z*9ykFDkxb%tL0P@)r2?=7%ky&o>) zNe=BiiK*90FwhJ@pWDPCBtSxG$vl@a&{DG3lF6RA4iTl8PP@SQK=>#n%6ve0w=>C9 zO0P%Ax~CFGp#LZc&{Rb$1#PlygWHo}(U!0wz2mX5i6n>W@{^BF=ET7xYz1w+CFGCo&haQ6@6@MF{cDuqI&R;ZVkA(DEN z34)%D>iW)ZT-^q3%|;@fUOD_E?+H-)VHzyJ11eDY*0mlR9~0$in3u`*%kq*#1 zaG<+wm?T8(GL&O`Cn+QmzcPk5zo7N7u3Q-FY-s4{o|)bx84=41WvCuW$^jCxES&q{ zU{44A$JmaJ<-IcN&{Rx=r798o3T1F>BeqoL_!xDO2<`1ODplik)9Exek?KUODU|KD zE>aGt(D7-P%FPzN-S5wgE(`^o2?JG%^d}O6D$fCdO7TL&g(@lp6!8B50n-8;C-=NE6e&WCj22{D=^?K&6vx#H+2i#skUgit%-*0+8t=Bcb-&@#cS>Ga~aHE>4 zPsanqzo|lakmF8s)}|f}9vu3#dB?(LOTy9I?aU5X8-SZEbr!W+Woc{!BaHvYqjB$s za#oSrg6mZwjX{M$_shjD^Ub)`0UEPD!EbgYM1z<%F;)d2qP?dIXkypRoh6(K3D%dE z3ni6cgi^vxkU=qN=je&wma!(qE;j?L!<7n8bW z284i_a9B__rH4qelS3MbT`T{=1i}tl33*Q`1%vGf{mj`g9X?HFZ20y20N`=lAR3x^ zk@-y+uEO~!9aC^t!nk56eqOh(5cW|aTo<)0P0`FWSv>PSJRJ$D1Hub%Ho!7aMD);4 zQV)3fb1M25b@bdrhZj^N(p5cBBB{KF)`! z$9VCOGdw~jPSd^!6@9C*U;{$v$Pe&PAc*m>zEWK))*Hn_wFX4H^~j(Ei4f*wmHKkK zm*ZoLyOX2gwRT<~L%yek<)4;c-_q!L@(d-e*E4#=thjaenyx{Wn=wWQ$=A|M?D}u( z-sK0cbnBoZmDB_aSz#ECCVdeIih{S>q!m2N_6)^JTM8s4mc|+I_(8q7U7y>cb@XaH z2utRD(y(>G-QuqY;@5qQWk~ks%&@zBe!HA1TQ@98=*0I~V(SK1wrBAV1ViEFTy$SuUe4^GRv5{z8Or5h|6V$?QTtLw5NYMa-Y;6uGVTkH z*Nv}XXUr>>qsa&dhck=$i~KN0BJrd>5{D1qA5MA{k!-cHVFK{VMJH!VHs(P_L^gU* ziP^c=@fyfH>Tsi}Ue6?y;hHFWbZblffPqW`2dB>oUjcE<`leVv^sA%-Ttl-G2=7w} zxf6ZsMh?%i$gVXeE>6ExVb-c&T20DzOYqi!SMxzGnRdKOJhx^e?nAs+IF(juXvBCz zIk*j1Bt(e0B{YUDOqI*Bu>t`Q-i8wkXEqpov0N*Lg?VzBG;xs{^yz^n#XAtEU+ezp z+fOgW0xUFC2>b=D#Ijtwxn5T|k{yeosC#|UqOv?#g9`JOgq3S_u_eGM&8(kf+T zEnjGETrInS9n%u3JiJX4AcjCJ{2zE-^z_#U$~CjE4kgb|e2E%ltdf z)Q|gtMgrC{3I6De%ii59r@XA0AWP{DmLT1fXSQA%XH%FyX3Q8A$im=Y#~*OkSf5io z31msw^f>Ull`&AfEyvR&QB>^A|6g9PluB#4g zeirUDAU0x!4X?mw(g4SmWj~XYeIl@K$j+|rDzYp#wc5LP`MEAdY&4ppYW=mocq$Q( zhkdRW|M<}Ff1(|Z!3SS&{dRVcp`~5Wmizt7R8TvCC5T~IiXaDPi~~%6Ds=dRjspwu zmG=kD_nb4|>WI9TK-Zm%#a7Y=!?Gqsu--0vE43-5T(&ML#gt*m*7_Ko*(@;dk~r&< z-&4Z`$227=Xe3ET1x-ZW;|a8BI_3$0qV3k{d@$B`kZ}H~!^Z5rS@Xcz?-H%b7Rw^WJwT+6 z$H>fktB5CnuM;rZKXkNE#!(- z0g==ah6a?pa5$_&-1w^`bdn-8Mt|TALrfXf(;Ess_zocTP7~Av!b&*V-Uc--tX^ABLI<`r!s> zg^T)m5ANEGLS&ATKg1B`#ToJDV{Q)B=b|oSi)~S-X8eRnjNNKiKJ+bale)ks zoIai7-gMADxsko%H#dn{gEzC(C$UTPBiK<>JF&ib0ekBv<%0j-#HS@0C9}P*0@!u_ zCLa*0xQrGJ2#U5;`P!GUJd)8po0y+URrKZZApl$0oPzR2b;#+&JRG*A*hFuwJG z5}edgS+sq;VHoS~DdE3rY$vsDWul~p@?613>-2w+nvlf6_B~YjeGD7|qSRyUmh#6+ zDS!W2Ptr|#=34Cm0rarN6ABCLyfLyKs(|}}bV>$wYfDm_<*8da%g}4x)oDNi`~sks zi#&7C41m2hcu7^>^xz$4pt~%Dp|l>Yk<67ncM&Ol+qZU6Alj7@X!X|SWovU4BS4u7 zam010LW~)LeM}iyAxnhB)*J`b-`+v7B0Q3WIL!j~DV0tVBCRXDRf6D>W9m34Esc`u ze-6QVkkWc@WA=)K7?&p@efT#p^$r?X3*I3@6sgkXQA^dAENMN0Q2@E*p#~-w72=*# zCFFOOV>#iD(_ZgP1*#etlm@jX0g5wYR-1=Sa;{fO>(Z35HRunTE+|T6xLSsMVhR#- zgpxSMHS>1mGvs;e0Of~%gB@N3QTq~x_N+>+Ai@>TrWz^RJMAK`+UF7Kw<`4~pA%6ZqLcY+1x9F?6Fj~?Sm>}j<}wKzf{;Tuvi zmxVO77}y;+ZsJ}msmw1aq}ZjlE+=#EN(ssYkZR)VW;m!}yAOAOe;4DRxK5)G$uR49Ic^+NzAdk^fc$H8yDlwf%RBiNUbnB1)7CasMavp< zT$UAt$iA52igiiuisY0+iX_4wI?+YZh^51Yibsjf!L``amM0Zlfe9&>GSkG9&1Dr^ zo&^j+7avxhY z6{@4b%5};5tASn1FlsM3TiP=MLWpIB964WFF({KDe7VoOo2)5{B|=)-4rAb zr0l{D8!TGf&SF{+DjI64U=C6$YB~-fi5f6ft>L<SgU{=Lz8rlQ1>v1zeng8-otB&v=0kzpBH@yGt+)^|GoY9HlLmtFS8ZZrCfW_Izko%f#7S>E=P z=jIvABl8CS=Y{{j84Ys$X8t&)v4D_=L|igQ|1ih>xKSMN^kMX>xFNC!isTvD_0FY| zen5S3Vk`8OZgY(nKK9q%m8%HU`(b;pPj-n>?AY!0rpyS6`V8s^tkF1EaEiUz3?tEc z5|C1OhZDRDsW=Nn_D!k-38U_byUK`FWS=_P=eW`|T)H4o1w1Gj>O zbVO?iV`B8>htLA--Tuyk+Lzs%_2YR!KgoWozvc(sCiYGEue328Q8cXjb&=cJvZ7J) zdi?)JVAiPn&XVNj-~a5)&V2w>Ze=$m{`(ZO+>7!&pPA)tgJBpw6r6s0jC9TYBz5`h&Y4wqF0XYMZ?Sm+5sI(ziE9DdYx9P?cQxzy#HLCCpn;AgUB zgXy$zR77`N>;l4tC~Qm|UO)10CRg_Pf(wPk_+YWpB_Am-E?;qfao3$6&IX{OmaWf~ zs(G0&_4^N&hKF`!(znQ-okO;!Z;SQyK)y!+Kc04;J-&JUMmq8R8=oluw`&)2Lp^?1 z4b-s^l>Txvj-t_(t8VwPxaIL1Qa$_wjqS}VL6tpLv!#j^Q05dN&dVZ%2%>&$+WOcg zDNMn@4r5qgL4#}brHp83#L<0Q&y`dbpth)FsH?UuD88{gIcQ^pe5R#@L_cq((p^v> z44(z>p@v$%WLA=!{5JjN7xyp!XD}T9aSEuhhuREL^>bVboy+LQO>Jb<+&Wa#KhOMM zq1Wiw@?vR~+J(4N;(MOm%%`eH9PM1vB$|efzmdX-5 z&z5OYFBS!=G3eJ*nhB9gj6*{%dHA&4iynweb_yDjLVk~uL}Dk=YM;Q+H8ZdlngAwe zmMKC75qTMxP}3ybb~GpMxkRK&k2B?0e3guMu0)z&1i|zen3$pJkSML6ej(5SrulqU__hGhlVUa$-pq-Q7A@t5 zqgL&*Yc)rZ0t}y<8ai(VKh;|zj9SaERuCEvs7t(IzGv$=!h4uP@U1fuPRcL7*=f`} zPR~RMg0ggEDN8awwnwtDk`!$Jv_^w48E5KxYICNO`vk0Q>vYC$m(E~5tU7SMbAhLx z=f^=bj^sjw2o5n8p;Q!sBTGl4ye$&mIgR_!-5L6B3CI`%#?UZHIYfn|P=s+T){h1c zWZh#M%d*HPyBp5hYhGUaUK)ll?gM5X$CBngqqJ+K6q)H`c&{J; zFP)WNY(Sb>uyKg=RlZIlR0BGVV06?}H8@~29Lv+oc zc)>*)$swMdE7jR-t+6$WsYh5~{_xIo&1X2$_{ZOnC|n+e%psF|XhIH9{-5COW$KUL zg|5&s9Vr{vOZpm{5f=otY20#yp{WY-4%AxTsVya77^bu)L{>)!=>fy1yO~;S~hwXO*W z+m#VGA9*uMH)vLdl;oI}O=CV?31ELl*NmJ~+v|5;cVz574>W$@yYM;iz;B-@Je=uS z2_i{vKRgvki!w5mUa(A^D%f7T`X!%aw7q6ivh{r%=RHzlTefOhoGbURH_}HrL_c1; zk}v+c#M{Yrc)@b?{@1K$$CQ=tECBa+V^53~dtCN4 zcMh|0!}95PZRxj|U6M;}T2^I!$mf5sE!jc&gJIX3eE9?W2QfthAj0kbbn)tU9tU#Z zF{&8p#P+ixtujAJbT4(nQvZ_yPKzc@2ie>+4R}a#NbCT(?jKTS`hnhM4&K7f1&hoG zL80bHQn?No+dJ(a|A~hJ_zl4#mn_YVBVYUB-o4MAuu@<>UoRL;VXay}zko~P#|NK$ zeRd9c8IF0$k;Kg!n4Sk9mdh-IxQ5|@>9~f0FIpd$1z*%->RSOJykDQH*P1SaRk@Hw zbWRDd`So#MK7&p{NqbtIFK*w26V_4PuLl2AqUU0l#wmBx6a!~`1AG{FT0*(mq};Md zj43!^n>NlsI{6lVSOF!`3q}&Z0s{;&TfzduhsnSQCQ#(Zsa|^oogK?k2_4s&jwykf z(9mtlO`(pV0ZBNLj^(s5*4KSVHtk1hM_yl!EarcTS~RaG)9bm;;o2AVU+O!${h=&C zKMuU4TGRJ`RJ_IINW9ZC-^V;FaQr~`cJZ5U?fty4G=m3)%18RL#0tq($AJGd@ZdRHK-&<9Z=#gqjmlx9JAvjJ#lNh-ZZ< zj^wZ$qm-b!kmM*PC?Vg>MvNt~cG;p6hEeWadfig4yJC_vO4O_lLcoLgf$8Xc#9~BZ zbi}VFyvwWDvk7)>7le>2qXauP)TlLB9>Wq4>JATw6bJ@EzJ=qWbF_H8^`;*sFQvN2@2@! zlFixfIya5)4VgfOZ3L*^x$4TE{J|&7_f8Xq{b=qx%D;Q=%L?6~x|uv@?B?nxAkyY# z^MZeIWaPE|E=MA#Nt!)cahmvkW4M0W)8|MUFbec3xjb5XLG9ath2VU`a9jJ7a%9xMP9I0_ za^6Kq(?w>6;YB8}c?}$4#dVg?cYs0ZdwgnR(N(+V3>elEc$|MnuifAdEztB|OFlEB zYWNiBH&@3sbMJx$RaSiJz{5|qf(ku#&lLR0KnavEAtkPxncBNugCc7Br1iy?bYOpP zI(*wM7fp39E7{1%SlfL*s5(kfOYCH{icc$r#yw14Kf(fMumCdux)#Wn4^IR}MP;>6 z>HJm-!)^g6b%5Sa9ikf}#yw^*; zQa#Pg$8_;|TRs2JFgjU!T)Tg7EbH2Ep8fx^3FAr4MbZz0eN8v~tqUvf?m}-NsmW*; zO@t7Xkh`8uw++D?7vwvuRFF<<0rAUG!o3Rhz?(WiNb&>b%qx9$kp*sYcteqBMp z6QcU#W#Uw&k#eSMy{SR=>*-m)M_`w*@zGGCTQL(1k+31a^FbCc6=zzcpInjaA2Xr9 z5UFXNMO`+#N^f)xp&nxjm%o#HOX>NrIH=Ipwsq6DW*vAzWCa?isvYXeX$o&4NE4{? zq8BqAe8+?s*W`UMHX56)F8UKcsXV1<6qD@&o{Bq8RWH-5{Cr*H;ocUdicb%OFkK5w z*$EXI7m(VJibGi{a2b_vXsUmPW=lfH1=#$mGyFiOJOac7qs{}4C#k;GPEdg5Uw~kU zf&JhzsFk5tez z?Y^~+bNi(DJ@A{HQ^V8qc8{XF5Qfgz!_r`h+yU1Ve}MYPmN z>nep20&Z`NH;6LZ?vB#x+C^|a*b~Ep3g8!A1pV6}#}!#Id+nZosRkxF;SJ7u>L%y?{;+h+VRPUFM29cGdKYoh+36LgK9F zjrX>6MwTp%_IMuG%eP-$%o$>W6PAFU8xz|~&F@v-lj!Rsf^%21Q~M#Du^Dh^p_EGG zh`Y*agHa-iUL22R8y@|1Ymn8?m`7?D;bWGKdeQWT!}ZeJd*B&{_9a!k`} z_1y`ww)}LvKMsY~r^~`fhG&w!9YmN-CSIB;4#yJ;4HT}xxjej~LH`3ciNij5I*S446X+Xrqoi+|~Fu0F^Sg#TOLHSzNL&jH8@z~fZTd)5I7S4271wO6*hoaF% z&jX-S%zWDqlZSb9E5r}6o7es{@5e(!IYzRC5XQs=DT$2)A4HEGQ4sLtvI=|y3VyZ3 zcU_TtXeUSzM*V4~M1zh|RXS|C2~}~hs2E@jGgQlWf^bLI1w-6VK_F}Wp@eIx45NlN zjbX&J`zbt*Hl*h;8_vL=FdJt1Mw-ww3cnsUBEPC5OqHv}Tm-#%eeNfHqcajBPg0Y= zN-C|bMlRBoth$oJI9>@&h?xDGg{ zou{(h0WNU+7_zOBz}F^e)pc&p1ex`8wNl7{Pi4|(9RSCrPzoEu`DciBo-&cWR2h|Bornt zz+3;oixc__uA6%^4_i0y*@JJtII#P>5zQEDs7Jr)ZxkdOZ{Ood`lV&5h)}j&p#_cG zU2_6xtPzIC+%eD1*%~b?n~HBPzai(9l$=&@;V?JjmYj7t3s|P!S0GTFJcj`$=Q&-M z9|u0WSjPeIFs3yoowXQTEww#)%_en6O4JB127xZBvU5o`(291YDk6JRm+)c3lKR!x z^trw8oki^64mGGaXR$~s#)kJruM;Lykw@DMlz`YPibMcVK(D`s)vQ>EO5SehDh;69 zPgp6b$zKv0ft!^AVeD}r^r7B}6y)c93@#ERSQBG2)Bug3MozaShsY|v1}_~f=p3|h zhdZgIpzyogQON6u#Ua z{qopg9=F3-TKM%%O$!#JSP#fe0+XaQVSc6EH~Q^dTRX|q;+$qVDNB_!i6*G=gO%lV zCTv#Ev*Zw5WC6qeqxPGsPFB7gk(bC$aT$;j+hKIJpVEs z7z#*0IShd8hHCRUDXPH<;`xB_Y-F37B6QZp1EnEh?D}y$asg#H(xA_g=U?qS!j8HL zpC39@B$VXu|9Rwz!(bFM7RF zHM2mgG6E{wxj+AfZU^~lo$R5`cfp1$DQi{C<+c+{o;U-m zjSD_{_-6m8JC}IN2|pg1F6Z#bJ=PW`GZaP+m-v1+jg8ky12%L7@s$i7t#Z|;{w~6!z!jM!>V?2b#JWdE1uvo4e^dkZQI`WtqSG@0yNI`FPsQc zj3HrKAV~`WP7-j%uzcc-Mk53i#ligi!ieA%C-T_qT#o^r|6j?xR+|2fRo1bj?76ba z6P-=@PNm>5#z!(G{9H3qWYy;5O@{>Zb{n+~|I1l1C;tgpi}e{B&-?@gEr!CR$(+!$ zPJdZ3!DSKsXjNRE>tyI%yufqwVK+CJ3;!ay|2hvLHFxIL}-(PA#YkzH2LC+ z%{lMRv5{8u-V-I;rI3p&dxxVjAUt)JFg5!2755U*>WiVGC94oIx4N+KszyLuQ;7c~ zpdPAHi|$SiRLsY|UAC!|G)uEm=T%jT=y;zl+NMt%3$8p0L zrIesja>*S^-bEksMuG+oGGsGKFK0psmYpU@S!!_j6k9_SK^Ds&0_vo* z@bh0SNboShpu7S901BqBEp(L2(MVPkhT^549|D^Tmtdi;oKaI1 z0%0V9iED^;`%doPsH#6S*J(JVTo@un0{ZEga~CrAbvbf2^rQ1m7xB)q7ujmAB#CT- ztdcz+Jg5CwII7_!vDSI=fML|L#hV>=K#slZz&V-LZtsE1VQ|)>$aO_{MmXL*hm`&u z1r6TmfH3P#d|_NC1XpU-t4JaKh6}YPCa)fA5_PXDsz$a~WH#8X-Y|j@d%J%d)$Qr> zCJuCd*827h;ID`HvwgN*JOh_;i+o&JUzMTS5D#)Bcnz1#bZ58$qmDi|v|LPldtv3h zw2($Iq+ClcrN>(x_M9}k??|u7 zM8?2}ggmJ!2q}%2B2-jp1 z#|+&uCVlbX#qU4ZdeO{g#x~{`Gc2EMZzS7tY<)KO%QweEfHBrd3=jfFBD505oO6RE zBgg&XOdEP|+eR6TDT@dy_}_l#2AO;YsqH{TZCfN7S1Qd1f7o2;?Hf2`06uIVLO~kN zmL;UJ)g)HW$mD_v7tcb_PYHo0AuDPsJe6H2nHUb z!=D-=-Q{)FiJ#gaS-W8_G)2duPU{|r1D>HI8_i7Sa<&@-V@HO&6}kGFW+C$n7|>Z2 zGe7Vrk8Z;6SqI6F-W1AXsMdr$s45Z!g(*uwJGxGwU-Q6Rj|G66=I3!P#^L!Ahmwor z+0<-CkB_G9mDN0X#~$0_z~OR3yOJvvRa;w#>)(>J820VO9qIXQ2IDR`9-)p{oa-Mu)H00V} zn7x_>gv!87)^=klXv%>YKRDXunpBon{MBv{4Yg6glX9w=&%<2hH89Temxli?@x#Mb zmOVp8wZRtJF!$e_R`H8=vFf6Vs*f=9-s`LXf+?&1G~s}y2U-Xv zY8~$U!-nTOqm5$J2XFqrVpf7GZ7vkwYjKIrk@GhAV6j#~@#8s_fN#-u;z$~61wGK| z1HDmY#kixz^>IS5B6^PaZ0)Bf1wKlVN~zhwBSaNh)U@6hw-=^w_k^DSoQ{Ohr~c{8 zez-la@W};DwY4vp;0L=WUe5c*G&BZp>Nl+LoO&{QiJsPt;lsgV%iVjl#?#Db%ipTtX=)v#ekhb>Edd-WNwJM+cu(bk(w>r%NwA(t;Y9XSH~ zGG$p^K4d}V7E<#4{gEFuojStW( zrTn>r`sK{{;ksP}()|q&-|`LuRBN!362jkEqE%&+7>&l)H*eH?wZ~^Jw32_O>Z&C| z8!ba&1Fp1}2F_;R-|rf;v~9BHjD)RSeOnm;*?;`|d#?AQ(YR`NGSO)3tftq>bUu?5 zrPOgwj!D&N*sjm-?b&^+we7c~{jIQw-YKI2nJx|rV5JkvAakT*p4k4RIG)^Ezj)P` z9GcQa<}qlux&Fo{{E8hs9=fJ>x^wmW{JG5*~ zAaZva470Abi@4WmU049+b=hfw=d;lPXu$PFF-rrx1A0RwFuSf&s-t$b9@uikzV0+w z+fVTPaD_LG;XYYZI~%s?`<7tojWM;{w?gg{jC%w1*pb4Z>1ETzDXaFX-PRuy8=Sx3 zcO=%QYfut%90iK`37HJk;@s7qg;Be26}^KXSO$dSdLe;pO?(GCcxeSzH$8DUKTr8h z$%$EWr5;Wr%RlK@X-Lw5pqR_FB~zd|I0Fs%uuJ=NPheJ4$kS8l{_O#-FupU zO96x|Tcj(3*lc(_m_}ub4F$Ty4cFYcO2{y`6(kX$sX;bsDb$lVuE>{0gU#G;OU;dW zZ*ov~68oi#wRgKBDfsq~wGGpj3`w^X{HYtcbg#8_ zk)qG4$Or9mkuAy``KFkGSWbix-5Fc&*VR_#{a3YEtdH?DPP{kMT!o%PIkzr!mNh48 zy}@9MEXG9iH@fWdg^RSFXGJTMbqiDaS-n>oRLfOqY;_{H|I&8oM; z-l$z8pkT*AKOS8*R+@n?%}#r5#&OINuP!(`JWKt7_Jh8aEuPU>&IJwX;X&o7IiOW& z{pXm1WoeJHyxE@gW|6B$7{$e&A5qw4FIz zd({=(tld&ro2E>T$q8Tfbxr7dOs^EXbcp)w>umQoS4)Y2F|UAy==LG<(zD*Q{g3&@A#&7iplag zE!R#J<|3wBugLOrPWyr&C%KMPblWivsEdH}7-=FBR})doMyt+-Tx`1I?ocqKpxQa{ zG}juW#AX@!eKbdrf%(nUG@2*O^{#6_^|qNpamghm`3d)<-rpNlgudp^5yJ+|HZZaJn@DZb zR+He`YMTY|s-8}%`p3<#{MF*!#kw)(FU5XpWDOx-u#PMV;dsi*6qu8G;o5~PaY1Qb za=GkE%1k9t9CH>Y$yT{oSoynP-!36CE2+u0rmNzUsFPa@4?Q5}ooJ1R8+rJ))7*EZ z2Cf8@|8+X=RZs2L6!#AK*TMJh?5xnh(b3+un@j(ueB)cC{K6-x?p4)~>c|PZa~ll~ zHf*e?*>`T&0G>|QXTp`_xLL!Q8QM?Dm~tr8AW%!8FDX}P=>oYL=_MMe%FX0P+oRv0 zna@Yr(1XxMb%VByIMAIFTQr3X?prRANYUI_Jk%6P!2{stEX$krT9Hms?UsQib%_(F z2^fjcFbCuD3Lb}uVdqId?vb1SvZ=s|4zctQhwJLHbxe^bjLeb~=NM9LuGJgwtSnCI zbXs}Y#oJ5-*coq{wX;*(YCc`f7t57Wb)8KNvJKCw=ris{z{rM0A<=RUNirN0712fq ze2tU}j@q56#`A16VP8iu0gCzKi`LA!f4+-CyCaEf^9iTZVRPS%|460?f$MCw+8Cn@ zuJ)QgF>p%uKzTRVv7sg1j=IMQwO>OTSFXXt7Xf3%F{@vM9qFaX}zoX3VyUXox&TfgJ^KX+ovX$sUc-~>` zj$k93k?IAP$76LvERxT;V6S-gsWs(?z0t#oQmk3&w)LCWDZ$yKm>R7SxLKw!?Lj^iCPS6r zVi3C+!RW6Bt-gtg@9kJ^SwUzV*gdznf1a{E51wV_;v7x`f4!kSw2M)k#s#g5TV4-- z9RTjBPp37?K9#7*tLtbf*d97&*9(9mhh&(>tvS8YPDU}qk12_Gv-d(Iy0=S*Mh!tw4e3G>lXuj0=uGH ztAxd7`I|+@TM)diuA#HD2}fuGNN#)^J~~sRX=-9|@$Eg5sKL3|L1-m3QQ^h|fL!i;?4E8g@e&QB} z|ElcE=28F0(vx&iIBKTT0z)2U+3NLo%|lhBB=s~$5~80<{tt`a|4?>zPCEeg)LFSvMN zzPb*mFwLKxeiHAvQC)7lsdtTOCsA$RkSSJcF44!93G~as@o!(Vj_K8c4_#j8OM;kI zW7KT5WlfpaP?{q=ocVNXX(7G(Iz#E%1@<2|tKnv2W?|*V?sMiUZ>B-uz$X0P0Bxo= zk_Zc;D6KtX(wkgvi%Kyh&)j(}#oM$`PiEzEjmz5ZtrVuN+BMyK0icQ$gBh?9xV@CT zeO(nPLwx6CX?5#O4b$kwTT0zff?%s=3X)C$+TaO5zvIhmOSKGH|1{Bz8fm^g=zr!%_F};Nb6UoFV=J{1u;M+Pi@panY z#t?lgRP#A(&1fRTwekkt-O!HX=uvz3iN5zO3*ylK>(W{xlW8_bi?piVg`$w?;9aP- zjH{<8uwgKmb>{erVhir2v%V(zOs2Liwrr|VvQdc$v;gB)!iT)Y3U>Bh0V80U@58`tG{-|gJZ0gSZRqLcB zce8Uk=gT$SlT&zKh4OH2hr`{}=E?0zbI8T>W7ZLW&fl2=mpjRmp$Os*A3C6>#z6qQ_fKBk^}+{ljXUAefp&HSa^-@YeCYEt?|@5b{+LGPWX@u2Kb1-; zvUkszX5)RY_?vRt_z5eb1~ z2#rnzoS*{r%aQXn&@T7k38fKV=M|jC6laW6ejP`K2~ubNsHg=l1>xt=7#wOKf zBwoh@sT;O8UaT7`t+n!%akY!wY$iTOe?1gf3_OYGu%PXm^NYPQhEq}Vemo}BdGXaj zLz~odbZj8IVVn3lG|=lhe+~!coO{_@vFXkEs^+j zGgTqG2oq30poU#M0t z-ow;&!zl$F&TJOy<=&7W?DoCxf8_S1vuwSQPWaUsS;&rcKB&Er)Z{=z%E(TyD=;y>&8PT?aw`}2J5D*N6tW?;iYBQtdg&L) zkc459`D#3MNswa*lLKUM@cz%Ak@x~b4}Z+twjUUVS!!5zmJm(To6T}fmOJ3B&_F%~ zVm#!ST;-^k7(zDBi^!x=+34it6ZSabP;!CcQdS}4WM^mZ!edB*E6$aL`t z9=z+y>VCHsy@H}o+pE%Rfvd^s@`eNM<;6d`tlJAyW6RksAgY+=Pj)|vGckL0`C1hO zsrpu%%#&H~kLLQR&DpBbcJVmSqaCy3+Pr>#crUN>yUtH};>qA_zFaC67Q^F)8eO?= zw;Cwp{T+4JcU><<9)IiJaC%&MgLeMmt7w1_%){8F?!l?;IJg17!MV_D3Y1v!sTnhm ztFA5DhYTFlcj90)fP7z@GuyB9RE@szxEt%rH!&O6jWw6oB1OBdcRPTo zDLzg(shjG$ts2$x&da<{X-K-Gv>y-GpKTbQJg^)m*#qi_n zdlILaX88C4V1&m|Ej}KXG{ckT37rV@&LLNi6pQa_Af#`Cj3|3L8Oc&Br8ThOZSRxh z8JyguuRefPhX4SFtZK2p&VS%$nU)Svp({jqJQm!YPBruS`p$;3uwZXIL63%ml{w9g zFm}7y$|@L12YtOAZ)D$H6~jz5I2wZe8W2>0>>cW4E<;1BCp0*Mw#SVm+yVCZIss%I zuxhAFi4Uo$*R$-Cfdk9GJto?~X%cFBkO-lZ)&0d1-RAdTB9} z&hDwf4GKgT*cCT<%3Hm4wvsihHSc@JT(X1^wpGY8K#kO}YqP~Rn`9cV1fK&Rx@!tN ze)BT%K|A}~FsJmGbf+TvOsCVf6Y`Rkuz)1=^``Oa@}i7uuqk@(Rivhy^K5Lt&90^P z{jr-oDN*#o%0Hq{wvk%Zc{oajeqH!>b8)#b(VOtO3(WTRdNXx>tEFw)X4nL6oAAr^ z!yIg=n=<-9ZT-Bg$!9}+kTv(l%g`xa97%!X+qhqe?w4^X_ znYv1rb%K=M>ZrG z#YK&zs2D#ts1J%>%^mU&HMeD|J+!Q<(FnA2Wa7Gd#`Bj46I;dB2^KsdQE{&0!R?8A zwil9-f))jFH9Q=r6!0t~?&q}%C94=DIq3&=&!1@a5(Zp*JE6VSJCK~&lzSG_nTD(_ z7;M^u6_rH$+g5B^QJe+oozgSKLOu^ZG}G0W(uoctCjN?g?yraT1xO>w3wM{u@GNtN z>H{U6Zl%mB0%jAU!lGu{bOnKoNdt%uBhrj{ZA{Q=2v*u97G^Ooz|~l-i({l&8H-7- zBuMuTOYxJ(%lZ9p9JpaLq(gcWRG@O#4PDSU|E~RxD+HyaRmF`^b+l?I*l45#D4hi_2cTmx>q|i zF{N8&c$8_S1FV#Vr=)$X$IfFWuGh2IYw*X@3@g!ths zPn#>3(9W^^!o=cv>2FO^tl>zax<&6q-Wfm9{5_pyy(zx`Q4FS8Z`KFOOAwIaEZgg7 z3MWDEHH{}xQa6-ZFPmcNMw-Q#!xRyYgp@DW71A9N*YwWByE0BPiZ0bkzi1VhSx(Tr zLbFw?l(UAQvU0jwF4!sryOhpRiVQVl0~QVfhruit#ogKc8BKGzn2!$OhyV=a;}St( zt1-^+EOExNEbZaU??!RCqyj#X#N3QlDzDcdjLzXsNgJM^3f|^85b6Kgq0!;-KPLp` zdMtz5u*}iL1VATE@;9N?|9q-%9jLwI0PS{kQID6g;w1F|JcD%{EyK0^bXIOMft5$q z_R4`3i&3WDmBMo?x34ZM8*)R7hL%l|-dg0S{jCfMRn#F7D^evB2He=c!VBlq*b2&n zsyu9Hve78F!s@?3W@uW07K-)ijHFG25nwnmE5}kYO!WJgoPj|vdBOP8;h2W{gv`$b zY@QdPhGCJx9{d`$_! zwcdbx37-+JRzs5ODFH4{X?4wDC#4hNNjnaa}9*FZf5 zLjzrYC|%{l9|(sQvsa?Im^1Tp1I@2h%KC#edhaA75HUrN8OM28m-)|{y8K4B&h=BT zOV-vEDG1|2R12r&uOL|DuCVZE(VdQXt%%iavwgnfb>pPZq{3p|Gcg)bnxb_6#Ii(G zko$ESwQM0(n&!)T%BpVJ)XD$sH;hd`=uzsoFc1(^fDiM;u4UEifZIVoKY2s7%rdDx zm8A%?W%6S7JqMGsBeM&W3M+oxo842YB(9$C#Umoxjt^*2p*Z`IpD~#)-JN|Il8IFo zCYq{ydviO}qCYHRn<`MUiGBCew+SLXZP=-Qdun7p?e@c|x&Tua@idQWm`hjzCh(ky~gogG#0K_R4e;DxJ;~_6n6Mr4rbVHOPLzU-l`V zRW`35y3@+zmLvj&pg$-+oCEuAHs8qI`8wloK7?8ewJp5kY^%Su#w!9dEPIfHl_<)J z(FT~>8q)+JAcU!W)$3bg9{_N{s!XX429nyj3jeOSIQhob_ zBwMp#>fpu*&v?PLhkcY@`bO2b@cgDETvLrqIKmzpuQ4s+0F9U~Uqn_n*HNG;4(yK2 zz>+Vjki9_h>?1USYCs(ouM8`0f?vPBhbAZ~Ww<_?wE#am%#2}+&$O)jlQA_$;Y|K8 z**;X8XV&J)gpyO%OQ7>Cgx+1YH&lel!U-IJ>V}cG4Y+NAJms+}Z%fiPZ_B}}FA=KQ zVg=w~a-sLAqvgIv)I| zz&IQ2j8&=!TJesgW~QxdMu}{8sSP?W5*IC9^zJp9{2Rpwvf`4>1Rp+}73MHV_=zqN zpH_k>ILnXDzs?9QSK|6qcgHdK<(*1IPZPo2`pVMkL6iiP?f8y*ga@>{+ZSEg8KmIl z=cebJ^YXZ}oRF;F1nPLLG>6tMxRyCt^|s-jUsT~szt`8Pm@XFvn_kBLwAZ8dJWlA& zgOqw7w8GJ>#v{nqp*&o59jmU!@zyJ4oppQKF-R1?wV|6qV{?1N3<{G?mgrPsouRFb z%?9TF&f-qw{gIuWY%SafN8kXA!k9y#j$^oxEz7j|3qdcQTLn&QAi|5rX# zt#L^b^!&X+kIA5oJzBHTYIwp^@7=J4gN%kAfsONmt7)DS^X-blI5jC3-vnNdREPOME%EKw`{XeheL|ge9LNZcfG6eX%C9>(7Ow?N?2Kx;OQsR}=oq1!1 zhx5m2Fc+tZeX1-1oKDkJU6yOo*lEXsjX( zW7r9%$w)bbg@{w7=fwRbJm?GS!)pTb_dYO=8n*l)~CV)ED&XCPfbIh!>2vzmoW;^Hu&` z_mf6BVz|Da1aNISM=bg0p?m2a3UEeD^>15bM>Zr*b^ZJb#V@&3SbhC9s>J?>N@io>mse+M5*AB1~+R!HxiUavG!b%f}B4M(H<6Nl{uryxLd!Y&GZWu zkr&26>ir5!w7Bt{KTvg?<91vgSZ~e87V4!bF^WAE;;Z3eMNQ>Fm2kmJjxuF6Hz|$bY zKFC70fFLso1t8>N$ugM75Vh6Y0XAGDm2z{rFvJj6<0(sl+4KU53}!Wv5W_^Y1Auu8 zSfTHMkutvaYiTCNYW^pWx4l}r+9+MH4EsERbVD(h$;@5m2CPFQ^{FJe0PE(~bZ4kp z<-~cVT|J_7E&^{agEx9h-A+S!?4|HZ2SUS?TTcXV{Om;Hk2C;=&wd|EdT$>Xvx;&- zhan$-ad)~qlT{KYsHKBW$Sf5I1qp0-YI;s)Zf4$yg7ZExlM5R^Q4@LCLnq|R~)?mgh zTe0&zB5UI>Pf~y7)LkN5^CkhazEsuyLOFBq#p>GcKZ(5UvcWmt%))}}=)K9ZAST<# z<-wmMqY*d}iPTb``n7~Y4|0=JMaNCe{FNL#p$e@DE}<<&uz?koolX`)a_O}M_bP7k zK+{UK3T3CB+)U^o8BQ^DLV)u};uB4fJ_aeP3d+zsg@ctk7+^_QAtp(kt7f^3~JoH=J}DTG8y)-(yeY&q*trlhWt1BHd2J@ojhkhdM>U5DDFN8aBLamdE;ehG}!`0Tm z!S2i-?4zgH;a1`2b z!|s1r0Oyt{!R6xO+1T?r+=kzW?a0;_(8t(s{`b>XJTE?*r$*e2cn;N|wc~IINu!q` z+5y0^71tSMV~?W5cDHJqMl6wV46Ta${2aCHw7q%KsKX$TW>~GJ*S8nqBp8ian<~#NhUSd(;W=US{4wY)z)gq zmMpR@b-k@kZ$ZM^~9$rF^m>?l(_Buw-((TE`^Y1L9w3=jQ6KLazGj@X{# zlg0KTF~?%h<>FQQq#QgDEq8q@PVrmeC}R8_<-dmh())0Fa2JUb=L3;-XOl_9`dvp3 zEFYe0WgJ~Nry$L!)jw#E4&5G31EQUuhd*}g1t>4?+zl}03tzS(-UMr>@5n(wjgCsj z?$a97T0$xhA%vz0{lKwJ*C)1Rf|-zDq_pH*#hg|M&ktYMBPtS;xKNIkbyshGs+VFZ z?#XtH?K*|agCFtu@0|>YLvoJ~SPEO_iwrh}B)2p0zkSKWV{wiT4@A{iF7NAMfbXf4 z(L@bo&T^iMvSD|8D?^Ta-i_QZ?0lh_TFZ|j5aD;K#d%xa3JLtFq5q}$zdo0g3Hu?H zp}3n6g&t#hg*^ITEbU&}gv3T_tHn5{M{0GG^Br?(yTw#ifUSDql=jAmJnY9`Oe`CW zwn6$5X9B_hvMiyfY%*IFH>Aq?>UEmmxq0Q)x86PKNtbP;{55!^MmAGco{i*6(Cqx*YyO^$QjPbgMEi!=g?L2n}`c$tFt34dWE|t_fX_(+wBw>w4M8%3*yi#43!?#V5P< z$-3DPbDzfri&6Q95tR@hpW;3lh<|&IvN(<;y{SjtZ4ZZe4_jPIHxk3R6Bj=E5Cz;+ zC1a=oc4G#7JV5NX1=oeihElGd=@r&tOc~9Y;Av2mtBg1Q|Nq(b{Yx;fIrqv3m3@pC5jezaz0I7?%~lx+Y*qI{>QIvb48sVuevrr3;>EVRu4*)TG(<_m z<(P4edJggS^GDa7=AmBG`Q!QgS~^GdYfo5lRo60TDv85nLw_J%Pt99wFI_iUZ52D6 zRk|Yu9%E4~7Mjq__98;Ahotd>Y2X%EyMWSk?v$~do+IUb<=Exm zva*l_Dx3~!hbwgXIW~={1Ql{MD?yzPc^4UT2vCku=c9>@ufCuxDy73{u|G zf;3EQ{-9(Cvt$zFt_3!Yfn98OtX+IMjkTgkFK!l*oFst`PDm3^_*AWy*S|o)%+MX56)kF31+TcHvQxcBO2~IGX1n5KcI6hW^0dg|teW%$8Oe-6TrD zIAB8AN|5>3U*RFL@6fE%YUIV4G<_l91o>wqd2xBEh?>wu0Y5B;D&H;AD5s=1pw(%$ zS}Zn4^tZ`RN=H=x!@g{vvU|GtyfxA;q~b4L;^8f zF6;02O^5iA8*CMFO3H4q7vgACX2H71BVey7%48Q@GuKUis6HcQ-cvWjj^u@q(6K+mFOBCq<@(ItF>=YffSjlcva z+}s#&JD%$My49bykm4)YKi~=<3SJ%e{_)wChfx6X0Ogd0jzS3Xq}U8W zU=caVr)8c<2hndfec7mTY$4~yz|NDQwZ1pa`eYmqeYc?1gB3ResoS2@|DzLE1Aj`0 zG8P~FZ#`@){AUjN83I+`a}BneRZ`d?t)TTURsEVlmPF?LS>PK7F3-WAa2-4@i@kGN zeX=S+UTYhN#||OQP_f}Aks2UTY7UuS)${NP}5{= zzr^O9G6+d=8>+^_bJ$hF`(e^-J8{7@P-X~B&#;WpD|b?FcA(@tqBaO4uN?!m!?NNC zS41OM8jMXXH&mp)7fb>(^3VJL$D9CxM?eU8Mo8cV>@4FVVjB4%T{L-m>x1{w9OdSa zYF%z9MSj>>qV{e%!q8ar=s(($ky zZuO?;as548MV6(<++ZiYiwRHc5dkrxp@(5Wka6SFvEql5{awZk)Am?oK5uIv&G9;Z zeX4Y$YPf}>Zl;P~^#q@>(0qp0z|-P8zbHp|v*ixjrB##LX(Vtu#U$e>rOlpKgQj8d zIjjWOM;IRbraO%wT~iX^1hZe&AR0W!uP;ehmF8l-P$`Los3{hwn0==Po(5a((5mh7 z8QBfQLt}sg?@pmW2R6_ZT&DmBUx2yY#y2+G#)Hm6fsulHWm)?>M{N3KY;gcnk#>~T z9iJ2q(`eJrEYf2N14bKz97a*>o>5i|Fw#)7sG1S1UkSF>Z}TiA*1~HYDWMjcl82)N zD_D*al+fx73}xSIJs-=rQ#5SXwT(FLYfz#;5<{{$0f-=|0JgqDF(!H*%DPsQ&90Ii zF^anUJA9sYt(tD*KMc)UnbVHA-;vL$ZO1gAzy5)PcC6kv@xw(*tDCh|p75S2zcQ52 ze$K~r$^Z8=Ng%^;P8VE~A&vfeHR^FtX<+A3Xk$p@ywY!8N`hDlfrZ&~t3ck#J%TN~ z0y-byo+Q`aVmnUAkp7ZJ*}=Q6KPizl;wh13H_d*NwZq<@gT?&`W=l9NO%V=rZ$ze4P{n+<6TL+?Dd)Ea`&*i9>%j%=$7lwbtrsL|y z3@6EBkWoV%a_s&=+kMP!c^t@)!~02g{wM*;u0Xri`#*PP?+ZKfo(Ow8` zNsICuPuQbJP9d=HLZOPT17UN`RWkmOeYSlj9?#sn^J>p+6uj|rPgoq$A+7Hcqw8iH zm8!Y?QYl}mbyR?8twZk$akg3S{>(cgN=XVtN9$Wwz!XbD&s)qt+q#1y;zx(n(Ju_& zHSSt?AI}e6OCbB~PLi2u*BL3|ysfn9t5)T~il}&e$wIuQMv1q(2P+JRJA3Zct93i3 z49K2}o=&jOdOI(TL%Cm;>Y`Wi+BxUx&P;TWQo)N;Z?w%y1JQ0!MPk4!1*e{#bO zJ!u2?0fe8671NS8{8t|UumTi=K{(G>C1?bwdv5^ay`&j{9T;Dd+hzjgWklQ<+wbM8 zK)P32&0Bv2?FY7gq2yt~A6WT%J4AiiK^q~nr6`)J5x*|m%dC{`Y42l4E=Q5AIj*}B zfscB=E7;iSc=xz$k^3%_Pc|j~x%T%`P8(!%OR=Wrc=-mt<%I_e`O=5+RWECD6gLaC zeTtrTS7j7!tj~77pT>)-P5Aylu#K*^sdX&gK_oTv=v`;?J6#qRVH`)HANWj1ct1mk z#_S#pvLiMjq7pGxj{LSW;WR7Yq_99&Iv~gT!O- z5p>0piGC0~i~<7$0w+I$JdGUh7-}^Q`qSRPYMTb+p0{40S&_-$=npZfy5`S6f#uHP z>XP)=F1vs-a>&G;XqPM9NgaIsLzy`jvH>>`_D+ zW1?pQErxPApf8OaJX6T5aLMs&GI6D8kky4e^F`_#@44;qte|`Ip^X_93Nz|&F}JT! z{HdNHsOI#J)&9V}yJ>(GBrm;jsVMjQ(X&+K;^SN>Y-CJbNXSJxKWP|$>&jfHzQ|hpOOZy5~OK6oOF`9pGde%$bC;1c%V@_^?ZG8Eg=Q zOo&9`CWt1(9o4xmrcCZY`upPvRuOcgINqk%edktSUECioL^Slm29@?+7`jH zq@@AD9Ea(t@0C{YAIsRkw5nS-Mqrc(zP4;1Q)WMhk9)&{m|M+dG~KW?>PbeMDUuY8 z)mb1`o684R>UsT1=IDp5NwF00(rJX1hF@0FnX&z)Ry)}_->SEw_dr+B@H+1`Lu^XQ z|13t)0r)>ZbNr(N+<$Kwj(!#ZKS0303E}|!XJMX?(64!^R4QyFM~d1F1J1Scj-tKY ze*rw*?yjKffpi3y!3uIn0xN{-qOVu9JNpVrp3ZMoX_if_%aKjnM)8??&1I5e9aQz}m|MQ!T=DSClEBlSL%G!Lsuos{r*|R+LT+t@e|6A+49qcnfW-%Sp1n?1p=iPhIAxCYaA@r>b;K z1naz7u6q;oAN{Rsh7V#j&sINqq912kiz7Aa?diZ%K3&rqWgLwrVd(Mqf>pn#zmQ_&D}f{vAXMG>v_Zd>@c!HB@RfX7++&%O{c0>*FhpYid+T2G$i%sLtcZEpa~X|w)J7kqYev83y?%_=k9l^a8JvjdpX2d?s}cwa}1Eg4RySU zdL_eWuc}zQJIBEYo>Cx6URxH`NIw}e2yK1vQR>3v`GByqG|>i`He2MnAWOX~nPln0 zcMLZssUeH^P^oQ_P5JuT{AiOY3^VfL;tc%q*Wh=Mc-q}9_2Gvn=JL4R+`ZuU)j>)A zwaA*)VZJ4ir)e+K^#@rHLMvor>4NX$!;$Ml&Wfx9^>u6}0IM2sbh9DokqouD6QSHd10 zlUsebg)1{xpYt@PjXYC65Jfd~xy-Vv`ZUpV(1;48#A%WqtdxTATzRPMitlAao6<{q z;*){^JbSjZYpj)8F$m*|#|3R?XgXEp;yZAERDl@9huET;p@D*r!^q@66vg03HrciIKu$czR z%3XVF8ek-FC?Nx?KDu@sXQ$+jtbge=90Xe=HiK_i@znhRu{1-hmy9#osaApbF-rb_r$e?v zE;{Cp@;~%SO_hxoz^kAC$qG$YbnIMu1a?)B^N$a>Fge4OH}v0rAkw!j_#oosOD~0< zh6{Lvy2p~x<*m$h4~}yST`!}KvG?VHFq6~r+h=AyL9cl-Sohe|rt4{cJ#AbdJLOa9 z$o*BdvQu6<%~uB7e(aJq8v) z_hwT?*t_|Bo?1r0b#bVOK*~uaRDBS*yJ)vuhYEys$yJpRK}&vnVqzQIn?-7*W-F?fnAI9cMC)lJc8i3CP>P=~^-0+P#uD3+L_B9{jyQwI6rQ-aAykY7@Pf% z2=@j(c;0UKqW~gkjd=73)61%Fw{E+kc^6?Qv5NGF)~ri!F0u79j>1vGO|=7j0}7*a zluNY6%90;daJ>!Ogn-zw{{4aU+?eK~M^a`wwo_|!70wXGXll_ka2U@(63T)VNxbo~ zb$JI$D3(gukTy7|mFM=svIbrCAFvN|-3GqBy%X_040 zch%mXg3B>KPxgVkfm+I_Va3lla+PKB$rCZmU)oOmdIJi*PR@kZ$7&Ot8<#%_>1EFGWO;KFbhDVfR ztA#>txl$}uE4AIsJ_?S9`I(Fh2Rgw2-0;uC^_elUse4^xGnJi3ImTdk{&}+noZ0ik zYl62Sw$~#{&{PThJmccwx^VSOpRTIeLoMSFyHAUPI-xHScX0`4P>b70s`g(WJ2Im# zRwSTzn9(QZ_8AXw57*Fyr?`zq{Ets~k3u9~tgjVZ=1g}pCsa`*9)&oE8q}f!>5O~; z=19+b@d!yX0oiU_hxDZHbSG{pZ5E(BpR`tR9*;5?XM}F@Z@=<@@)DhcI!wi0;k$+C zBbFPJmoC&A_?hR(uh}{g^g6p;Mwr)+qhZSR+J$udMe#P@Zxwlt!$vj)qkwsz&+s+TVI$;l6wHb~7<`g&!y^ z$&__+4mbPZOeIt^3%X;s81nJ_f2z#GV}2=onWHPGEnjf;u_+;QV&;m6vul@o$$vu@ z2lB+A>qtR0vI{qv5b$ z;fngC!)UVpM-8Zf~bu zmE`+xEK$ym5mwC^aPsolMCJv?a}CFNnvFiZlzOZD%U7+MT1XzGh*w;f?67b+-=SRY zaKJpJp&~5Er@JYufyK=L$=6mfVjR7&6(Efu$?0Ydh>@~pptkVMSU}y}1TL=dsk2sV z=goaYo|whj@4!hGO#_*SZnhB_zrV`G>U9+tPZYs@ANbYtHe;yjLkIxL3a7U>FzF63(k9BB!e z0N>i+P95FSzijMO!6^VUru2G^HE1@e+Euud%nz<1u94V=9iXbR3*`%$v!>L=B{^gMW(|m{WXSK(= zV|l;i$XoQa&N`$U9w z)j;8mb&Ay=%Xugm69R&yv^FJF@$0;e>-frCChOWl4A8L)y2l2oQ(ezoas@)`rL00D zv%T?V{b%!abZacp3gKj@KJHiMwiY1XD1)ZygNgU{5+;zxy|sDIIM7MOcJ(DThiN6) zyA7Q|!_%(hes6HY0Rg!De(|FP(sg)<|t z!|wEsVk2~3@WywWl(&xe+?S2eu3`npD;{7eQT>fnr>>ua>4tk^jve8!McmEX_Iu9? z=Z!8kEvVwHddu#w`eZC3S75GqJF`T%o=fX_3|(o1Kh;4ZBrmO!CeG(dGqFu#J)e@1 zZCIC7`%v*ePO=v+7`3EgUpNm1aQLr&u#Pz@a!n%^WJMeiw^WM)C?g>S@r^hZ>`lQ^ zsK6CJK&SCN>1g+F>B-dC@T;npa{ngMLLoEqveD_*AID!_BQAAat2PN>EeUy`VVgwU zk1$2BBEK0WRg)lvvKbU_#I~lYS`?m^J>A_A#=#~sDX-fNBjQ~=I8#{hEL0qU%BSwt zK%+y8`qvFtr9#YP91g`2N@XzqccIv%=UWE<<8F4Y1rc!DFI5C4->&_+(hn6b_7E=> zQA99gc(`qE;W^Q74h7DmT@7BmF@*7SGHObsoS~AUQY;K|c52Q5mNKN&er3Bq9T~fF zC|c@3&D-+<{=v52yqRhqDQoUmV|_}+zLh__ZU!}EwrVT#OHe9*`e?XOq!qX}S$^qIXj@6-g*J2BeUg zKPZ~_Xy7Pqd63rd-?2ygg!J=OY-<&4oedc>oA=4Nqg6Fqin&BQN=@uh2hJ)DwrN=o zPRqFqu18ENPez~w*`x;s5OOe#Wf-30Ce>!GWi@&qPD%pJd+^>wd&J$-? z^$}#`my&kx@*1>kE2vMgbMeEz@a64R>$b)(Vv0Ka`umG%jIgGjn7&OeoXR98lY!&r)t7WG1t}iHJrjeqbH2hEY4Wy>y7gXf;Yi`){s0FqRzZ*5(?zd4KQfOLz ziWU}ULgEZ%bRcWnK1MBVW0@|*#6;NsG3XwM^1moSO9YOQr}mU_j^NbLb8HI+buHRi zs}-^N)AdU|PywZm-D*X2dtG8ynI|zNH^`ZCRxOyJ%oP*3qTNPp>4t7P0WAk=_7!o0 zab|&H)^0Zl!a(k(NrqGg8s}Qpt~GTHu%Y_hZCk4zTl_No&C#u`g2BJ_oU94=Ut^e; zJs0lM6Qg&!0&E3fMmXy@#KY=6nE9USc-*gkzVqxM9;$y~-Z^mgBflLuw9O{q)k2uT z@NpM!JW^gs6dY8Y+WY*XtJ{q|9Nb!!!JySA@7!03lJQw)mz|o{F}yeFbi1%#jE1Ix z6aNI;)Qt$5v;b*2`gUvP0~~aGio@#BYM@*b0p=RI#!%5}B#1$T63vic+lF36iE3{n z6S&ZklA-8&R6PjT0y1m>G9hzfkYQpSC|lD?2Vcx207bOQX`CW3-$eH#DUzTCMOGv( z>=gKl5_yNfiT4KLsE!zp(KF1w>_nA~(z9t!I3dtH?|{%}fgh!59C@61D#W@^NFWB`v;@MBWL$6(17%G&T?dLjP8=#Y|+tyt6^tiVl z$R<~(##EGaEcyXhwdOn9O?;jWB7`O@g!~FEOVmHCz&@pMBKZ(pe8aE{R#)i_f6#^c zJkh&$Gl-s{TBZC%A3;cWu zP5_fe;2_#i2Q^B}O5IrwUtnl=WzsdC4vuetyjN?|nJ_#Y@kJ!;WBwv?)QM39?&_6Ay3W z@C5ex_V7P_um4>7f1mv5Ti{#zl(z9+2u^{HhOid0m*vh-3OP|oXtFL7j6|E1)#+GC_-ja7O225i=?y^enrzYrL2j-Tc9WzF` zl$=mX2x}ViEX}e4E%RJkv*F<{S^#bsn*Xzq!q!#gBq=giwe4t~F;>>1!T6@@yTiGK zZjt@~tpY%K;L8dCJwk{jP^1AfT6{`1;g|y`^W_!CNGP}m7Ha_ENKuh;bOg(KlqYhX zX!;q;t=@B$Ggk0okzoQm0E>;w3LZSi*BcEY!_nm^pyfI=^cJ{Dh#)0E;OJ2gB|F}s z?{JHgs-`5oILqIyD|@eoCP_geFT_C6>21h^TY@mFqS)Q8V(nl+Djr+R;SeCc(alACmIrM8c>=hQk`wCrF6lN8k7$Aqe{&zJ{|d$qJcUv%%A zPi3kLVq2dXx+u#eBkuhD-$a1Nh1j>5-E^72uh%v6-eX60`3MT|vK?T7MQ2S9FuuU6sLf0yJvfQWta(5h!r*nG$25J0S%kQP_e81&zpX-|7dXl>AUV8 z^yP2yS{NY%AEcSk$0D>|>663rJ-4i+#Mw4jqdhkMZMcwKv;qtI?>GzTdXG;8iQ9R@ zU@q*coleIJo;@(sYb^P0La9CzM`ZFt@7}H4l6lUSDJ5TZt`!!&fvvQFwSx?E(}7J!tezoFsp31oX->*pf|2T7N38>)@7eA~f-fq}DW5~25UIA?i{g!pA*4l( zqX*Z}hyfq?kiX)Xno@UG^Q6+UnxxNa14Av&73gyM4fF$rT3Bdh(%8T|JA{zu0AwQ3 ztbjt6Yb>qr9W*xQ9QdJQ~2ifFjJ>b|#5~g+Ficx}dY4$-F%ZQNLfCaX( zSMRJbE(9NA0I18lE;Xl%vJ%pVa*p02-0qCmhL4O170M#mF!X|p=rd6J^BK{AF>1se zFu6Igo-$K~o)bKGjs;W0Y+!>Q+QD&*)f*Tw#F&siI0{dr&qA(If^v~;kPXew{*WO; z7EKiBX@X!P2`>nOBTgHECGIfE5lV*prB&t_AW{z2B`N&m@X=5rb5-#C&L`B-Oufo2 z&;Ow>8%Bq}e)SOV8`r?q$PblX(<~ug%sAF-S9f%71Ge?U_y6+5pbrh0IQs+(xrB&( z>dOIJs1=of6Ms^@hh`|8$BjuC!RO~nl2@05wI_|7#H71q*CBdq#2c$m4EjwP!`Z5O zD4)*cj*Vo)A!ASy5iWCVpbc4nDD&(Qr#0|d=`d1aPa{zfMkyQvC`~P?^_1jCb_HZ4 zI2}a;;m#i&9dY_p!1dd?deBlk+8_fJZ45HhpR+mBO!PJ(k(l@JUMa9&*+d`*-Fg=6RQUPY8Wx;hvbB2)Hqx|udVT3NL@EUttH0hOq?7lBNhZIIk_3tYNdcA4NOr#m)eZ%ql7q`d*p`-in{X?{C+<_ zB2Q=3T^)ZY7as068tN_|U#t!`ZOmIYZqpP!%kGvww=ow>X%-xTVSzSx1( zr1duw9v$s9wI(0RsW3#P z>xC_ASeR=+WR0N{$0Iw4BJ72J;3H1q5CTg=u1qydDzdU8NAc&uky65%lTl-u<8YQ? za6SxA|6~?2s0o2Y<2r*oU^T2B35}q}?orAH!7jL#c|cyAp&B7XreIC^t%Q$+m$Ald zv0=c*ok?n8K{vEo;~GUCXL8o_$uvtPf8v{%8e?(1iP{NEH2Fsa;@&*Ab_3V4}GPknDkJvnJr9*+|3n|R;QfFw( zme2QO{8H}2L~mc2eyF&hq>kux^2$1yI}WdIHj4|*=)7q~0~fsxMgk_aa$z=^(1kyO z;WMUE{f7ptN!xXau->2sB2-=hgC5gKHzVGdMxo*`MnyEjP#6s@3{Zzx4I}c3KxpCq z5177kXEwoZsfRz#J|ONBx=7nM&6&gk@**Xd@8BOT{Xx8!nHQ=^y^zrk9T1wAtC*I! zxAMA8iPT^BM6=Y@M^+ScAoL1CPAGPR0FkAPk&i1)tv^E^rxqo?ht2r(X?21A?O3X< zpk|DFB|@Y@C3ds0%%Y-Uj7oDOXoBnCYV%9Y@NhQM)+uw36&I>W!bX~xk~>{zY9JH@ z3wj0vo@y|XQFN=S3OvGT9%+1x5ng{aT!|^)Ov~h{2*BM^m7opies|vObi)OO{!fZy z-~;$s#5O5SBT>UPpFpnbWGK&v_7;Lw`6qU(*ILXrZ_q0Udp}K`MtS)?5uMvsEjpU@ z-@zo1yuZpHtvgV8B1+iO0xLIE{hyFsPEqjQ^U8~nUTw4_<=6lF(m%hLmF{i{##8YK zfpEgbMxuZ%bE*eZF~aFGCl_{Z{wd6NM+ucyBfM$OSXJb#JweDVHar zx|Q9acm}t+*Z3OE$D+2?t@HXv^n*NYb&vPk%KR`kTX#j3r3gDe9FMK~>I8=o13!4B zw*#5e)D!&vI%@_+HFTX8_3~}^7QhSyB1V2#{s7T46QOKq{(i`@?P6y(Qi_;p{=RArj=GI`6^aDC z`97*oPNj`fl0)?xm=5p~{~!-x2&28Gm{NM{2%S3!YC~iy#A&n3vG-bwGZP{Z(`Gcc ziTW;DSL`Zut~jn10*sww8-(7EVq=AJjI$PEBY_*Yo&jfYENa>U>Pw_<0SYkke)e#B zsVK+N!knW&?TZ0VytEcRd?_6Qd%4cRvG# zDh+?T&nXCDKl?AoFS|_>YV~@<0G(+2rAwiIVZ%JQ^_JgsvgvxuLc#`NAI_EV{r)Z+ z2qK|V^I&dPQ4YS89{a;=iu*q~y3cmf`#D z(SeqQ+ar?cBP`ov4=OoFOe>yN#le_jcV%+r+EYjMG5v0iAN zR>CkfE3K|(m5D;L@^P)oF1eZNib8F7fyJc2L$8x4i!W(aW>7V1-(=azKS-$Qb1Yoi zZ+yG57=gj>)^K!H7m5m^@XkDz>qUakcrUH~yCb+eLZLg>$*JRL_0`vJ`4 z`MP@a2}9xUEJP?z)tKDA?PypdN_pm_#WYwWT6yA7nRg+sing8&QzDip3V&ekC?xBS zTAm3}{NxJw{2nMu%+W13Z-4yBEEMC3p zQ2-z`q0xvePR1&%BP*53-TEjtv+@R{!62r-cB~qYY06O`>}4{M*?_nmt(FIEo020i zUxSbl?~-U;nQhYiIC2q-Y|*zaK8sC}807auVtKP|QbK$>+U@bm;dIjYe;zw{`roZ% z3N*e_lEO-0c@f^6P(_^TwDt1H4+7b*oIl#~;jixOZA^4jy%^Xev<5ngfR#Z|kq(!L z{i@Cr3B-&W1t@K2HcOnig@3)G%Fi4v(MkPs$9rw|U#%bB`*+)Y0)uu&sRSAz-^DUV z$615Maj(~5)+v=B6ql){omz37W2)`ej!@(4aCU)g!**OATpqvJil+ado@~9rPc#Pm zmrx@I?-OUnemjP9C&4XR4K^h6?#AuOZ7t{}Y9Q@uSdh?!3X9{SV3-4OVz;-Qj?n_;Qprni3_(rQ95Te=EH~OUj~ADiGC?r~375XC(fKqnp-LIMrlay*n@V zF%vv5zv9tOE>weMjbJJ2xz(+0hx0vrIig*_o_Py8$L>_QZaja{gZ{lC)<^F`0_U`( znmvV(n$XVVK+g`?YNM@<`9#XqW;>s6AQwD2O~p3%vSSY?du8E)4LqZKQ;_GE5S9QRb#&hX6+T^60TF!sXv4sV1}W(!b) zQ^FKyPD%0Zu~yoEwngbvxo!%d=~k_}wyCzhCVdW=478`~Mh%1fA0x3<8Q@t;xBz;e zz?u4aU8FL0iOv>eDnV8aRd_JQE<_P!yJSwxf-#zE5o)QVQa!A&3+$S#BGk)5E{G{K zxw1;K6uFY)02#_0sTzlHUMym}A`|LxJy4)w+K7i8UHcc?#EP$vp3N@jPkW}59$|bL zj7oVT1d4p#yI6f?QEU+`ix^3 z9@^&jGOu2e8BC(L9LSbYax!50$V732{=q(dk5m`~kJ@wU`*2f`kGQBRXp0d5 z@ms*P!(7Og;FPNfkpI*!*KNyoWJz*%;XFxE@;pH|)RgCCQef(gL=)r)%TomQY)2RYFPV4S$fm8b&635uiGr60?oYVE$71&-ff6+;=6K#+i3{MY1W>I zzH#@+$^vHRU}J0j3=o2b;SXqjMr*LX&d%|TbkfUvGwyz;cUVSC6;&^aLX1bUpTG~t z0BPt^n2Azp_bD5mXO{~+pYIv~H?-9OdJD4&bgKP_gDDYg5J)zkC6dXoz z2qeL2wVS3hs5Q*aSz4;t&2N~aLPiam68>)<{dn_eK`Sw&4M}f2$LQ>y)p(!cOk2$O zN97}j!sAQEr>jza(;|%1s|SBNNdUED(5ID;e&>#zQ^CzYIgr|mUeDS0*nP{T9*-Cs zp8do5jx0^HoM3FuYu@e?QZFrmqndP_cmCXe-7T{?a0~XIpX;H*Pwg!G^bJf8OZ> zXq5q)qp~DqUYPczGYE~yv?rWLi4rvD!GuP(8wmub)!H!*hq@lof;kB!5PBzS0H_Qj zK8U>M6_qdphOVoQ#4}VR-)6Zq!*OiVuQCcnju$pwx=ljYOi*8`)U8d94%`#tQ?V+K zo6FUH5uPF4|3$b{`gR;3Mo9q(SE}8TiQwm&RaFWB% zhq}p7YU>=P{w#QF{`N~-s?<3KuUuDgf`tp`nvF*C-t7l0qOO_eX@ul-vS_?Ah*|f( z)@}|=elaCxa3+N*42~gi9@m&P77?(aryrHerQ`3(Ebr#fEkWr6jnMDM?SWRW*Hl00 zhc-b(`lD>8&PXgsv8(F_^8QFc@jyTPxL5L;8Zs_T)5hb_e+wI>+%-){W~cB7hqST) zUt!5ZB5lQx55zj1CAQOGiNQk#NLuUU2og$wj|7ZF1Se#YqI0eV4`><3v2%aSlQva$ z)XuE8R2)9m8T1Ok^WYtwndAC++;n7?G1@)E1eiu=Y=Cgv3 z&imPbeHOs6m{3zEzg(`uB^5)k=y5?wTXiKuEjEY)d)=j;_nZ zB*MmlOSiK$1u+IKuKO5TR1$tANz|-)3m`Qirn)g>yuI#NmI(MBd*(D^W`F4lBT795(Poqk=qqkC}~^h#Dfad=VQ(7Vv7J#kH;u$eh0M&$2@=TFghcK*a#4sSc|Cl2BW3>FLGPQnK#|{2Z|cYsu8B{JBnG% zlVnuIfpz7;cY6Wy@tXw16EMXbCr6u*_oc%a<3d{$V4<`?upn!)SX3i55zumDT$MSM z?9%jGBCw*tgLa(=|H6u9hqHJ5uajP>#xL=*SYU%2Oh{Vs6_kE^@#ndRe(yDo=`$j}4^N->p;mj(9ptcX_2x1`(KyHR41twq=lS*$%VOYG^f^Rs`8-Mo z>)gw1BJylz3}gj^>Z?WiUI=-Vaf#q<84X!ADkt3Lazk0VOeK9xsEzQG7u=rF8_}Ox z4XL$Sp8jDlP*5GyMx|RVyel@~=~h;RcKC&HM$8qjgm(|pB$~t{xK332dM&d~Y2LkU zX0x)yNU=3|EqC*z_MU8kV@553$r1I4)}8@#m^N6zJog{x_qR5`gvWkbQ?0v`-0P{ zR8Unp zlNg5ntcuM@<7mJ<|DZmWdd>Stzwf;Aa%Ll<>CGa5>tKo*qI4r9tLUCG?H!PG!?JVL^7(_7c$?3^-;yJf zRfgkv&PYR#Gc_-HLLRojP=1s0h_SK6{2OI(_no=D`9rcvoH81fmG_%pE!W>x8C@}l zeq3+xmcZo{hqwFI1WnLgmv3Et_SmsvU1+#Z+4Ycp2fZATlQ*@ym*qY(L4hxO>3lx5 zuZGTO3=)%t9ZB0hYr=WFy(143_C-B+J@Y~4DXTXeh)mDMQ`IFy?uOqK88^o2CGx~I z%`d{~MOUNQyyXTJ37eNZfp1}3e%UaeBGYtIs=YBbbyaDW>yz6u$+46EyXLg3XRlPR zk%a0kd-Z>;b_Y|K(14271r=r5xHf%aW$9XqySk{jzDFEC?d4c^kWz1`wl#cN%djwy z(X2jXlaXvylai!RSZ{0e224F&t{4F^5jW-nI({=x%1hZXu@28$Mbn6ynl93|U>`xCX zInmsOqAJU6dweLntx%=7f@rBSdG}TCG(hW>w_*et6ORyou~}&=?s=?_l*c|6(;7V~MCUv<0 zXy0Y=+~^h6=cDT2YO}`a41iyoqANO&0HUK>#ht*=oj@ zr3zQ^r3pd+epsFQ$ujGBEVo?F^3!}L1Fh}aYA$y(>hVlxa`|FqGZ;QNWHe&BbM+OC zTIpSl5ARrSvPo6T?hT_}7eE_8AV!QuRho>Hh|E+J)h^(wgU1q~Xx3f#1g8Cb`Ubai zYnK4+JXBs%FB;sVYU$D@HF#DX=)6H_oxL`4&}>+vh&Jr?20d2T%2)R`Kc7Pbts`xN zOES7y#*f)koOXG{1G}ZXeju}v14lwBD=GSE_L?-)I%L)uyqkd-->Sc$?ZCf=Vp0(_ zLcc6k%y%)@!<0w!JJ|)ex~g%gz8zLLkr%}T?6kF4uc)lW#gn}o2AWV4bJvQ;O8&** zA&yf*o2Q>gD+9c zRx_u>jj2Xlr6SS-;Xv})T&c1tS<+Gq4pBgt3OfzP%ce+KDzM6?TlOcZ0k5%utzS`2 zx*iL2=t3QJg;B}HCNnaRt*tOs&`2z(uxZdxVLSp4jp>sXQlKf3R*s={pfDjZhHFSF z6FQdjl}!PVh?jg^CZ*<5^Y7FDF=@)!LCt2%zSsnHIi1#g{q}q+4MX<+L&1jO;(?cr zUjovbvF=5zIQ~^o8FC6#>Usf^u0YzLpl4SP6`u@yLU~p46pAR@fat!Y!NOYUyDn|~ z+maIcz8bwFkL8`4yidu!4JZRkQkD`5n?*}ku{C@BB6$`9Wk6M5JYCsz(&ATB!Nx_n z?*DBQ;&Uw7${rqEJ$N#->~1T#51>D{Gw&IR&Hzc}YDEn8#>1tIZDK)Hs!m4WNQ@6N zD9fthf8!LmPlUZA1Y}`quhXw(rv-#EA%OpZ8zKQuq4m3zKkMGM(ai~kDu2!o{ zVLn;5+w4;xY9v@tReFc7sYQ_&;e2eITW7Js!3ao+C5yRVoEmQ`4x1UIEJ7l&nKpiO zx%H%2Y(|*p?2$s@nA?)oYt!}$$lSujeby0@B~FnxUP@3lvy;K(5HBq2J>?Ngu>!Lc zkWY#!HjANGF5xUQB#`}A>za}Dyt!x1l9JdUbRA5E7JkG9A1;hyjECg0sC0^79ojw| zCOztGQi5=3;m*3(>hV0wMTmV{{Bp7wnDke#Q}Iaf;~X3lzB9OFo?SA?q=XW`5H;EO zv8Z8~PQPg`SFVK%*U2;m&S>Gjj&OZ6cFr~RyIX=q>DZ*3K8Uo^2nfIxv^YY@q-8m2 z7y`7{mOI$?!It2F#QMt@F^MaJ9CjT}hEUJQ`+p76e-k`?9B<{+ObtfMhiEeSn8?#!|=;`Ql-h{72dz~ry z5xyLWri+E*8VLe1_pH$LUspP8?&G;TH#p2^g`je7<@HVX^$UxFkk;|C*hRm+czWJ= z$R2mw#Ak11W{O`PpVjwCSN@6+Q9oW4!VIXi)D-==6*~>_sG-SJV8Jg~v2(V~%v4{A zy!YmOn#rZ+?U`!cNSt~uloL4Z>^9Omy+d+>k;=bw|3TBFBmyZh1k3S6`hkE6ek zfB)xaU)a%mN7!(eUukMSswv)T#RWYPYQyuzB&Jb1-mtSh!QzfgYn~=fxi4I-s;b$#u{Z_5U$sBLRO1p7|#aG zvLtd?6sB~;;A1q-8FP~fcw$1!&2%fL>hnirFnZCnIV$E$6Ort#*d_v%NTRk#t0dG@uJ8{c$2Q9Y8C z7sdhw6w|Q_V8$<_w@R&>FJsqsh~|}7G@Nki*4VBnY570q{%eG2o5*-IpkoGE6Ogbq z)Bn8LM_(yMrJsCl=b?p4l5TUTw<=9a4rF*9xoTvvr2+A5BQ39G#3b3ahoD77$K{}U zPI2B%bwjiEbe(uj*yO}n7uinqr9g#^5E{wtA$QDx_DUN;>%L;LFf4O0+PSm~y|+gy zfK~Cp{^#G_UH|aZXFn;J$cDZ7k2X9Zj!1K$2Zy4Q1YtLm&d9Tcwx1?4V6gGP z`E{APa$qxZXj6JWRS%S3^qO)R-w{p_05P&yJc8nK3D@;tm_Z*~Bj&fp??u!wRf+~O zE27sttmjiu-_PByu9d6gk*fyUQZ{MmvS_KYL?PPn>8B?3zC|c~+nm8dM!~@t9B4&v zHwBLnsy!Wlx5k*w#BmIV?hDh*;Q8nAS#2z(b~OgDWc?&fzUHdSeu>t@_W1Z?U+TMy zSB*x#P!1k)1~)Bigceb@cjn9*^%9&%hJSbr<07#R)xlxwk92iWkF`{{(oLn?a+q0d z>3aRH!C>GmUtiQx8a;TdX{shKhh1O0du#6EC9_#;wHlII$yaNeYFN;eQKjajq7%~&-04!kEumN7+auRqP!`SH5m)89SOlg_A%kV57rNx}!u*||BTQ`iAGe3wo zT0u8lj-?{~q?uaS-Hqis^)wS|G?)1EE;fM3jlCIsk_xt~x6}FosMV^=&{+`mRkxCD z3nG(bVREeb+nt)Bu_ZN=6Vv5l^>D7uhs_H`^9RMlBa5%qpmZd=FFdD~8DZCyd%ric zr@D_!_UOlT1(8L0jid|zS9J*!SeNyExdR?Q9B27!-J>1(&c{p!_)8=f*)+m6#i2eQ@AP#?(2s#w|Fb=Q;$(+nmNkT!*YH zo1Ao92)E~`PSx5Lni%(jNNk>Cpm<{=IOqM2#mCZU{dDjWC7kUthgJiiXdjXQFaT{& z)M(7P$~(!wVQA5B9|eoU3-Fcg$uM8|6X%V_V+D=tc=^^>1;~fnIWb^|_Vdl37izV? zgHS$p%hjbnWajSd-XvIXNIqkij;JOn?f|{$YZgp)lAN>U6*8s(H$ce0*tGD!#!Z9k zgl@2tkS#D~J*exelg=E-xN^p;Y1Alp~Ch>3@Yg@~4KxT!@9R7zJ2ac~1E8zBPW&YIc-nR7(K1ui_jbB0p5 zc!YbtGD9u2`i^Ll;m-3+qY;x0H(L7$5Ri_|YcPOLM6A_>M9~nqK}Qw5)c*pXd=#f7 zCoyxisIGoK&E*hteaJ{qloxlpq{s%`k}4+j9{s69gr$15+AEFM$WGo+yR1Yi` zsAR{BD7z+4NVYNSX~D%&En^gexhtoE37MFstJeoqCPB^%&te7vJY0z6kPnVy^N##) z*Mgu)4n}gnA;p;3twmGBLhxe4fC?m^WZ6S2BWjzK|L$yjvt_mw?h|rdx`I6kZit{4 zMsB>}HVozFd{p+Dzuv+jI`u(@8oZK>%b50Jw6JSTLO%z=47lGs( z0Lv_x=_9YfVk>Xhz^l^}WZ>lFt+6(fBVW`moq1C|Tb7cj=9H9L)m-=k0{_oKm92WM zk})0wsvvQFj~qB-el((j`=2xGW*5S4_Pb%s5dP1gQRSje&0^u0%q@@suP?F5JG0Gn zO4AVuc&!r`6d1CY9O`p{i%pyUBN$8|`o&gD6rsP!#2n8h;!L)!$uW-~Jj$`>HHxL6 zUoDl%HlWcPLbMVpRYD%1P8Gd1&&>LU0<+QS7L!l;-o4r_AZRd+tGs05U#+e4v9h~! zrx|;!p%Z*&bU!lRug>pQTewN)UtYXc8QXxFNLmwHq~$K>2#Xcd?R)-D6u7vR(p|^j z5;Bk74MEOEV``k$XDkfI;~0vssnJ0|da5WxI39AbpHUsPC|Nk{rV&OJF{2^h!(A%G zdIiLL=p_=h5G^$?(`+^S>oUisZ5%34;Ix{2%5j^UWmAQYMasVmt(#|mrzhn``yaPY z&9d2|4Rl7el+5SOB7z6ikuzQyb<9P;Z=KQg3vkoI!wT z*`}C+zc57?xKfOQ32A*w(Re~MEDqi(WkW)3T!&KuN@Bo4FMuY5OGX&>0HIY3=Sl`u zna>awSV%Xe*sFQqG@D|Bl{L^20%PL1ye^Ms>OSp; z%{h*CfNRe6Uv6w$&Ixx0f^4&d?{N_otxx)vfE;-%A^#|Va7Pj+#SI*24seH|Vo_zr zRbze*SI6lj1OCZK`EiTIAW#Ve2#^%0yq*m`1JPNfjCJQ2E5M=GNW$B3nMZSXtiKk< zV#+&`!ygAEb#i_dI!R>NwKOIPl-5y({~lT3ivp4~Q^!Pm+^AG0#Y#1jjs|!!(h7+R zb+65!R4avtcD0o&v}bJ`Qk|rcdZpMeMw^m&b@isLr9%g)yIye+s+AJI=SCI|ewU)@9?9&NF&E)(<1hb3JW?R$4k=3q|{;aSK2oY5QfY$25894370g z4s=60sMSZ7D-+P_sXr?)LB*u8tF|7zx<+q z+#0qjmm8$-zbx_p??5W$tt%t9)N5;`T5vRkjZ@C`0l|d7s-jMx{gK+g*#K|ELz6Lg z?_R}?e5l1)>oi?a?Ad32HVXXdr|yjDSh?srmN2mnS)eu9_4EA;jbjMehvJ*;sX@{I z9!u;(;65*1_f~YzjbBVA9Tx!VOl^KnDzL;%Wk49OrKr>yO~ne<+gD7n4XH_ z#Zf5Yv#Cuk+mbtq`)&1xWEcvnGx)4HPtzf z(O?%WT%Li5mV>0};zmf5cxDPPv++W!Ti+JffP{%IG;1nmtcm#r+5^|cnMsaKKtnmR?ZPRg{{v>(^>zmEJ(zT*m1AMi9w9tfs<>S4{$X^~5V~2kzO=!+Hlm zhs5Wqwb_qfJH)xdNM<{~OL=XD{3!h!tq5qj(VG74gEPa$_ez7+W~HCI4;&xoie0?( z{^Cspy!)@R}jXgn-*IydQwz>91*rMImK9kA1uIlh;Wjh~YQY2GWP0-VwaoNZ>9 z2nnOGur8RXBCoZlVi7}ciEYQm-1BWP9Qa22tz!c+g5vUIh$0^)R&Nn4>DbUYadU}o zAWJ0bSi%W?YW>1B>J1SiG2MKek(fNcFV-|SOXa${RxYut-oF4)nrpj)k6A0Ff@{W< zd|_ED2zWro)U079Z>xWIYGD4)ue{T_d{27$eTu&0rF{S!9U~N|Ik|2OAleN9ZxX7W z3kP$)pnYZ-^9H!5$;PXS+QXx$QEe~$iS9W<$10p=X&+O@83S{Cq9g?+$Zk_#)5t2G zc+W~JQdMp=>RKaTDwkKl+3j3>mejiK^H;<^ujc zz#F9J#>4h@;%vCwsBo!xB@3<7es4CPjmJ%~B(q-G&K`=(2bhKv4{4X&`#;_Q!GITV&f3N`&;L}t>}NS zV^DRL4{trEbY3_j8vb{qT^Uay02llfR)3|q`gEbPc=tMiyMO^)AIfa$i3vAZ4V+Rx z0M+GO=hYp{@WHZ9HR(Aa=-)=FAQFxrnx(cYv#6LM9kMVa6b)*I@*oPOk}jwcb7))+ zlT4nL?vc zS}F(a+DH5&H(VB(EO870f_huw+*fkHaR=UM>+dj19RBLG;wRtjviQ?zaP5j+_)?># z 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%0DF6I5* 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]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∈Ny~pb{D+8d0}|G`{sFRBkM-sQ0oS1NH34p~GdC_#_hMpc(#6=r& zfBXR9^ApS=5)CmOs*8;5dluu(N}XRA%6w|P!6=c$;$=cPp*)Nx#|C(7E+fe{7ifPT z<)k+Z4*#+8*WaPRN;wyV|L4gk_Tcu+4b)?=TGIirc~WWZoxGqZ=Ca_)&s2h@;k81f z3EZU|9MQ7#??!qb6pku_1{en*P4Ya#0M9do9TF&NZS)z?S7!7BTt0h{%aX^MS7{%h ze;lKUxxhb2?+3?zkJGSpQxr&m?qYrDiF(SGyde>!v7c5AgUf8Y|7o)B6qvV6z8k@6 z(~KV6-%pA(0ZizaC5;scIi4$i`=LWKY+=akjyp6}-|39}M*kx=t7OG&EqM<0DX`Sp zl8w5?$aD$bQ_eAXME0c(hGDb!rV^>mZW9#v@t< z+fd*Hz2oMTr1ez6OYabsWIo(MVD@sAqplQ|7AWY(jGaTI<&X<77dM`Su?6%k%ZM6z>3xW)PAXz z|31H$73UD$JbvlW7Lb=wb*a*#B)md?tN_7nq;}ptm)hZTjHPW|;BPsnzD#gsqigXX` z2@p&Q72LF?SWB8pMBg%5S(QAJC@_KS&E|x*jAl-%lSG;yE6Ypx@LPgtB97yMZcJcK z#qp?w%EQQpGC(buJh@xzLUs)e_~Q-;-BiNnYerB$8Q7#uijabyTYHLs5;pXxGBW@? z@fOKfzc;(b((KbLR03F#+C3DaxT~q6Fd;hI>F1REr7Kq`>Hsh20P>b%9Le2 zI31D)sniZl7@lfO@Uow3R9Q=ad9)(IeUp8BRlnz{@n}w_%rPH`f>CDVEX8p*N=1~6 zrUn#ag;sy5-h$8sLxrfPUY~4Bv^fbn(^9w;n{gVa@pRApw!^RB!9dnxHX01urCR@p zs>a@=efpi$xur?ST3rA90>b8b2FSqOGLJKe(I-ib{)9Q5u~^;o(fc%)ZGXcyL#tE_pV zCTs0hP9rEnK35u81|s9*W*@Uzy{ z)G9L2{;1fEjln>Bs|mBv9CkPFJn!-W?=uf=#Cx&b*e;;OoStWWfcHJ`3*P6vPj;y- zOgj>yw5(wDopeDFU6&33{e0Nzlsh6UNkP}tp&3&P)ytdnZ8&W!DG_l%FqXsz-{QMN zyM3X~m;c_MH+~aYA1$m|1O#(a3e1DXS85PNPt6WOLw#&ztM= zJpP{EmYN7iq#8E{Vbu_(67SWG1rsK=G6k?Q@{56@CaSrtFT^~oKO5_ta3@P(-9S8k z*x@t|Y0CoIw_S=vgkVrcQ!|CjM*rFCJq}hnQ`><-FM)b!kL?NL)|F&40&<)VyOO}C z{cvjsNqu232%?RW-3ik8zBHEUw8TL^l`8iS^fj7|{`S!=stYwrabPTN_L>#l?eaX) zg`}gG;QM;!0~xwQ#i2aiDr|?1iKQ5Y@7wq6WY)&8+ptfX)%}QoZKsGA1OAUoUo*cnXJ_!M)Jz2uq|9j5BmN!bT z;jdN`pLpu*6FXq~__%{a(=WI1Tc4Uno#=)a={;Y?R2;VNdkQ5n7x3;5pKX(%)Hz*D z-SwkWwWw%WJ za(1lVbcuzmU_9UT$Nu#nDiPs7kVDr&U;pWt3LSaC)&GuPzt(J8aiH^(P?&7a3P{jr za{|#U$`w(0hL_**`X`>PaoE-$zJ@;SFfkfxZz8rCQn(DA$CCX8n;6YDZ=VlJZ6{hJ zfv_5%Jx>sWU82wmB(FeTHC^S%l8vy|GuKUk`ZJs@f#S1i)Qet0YRbO8KN_1RaR2d!j9Z)-}~}eA5~ma(>T3cxH+XPRPXecaKNO&YdI!t>>M}xJq)uL z7j?8S7x5r%G*ZFUd8MqVjsQzrJ+Igdh6tBZvksIX%*BKK3PK-f6z3};NO8B2EvId{MWp^Tm#bvOG`R7q zf(EUC)+n~`&^KA*8b8G)J_msx&brm^3v;i34_09y$~{m}~^8)RPGlu+u+lkL;iXMXMJ=_r3rO|SVGIEQ z>!1XMjFufNyr?h99ixVyZ`?QbJkvWG5&jcD5@|7aD=-#L7Tx~ciHH`Vc#d30L5Ww(b6Hk z8T)h^i|5~Y8z)IT?Lhf>lwwH2GA)PXLV8gKy)eTRw&Q5dCx?|iNsLMIUUN2CX?h+f zxgJT_iXw`@ZAfq}Jo8#R*^!lN9~0tn0p{oU@`Qi|CPgplZ+~c(&fynvN$KR$_;uI+ zLaRi5fIZQp<7PJ%0Sep1*q!kCC?hx2A0f(d@dd0lUw*G1*UBc87QQL^W3IEhGB?(@ zLMuPx378ALs9W_XMy8eu%e%xf`FIqx!%w-_nhA7wSd*SU+IQdckKH$TQVFJ>?r7+m z*8h3hujkf#MG<`>ArI6X@A*t%TjNS}@j6>8z##M8j23`80?t@NLA@ouo8jE$IEsYq zSLLuz`7)SPLB%vUoY>ZYpZ~*2FkBGB7vx|0+F}2O{&y-zKq}uw@i6fGnk$*w=#sA+ zfd4Ce6v;xY3!7)BKCrS(8XOI{4{Lv%FKm9mQ)RG*MMNA8X8u!{Hnmw zDZ?26E`UBKX;T)0Chn#KkOkYD5tH^LV9uM*Bq`6dr4LrD)^b_enFT@Qqw?upP59Gf z(ip@Y;7`y_LGgufEnbi~wyWf*%toX{?g?%%h-W)a$IuRUIDliq)E z<|u?^M}bCax*GUG{@0D4$KB=mPg_%S#(C}4OdV8t4vMJXowL*z;!a9X{M|&_Fek!xc3ac9ZX9NIkM(HyS6a`HaT?T&Z&T>YHg(-mn*)I@;%OTj%f9 z##}s+u%mW@H%MmnuyM=w%!cdE5`Z#OlJpo+Mks5rQE3IN*=1b_(^Bcu9$h)doh$gP zzIvk!x>HpZDJCjw-`6t*)nL*6c*J4B<$4yqHrpEL>)W;#hQALY2RCgE%)2S-d?hN20fM2z=zx(g6_wnVFNWhQ& zyz-ZSf3e%$EBS@Xho#a_OP9|85ah_5oH^tFMI~js{rExqD}*;Z`2Vnz?4~Zi8UkKN zb++L=Udsn<(1|R-_fG%%vkI|{e{`lt7n!g2<=K;bh|4xJ>WBK6h_X|AjhyFZc(BG^ z;of#2|MuwaKk4Q$OYtGX>=LsZ%3&|duefx+-}4`n|0q8Je-m?gg~(?^G&cJS*={6& zp*Z8Qwd(A=1`ciGYTzemv2BS{4=13R@z>7hO8V#f5sEetqka~aX42C$!6nMyE`n?_ zBsGSn7qy{Ei~u3_Dq*nWZNXQpBf(bP3`bJDLLYqIB(m_VbV5@w3pe0irx|8Z=HUOc z5;x{}99SW>ESQ2OnTWR?iR^}Q*vs4$BDM{n)$f zGm5RgsqhIWh3P0R@ckHfK3k%6zkSal*;(ex22XTwe#^5noMq#0@nZ=iCr$>GKsJL% zgP#rkz)bC^-eCi>m~JJGW^D6vIR5BuNfzLt;ul}CVK(Lbh0h|}8vnW}v?i<@pW@qz zSP=Rx%T5;-%=q_mmh+ldJ5PAL3-UxZ#S5l+V=6{rc=;%SXJI|(7_42Jk5*(f3jhAS z|LCm@QS7gu&MK5#nj|eUk^FR(gtdUA0$e@ce6+EV)j6ur>_no)@+>T9Yp?LRtT@AR z3jF_`vOg!_gD!l>;TQ=+f_U17$pXs4T@nfPKaoo~Nm)zocr8ZzAfqN5mq_UG{hMb= zV!EjP8Eb&EI=Egw|Cdlm za>1AvuLQ0Bs!@yb2jXQeU`%8$$F&mT?!em;vv8ILN+xNadW})$JWe+sxc4V9(kR-F zgTJ&P(YWZMSdL!Q*4<(}5eES*JHZHH{;eF@?LqsNoa%u$)NFQYc6}xXZT%G%u77%V zwuLlcT25)!`O=zZEhgz29}Mi|8twndJgv>DmW;y7Y(>oSay59qqc|G~$xdWe+jq~#^DMO?FO82aDWc)46fXUj z6`gK(4Q@2=Y`@p^Dl0)ng38xQrV_4c&^{d{dq35ku|!qzM7xnv?oop{s*pn@W*M)< zxH4+t9DHI47OZmZJbhF-I=}2w))ph8|JE{zKFuJ`B=Q`G>0exGbuEGLc_ds*ik}~1 z`vSQ18$YMG{&p!rl_PBG3bPgOPLeMw)~SCpY|osGK9kuWpPI~mt0Tp~Q24yh9sE6b z-7LH0&$D*{ZC?LBfQ;h2%%$&&-9wd2c%5W*SSe`gx`~0e6vQTA8-S(D>PD9KY?QIA zSN9gByh8j9IRy?z=F-@;Dg4x*_%YcAL-ZlvtFMT<_?O?r)umy>u1)$iZ5Kg}R81$619<2UBn#17yb6tcmitN>1( z#xA}rL=wf=lljg(7Q29FbOo2QibN;2#dY8U9Ja??OS4QqJ21(XIM1Q!6PA-EMabBY z&ejVV-;!gJw&y1Pvt*6?y|-%i2=4o+Xq@IbNcZHEQ;pFZ*~CXo=mb84w^M1!2wrf6 zWQWsRKs7za$LJ~c)-{nGD-*r)23LGUj9=kRq0b9aICR}pf0mD*QLhK%zv(gcY1}AX zuWTu_HlCj(U(ZPTgV%SWD#E_vM$vv-C76-oabnqQTUGEB&VvgI;1txsNvB?xoZ`N0 zHuH@rT0@tO#(ZG6jlOo7hG-4jc7>>*CDUmkeiP)?t<<(=dJQbh$T2>LCD;pc5G!l( z2-cuZ4c?RF_x5^>hs+4Zbrxyp>ayUY`;Z>*j1_KXx|*jd93ipqZQqc!z3yrGcx@t1 zE({ssf@LYtRfPiZWa$S5C@&$`0>>_T{cFA0rY2mpq1ey{HCR`wb_zGLHEf~F?}e=Z zN+5fyzQIC{<{)h}JoTjhtcqebBZO;RLq2HYp?i%#6yMcn|zD70=k;Dcd(1L#+W}bfO`hnl4uGjI{DC zK#mL~;914a23c%b8wE1AEmz1IX-pjVVPt2_dLIZj2;yM-$pW*LGDo@oweOw(H?ylg zDyb!oI0op8fhy)>Ne0(KAgB!{Qm%lXoK!~~rq;37L{SlO^$49g>2miR@n61)c~^cU zCfCJGzqq_fSD%bzPG~^LT4@=Zc-H)L}5sOCL&R2 z``6ovOLtq?3IcNh#uwo30$eHhTsB5W3+d&5%tU$TbdAgjI}2dY{2}l;XIf)}@NqE> zo=pH;>pc0DYW1O8eLr@}@9-yTz%xBAzJ*KsENN`$+CptwsKGy4>|<1V9DgKxBH2$+ zSYF5g_BnDd0;lC%wr`eaAGE9f71ra?TeS~lDVt|IzV(H=>`1$oP&>TvWltJ@BUjT) z$?7)7I@{gnBkLUr+@(pe7K*;h@Eu1znYBYO9~g2<6_kHkgNmpz*^yzJneU8`p= z|A04ai8{3WlN5C1f8xSgPmoM4xvUT+*`;jC$p{DY;K6b5e@JstRDz1%Xro2>I(UkZg5KD;GVDpQf5m*5d>X)zb3YVF9XFA*N>Wq5wrH5F`yL~<@J^O8Y9A_-2XJpT2jRrZa ziF}KVin3&_77crHtYnMWB)e6<$)QN*eymrtIMDp*d!4+TGVGj9Ycf?jE&wNtWjcz0zDI2Frd9j7(S08e&V$K&lZcBz1+ z*?sjin7V-HJauEp5Pzw`KNZKe9xGd7@FIM%gO z$*9)$P`$f|rDnxi^%7YpYErNik*^X%tuIX~NwXX=)yq6cB#za2$iuC)ZP4f=TC1feo zUez6MI=Sx?sgc#{Nc{t5ER*kON`sOD04*^BmZE)=hsT)NZAq#a7{pZ-!Du`yGBm7h zBQzXgnKg0WGu%s&f`hZ#hYLeK7F?$B@Ko4k$#tFvQyn14KPR24nSAMn+g0(_PP~*2 zD`6Y%by%l~>+spse(Tw3b+7b_qo^&})oyUCc77S`Vu3k>PAQ*ld#Py7FXger=ZK)r zw@KB)THO2Kd-Wqb{a$_{olC3u6X_5(Fm}3|;Py;dfWa;HxwazcO%Z0wsQ`7mkfl}Z zi3$kxFt9^C`V=jM1z3tWtl0Da-z`BCsD(w$kf`K&H%?4bL3&)z3Is8A9EGd`BGWirZ1$jPbSyk<{W(PBrOFU)%I4X zK>s?S1DX&7K8qyUEKHn89dL}#Ar4u){`VFtkRyN{{b(8SuO0Nu#IPqe~I~~;Ox5+RWkj&U+5UdksOKRRg#1= z$vZJmKLPJJ9cT#YDi+$u;$=Zr62NzfkwV+{%%tOcGp;9{l^rhBgY7{abOhf)Du@@V zR|>h+RnVeg z(rvn#jx$0BT2|8oNVT+y^Jr)TUJKr-ZP=|s9V*}iYuGvOcjUi~-(+kM1`Oc80cvp? zOO)bc$lHSBw(jx&?$e=@h4t|Eq=7KqWW=;+SZX})PelMpwzbt8_F^^N%jbStpCEm- zHj=c?lqglB8Jh_v=9F~{WY<#Z(28>pYeNsRAwAhve|Qv<^MumCGo8j>sI>V0N~U!Jt1N(rvIER?1>sV9 z4dGF|tEX+!r5lzq-u$#js)sX7@h;YUH#JWz)nSM!9wjG*(T$EiF$6H%Ij4 ziJvKBM1=_z5ULTu;RC!C1M~D9zgCj8eg4S+exf2@9&4;CR!s^(#1RkX{yS<;Y;LbPDBPP znvd#L#Eveb6$v-*apFHW7C?ngHPSSkcQ~BKx7Hgt&n{eai~gf7=3|(unH1+C$_}kM z7-!IzQ1__<(j4>-A>KHC(xNP4c^6ZI%&XeG?1qoNF~b%9PRR+htPe@-T_!4GWYu9Z zj*PH6Gm@YB49`{!#y2HLOsF*QQ7EUQ;uMN*^_-_>mh$?c%MD6P$(eed0KE+(0*)WK zTre?i8~HCVJPs&cTZnJf=b!sVFVQ7CxgGn`+~T*b4<6Jw8CAd}7HsC=GlfN{4G+X2J0} znOY*fAy1;UmEk})DxuKhqoc7>_WC=u!C<%@(G_g5HJb))4dwSGtqO%QjwWCNDgkBb z?WbUsbzRoFZKa$>$vS0QP0|5_u~A#M7v&?vsW`#_gX}s(U#-n;tXunfPg?CVbKFwt zWpC)7U}@vedZ^W`fZFb#UWqDq=sTN?8A{XRD{EYCfsfXBhrduqsY$iJdvbyQEaN3q zsB3HqHAu@RJXwfx!6e#gnosF>X&M(F^sDTCLUf}6VRt6G&~9g)Hb5r2-%F$+E$K*4 z1~N)YNz2H}$tx%-DXXX&w03P>L({01F>Q&~{^UuTGHu4JIrA1QTC!}#Du3ER>o(Zg zrY-yIcfhuT>|%$h*6wjJWFaA`f?a!D_1_P*8nx=A)yuFO_Zs9b=k|*lHEGr&t5utJ z9XfUCmeZqGpMC=d4H-6K)R=J-CQX?(W7eE`3l=T0yC1S*l|B5ZbsK!O|82{*9glH3Z=G=uAaVup^+bfDzJQ0e>ya6_?XiYsZ6d=s?>bkIjv4_ zhzYmW!2}$fi^-YD6imrfOpQaB-UVCNZp0WF6&({B7oU)*Dw~!vnFE;NBKr|_Esv^m zr32|1nZboO6_1EaVe;Cae5%xGxLB)JrHf7mx`~?aYDqN&!F%Bx2)gPTy=`v-@m9J2-5q9Q=tS;N6TE zTm7-GGnk?*F}Apbq?ELbtem`pqEad_6;(BL4NWb5`cuPd8|)bu5fG7(QBaFPL&w0x z!lpA&*Uu+_ryG!x-EXgX9ESHX6wq0z>T-&Lu=diKMl_y^T+H8Hr z?)QgViPGYfDOaIVm1;F=o8H&r00>CO*&ZGXhuXZWVqxRp;^7zJs;3!!Q(_WQGV*4Y zZ;m{+zkO+>(MqSIXJBNKQDsFHw}ZJ|6;x=Am5rT)lS?MIEFKHl?mA&oN?OL*x*MC7 zm6KOcR8m%<(j4`}+XwgRCTOQTN0O~7$hebiH3^^dF!j^@oo}S7rmmr>fe{qLiB9%A zMKh(fSmk&dY(O6dF%zL)i*c9)<1r~F*lQneKKAnsJ=GCi^wV|q zrdqM@?T%MXZNRC@3^6vwO0ohUn25c~8CTrY`soBeDHsncoeXj* z!VktlM+NGCD{a=Q{i*GA6g(hA$j>S|beL~YT)6NNB1VcFl&ZQ|RAKn6UeClyl9s6| zUs^J%@G2QIRnUr3QI)ZU(CXJUGj~~e^5)CmXk(2x(PTlYFdtROv>%nx%3khP)%?${ z^>&M@swbUx7L=+&myxp?zwNI3HOzat%m|0laIB+bU zmCO!F_QS(V(m(z{NqD*xWcnnzJIe`FlIHfI$FMC-m_L)l+J&m$K6V^9apA^;7ax8C z1PKu)LbN|v4z^hpg1-g()E03{XjjK`+pQ{9+s!gCv9NJ)KLSgA;Z4Vy*R-kwLLy>U z--JA+T&q-2^QuOsZ$z*-gWA1H`zLjAueW1`PSV<1x)v;JU2(x8(tcjw+KgUS**n9q zU71R_sngjP`%dV_tvmM~lzu}2+p?Fm@GnbI)~;Ms&&AIDf1Buq-k22yXrl;orPmw5 zFf8N8_%i`aFQzvboUeiPc6C)`^{=L^#YfS=2hn8uM>{&fMmOsMMlZPhmG#k&K@5YB zk&|_*0zgBq6ZKhr8snJ6G-ff6MJz*zRfw_H4+H98NJhsd8;q&ekVDa2Ew#T6)Yy8h zHrA$6R(18oPxUi!x;E6_TBe)c4cJ|?Yju56y6VHsR95B6>Gi4D)GgN3_L9){W1bQ2 zT2I>D^lh~#e$K>SZa)fnl@qdb1bljUuZcv9sweVNj0tAy;GCkROK0}JN>=;rhf)Y8 zhYv527N;$(Rgy>4UM}5G?G=xvM{qw%Y?U}(>SR66ReSRPJOZcXKcIgG7`EdFXc<+^+rDA3!(cN&e5%%w_=V$QOB;S9$&Y zNVtd}KL3Iw{C(tnR9R1IPClv1yt{R;?$?7Xcvz3>X+C>g9a+p$zT|7ZMV9YbMlrAQ zBR}&izw;;ZtSZ0~ptza1!#%B@22oTp&d&19&ScI`n%=|xaR`7Ahs)y&gd(v7Mo>&D zlS5%}1d@UhMMX_RONXY%FfcMPvtUzWCG7p*P$w5Rj)#|zUqDbuSVWXyJnyNuJukd? zx2%(R-cye~ua0<)IK9{MVs)%sG0)qdP9g8^+|kLgbEY2xf2S6?yR)#nOBYnXqZ9RY zZ3KKE2q}8E1-kXyhyq9XmyVvkD0^uf#ybykclAHV^h43_lzqk%o+T&!M`zJ?ClYN* zwNvCP(9e7b+a3KC<*&a|6`iVmit{pD&F(~7{CT0gd*&iy5{>YRCT5eF*RZ`HCl@yl zFCV{vZiQ=vH5g*|ho=*HvZ&l2o=(*L#6qAjO*jIHLSwKR-BmR@!@k0c*&K`RN|D&5 zJAqB57*5=cYlh``;qc#CA(b|kyuUS&2z?vQS1eok_#`B(uP-X4DkaqZ{BDHt0#6{4 z$P_A#&S0|G00?ooJib6E5=&qN#iTMh6b45iDJW4?)HJkoXnG6-BNHwSB&{=7CeF*P%{P+F=0HK>7L1jTTIq-ciactO-k8W=$_oFFNh zVL9H%qXY-;9-fZ8T;7s9F9w&GOzq zBDM7CH=u1$2gPuLR46HxTBFs`dd6ThnJui|g#y}WN_#n~ar#SFO8EmQ-}A$`&M6GKR)jZIRQ zDS$dV&Zfg&Z!RH#y;PJ<>f2`$=m=#F3IwAy8Fp8nl6uQ~Z+_r-6&(p|rOsQ%=; z?*)O`kt^}<%OQkXxOVN^_V`d7owx>7-#TBe9WV?qIVClOPI^lSChy8apz4?;A0IoT z&1Ty7m=aEq6pJlwS<|#6Q$ayi?UG?g!_}fw%n3L_QY>~!`d3}Bgk_vq8L#jcJU#!{-$GW)Dt1r8($F$?>jLH}*DK+Yy}%i@GonmF%Cd&g zNpA(gp zIwy4-ay>O~FX5%+pF`M=2&32H@{+YBmq7%QxN~N{q)*JVNY}3g2!bFuz=UjRD^i+y z_7XlNoFFL{yF|I{A}$D3qN-E1FHC-+iKBIM1bETIcPqf-LKhq9@PB_byaWY>uJErQ(c>n)O}1WLIlG-Q|}C})Hwo-G9_&1 zl++M9>AkCP%286M_U&uMbivm?qYJ)%oQ%Krbp4)+Ok$&6g`D_}E^i`b9{*+1?R&*5 z;cD*lds5ATrlmhc-q(60d4BB|%^iB<5J!?FZM&v{^lkWkEIs*=@%tq|M*HCd!CYxy zM85pkFni0>hOgnWKB11}sTl(mh%DES(k(?42&JO&wp*;=Iapo1lUa@o1cqxduH6t&tr{_&fYc@qbR36l~=bsp^ zO?ww&VU+LIGjKLjSCT~onE||0>_I-f^k`0Iet^d4xgqy<#cs64>Fjw3UVsROBN$Gy zXlS7fATkQCm|Y}fpunwV2R4aW`d6T1nf>Y4y~s_kjX@v#>rt-n7WCq5@#_EVP)5(& z(f#jh&YE9wz8tEk$r3qW{=&qr5n#$FQcj$|Ske9}9euX*yo^?9zdR2qrmqh5tq7r7=#4ZIhF|H|j6{ z+6B}|@*&n8C-47fP`FDsjuTr|)TSjfY1@+__0DA>PFzPuHE3;`eCCGM+ijc9(nt@G z{f=v{dKQHT*(cfpia&mT*$O@xE61cd+?9~`J8p}whaR`8kjuP8%iU4QVmmkhw2)(W zt>(dQh|~D|M!cwAPNjH6bpTVHg8T(vRAXGAW-*u5sW6;;z&cg@@d5Fs#>DC3_au|D z_lUVzr(=TMmt*#ul3jTYURmO-$308UxR3H^o=+9lrwTXP!mxPk%y zfAhG71S?*LZ8bM+WnY)tR&I~pif66s_p_~QCjvYFy39@sKsd=-^u=c5mL>hfqi#a8 z?9+|zj(4rKcqkx*V5QF}GqFd^dxv&mdK%Ssg?092G`x57OFI|4r%!g&-R#;1UCZ7KMRVTVXv=OBIvToPTnnQWRCC1{ zeUsFYyt`gWqC{6srL^rllJnP!V?Rr(Nnd!aUk+u^`G*eS?!#eH|0oRS=hknL+7KC{ogu#qeDi1N%O+IOiC|v(lO7L z|DwB1(q7E>JWv!xQ4~dKk0=O&AP8a@(M8AV2&2XAPFIFHK95`C$t>zaI}BJ9{}tG= z0RCqUsd*6;QVn&qA;F^4)L5~6!lMSs#63Vt#qeB8EZ>k;XAPZ>RQ8tgh1lb0@ID?8 zf?pIE00_YdDyBB3GXo$5BdC}q1cAxODSuL3Ht3k&kzoCp_&h8}gt$<4KI#b@(T2k# z;w^Oh`s4rp`24&yZtlJq${^wZH4Fqm2p0K~;pL2uc{+&J3mgPN5Cp*?3gbAA<2a5- zVTp{Ka#0<=m3aq{xeY$YD@Q3KZ^v|Z@DjuTKnO-qF*5~%93$m^b*kqeK0h4EvkUy5 zOJ2+++k#c$;o8b~JnTtR!Ty7X^L5D1beU(fGNLJJq=Pun)sZK zuXux#zUk2Ko0x8$0e}#UpkihP1cAxODJZENBN;j6>FVqbQvy&(9kkPkMb&LcGN;q4 zgcW;&tZ5{frBP`w*jAKpsRR+SkPKEK9gHaK4Vt8^Q}HS`O-bB%;uImNbE%SvOBgS9 z=~)mrsQ@lwxqxJKfJX*?YXKIlIpV*X=YX4VY%dV8q|f}K$l3^5vMYH4-}v9A!jX1l z3l|TL$fgmA$l5VC_G@rcSKUJg^9X-^lcxsR@HI}QIjigA~k(Eg>5b@dV_EqX{3fbW30X${IwhkIH7QOO>YHpVoT@U+abF*+d7Q|PnoV~&iLYW7JN^aD?s{|$+ z_5VuZw(v?X9UtJj(P+8H~Ek9Nr zQKkgiH#yT#HG2v#dMAh%cMjp^L-akWPWEZJ*RWatB=;zG?bGt54$+U|ug4>2w*!?oKwddp59P*l zB>n%B9#Yw+HF&d7I9+RtAjHaN2oxg6T!}%fljtct_EJd{5JcQiy0^*4ww=<<7t;f9 zl4JQ0G3eu>GsGQ-@W*}S0zW+6*9X!vd@7j)Uu8xpt+y+-cSa#zZE z&)gd&v5jGocI&*lAQP8&r-Cx*6sMTM+&Oi z${l?2_!@vSV^aS0LmdSL+)!{XRAcWwyiXOz2vQ6QPdZePgl7@p5&d5P0000000000 zK-m`vBv^Q2!v+2V2^JPIYzPG2kRe5mz=jL+zPTrzqdMrZu8CBkw>JslsC{tep61M0 zG{jK2WqQ5_KASCJA6x4S;_1au81*PPtj|magRNtVcldD;e21+ zzA~AYK5(EHOP{hG;dqfYan9^?u=>DS_I&SjSjSkmx)1Y*ODU-TEqNChevCfK`isF# z3BEpO-&yam^lUv1q0tg`027?HO23vgdLD1WdB`OF$b3uI!o9-*{&Jo)#=bI7J4m%I za!Eo_0F@nRjGO4Dllb$Hd`X;oNE?f0BK>II^A!iI@bQG8J69@_{2w`M-nzZ5Q+Nr= zZX35f7jz2dW8k+G4v8$?y(v4mNR^V$!L}~GISi4v&0UBRnaerSo(2ITtaB)eO{?-b z$nf-102Q<9kM>!BE%jCieInVflf(dohg-zfu6mis1#Y|ZBvO_SuRW*YXM~wtNM+gC zRRchT31uc1QdxHPv*FbN03u8%8w-AzU#|%ID`Q`oK2`=*CxZw(xKD^2i;opUQ*`Cf zz@L{k%(L>rBJ*qb5}!q`%303~TBda=$fqtBDaq?V`sov-F$m9&FS|;Y=EGd)tT1~V zG{5L_je6^pEivhw&C$AIqd#g-MPKQd6god0l&Y8Fk!UfNPH&2_i794wB#+|;WxoJ! zZeeLa~8Wv(S77XWx#gRhR3uYrDG0XpxdhAXwG4;)-zHm&Nj;}QQgkuycZ^gioYr_JW#O0A!PM%X}w8! z{yKP9H}=57-~}GstFIOwmt`r2`fCAE!owWH7nJFlE0+SR??Uhv;TsjXc$Uxn`Pe50 zeLnYkmc40iM7Md2rZ=>fUsagw=7<4)gwPon_=hhx@jII$q`1Z^X}H29f+TBaJ;L^s zOlGJ%z@C!=PU{?AlDCUgq&v`fc6a~+$2zW>I!JjPijaz(vbclY<~CWG>cAm zU_e2}li~!T{f~XTS#5rMMSX@Xx1G4g9muv((5x^`>>UOmkit5EMUym;&_E%B1_OtL z1p)>D6np93Av1#}LpAk@X16Os1661V#6`Q&%o0Lh_&D6yg#kFMKK&<8aSz>qlIPLs zWE(f9+D*`IBJo?fohecE0V`B|_12XsPtXQ?M~GgHwY{No3w+y+73{S}`%8SjGPR-! zRK2#LuBcWxfvS(R%FFjT4^3>H6;Sc+Y=(%d(*rnv-!t(7_K>ikK_Fp)ME-yl`&Tc; zz^4}dsrt#UjWZ&7b<%0#s7nI?gKs%-;JwGj;h)1u1?b#*;q0R_75msNF%us&)6x6gu&#drE zUzhA`Q%&68DRcejY!8UJ9LZBtDjOW zeofwOx!t>jP0XWcI2l%3|)XVueIq__M1znB8KPxbYdmWZ3*Hy9Wg z7#J8BShInFfq^v`*w1V(-8}ctLt-|}rkR-OdH!m7yP8h?b;CIo)-Y$*=QDeUpwUIR zX?ev|EcPa=`3;2n#OHs$U3OVE#vT7n98wOdUrBfrKYtOs-Q-goz86t1xB$ZLCBQe#&rn?8LuElS!Igd9Q?5xdv_3I9~T06QuiQjA;$+&mzMpIlF)!ia<6^1X6wr-*2HGwH_YDiSh!~GS{6!i_{`@vtq!}+zy zLE;KhlC+X7uUmSP{+`4-H2wOQ(Pf}jgpaz>c!HUAds6kY=L+OQ(pGiUQ3`E1fZ|2A zq6{B(V=YO8Ue)$;FoM!FhIVW&sE4vg(qVIU zE88f&Nm_QuKmdZ@l83;h^39RiX;e{y%iHawblQkyDo0Pkkj8iejG!1!kQ9qu(mLYTPq)1P z{$Ho_{5(Bx4r~x!8`?apqn+OC@?73N(EYiu^m_f=ybl_~Wz+LO6MjZ8q0Hn$D$DM@ zqgA9@Y6xAwsZWjis0(0TWuMF%U9 z^z;lFY}4W-h)r)5Y-avh!axe0#oNg03>(c$i~%Gel7_~VGBTOQiZOA5q*!celL3Gb zjG!13CrFCLmNp$g?vx8dM&)Fi7<*bd1OgB&Qz%txjaG;1bEpNOy8);Xtq#4SvZGH4 zf@N}r4%Lsd0Si3BYgwUV!@qGkh&|jb#5l2zBFj9FgwAaNBg+oouZ;}et+EW?uX;GS ztbv%!SG;i3Y%;vZI^XLSiwZ-nd1zefDJLq_5XM_Fd;4M4?~yY_tZ%JHa7u$^1X9_W ziU@GKE{DZ20=&X}gVPBLt%((Zky*`dRh-nR@~>Li&E8u{E1ZnMF=A zq*66fmPTqC_?jtkxt#_!1c?!!i(8wo=_PKJMcn6iMpR8znUGe{3GD5<2h@O7k)1|e zRfbh40@YgFm-}0?eR>77pta8UvTEnz2Qu4AA=YeK1Z*_fvGbd_+4*jS78;zBS-WYI zo|eX&TuVwcpSn`JP~K zz2@fgNYa&PYgFg8E}q#J{5{3|*6Tcyfa~YaW2_}#L(aCUy-l+U;V#kwW!Ul zzhtb<_aB|5#xG6qbw##Db`LwyG;tp!ON*N?H@_21PF}t&r(Nd*vCFZbBa*xcObjYV zl!?5u7dtQ_cVLrJP;B;na)GQk@Z2Os-=NDo|IxT>ciH&twY^k@?gF{Ja3~u0&%J*D zjZlm53tr9s)?EFi-C9%1f;3+EtB;0Sg7}G-TaBL|%5^6rv8A|ohN6q=mt_DAA)*`i UMQ*lvlk>j@mv~dN-b!vFvP literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/inter.css b/xhiveframework/public/css/fonts/inter/inter.css new file mode 100644 index 0000000..c530373 --- /dev/null +++ b/xhiveframework/public/css/fonts/inter/inter.css @@ -0,0 +1,33 @@ +@font-face { + font-family: InterVariable; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("/assets/xhiveframework/css/fonts/inter/InterVariable.woff2") format("woff2"); + } + @font-face { + font-family: InterVariable; + font-style: italic; + font-weight: 100 900; + font-display: swap; + src: url("/assets/xhiveframework/css/fonts/inter/InterVariable-Italic.woff2") format("woff2"); + } + /* static fonts */ + @font-face { font-family: "Inter"; font-style: normal; font-weight: 100; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Thin.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 100; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-ThinItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 200; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-ExtraLight.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 200; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-ExtraLightItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 300; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Light.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 300; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-LightItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Regular.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 400; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Italic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Medium.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 500; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-MediumItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-SemiBold.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 600; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-SemiBoldItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Bold.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 700; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-BoldItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 800; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-ExtraBold.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 800; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-ExtraBoldItalic.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: normal; font-weight: 900; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-Black.woff2") format("woff2"); } + @font-face { font-family: "Inter"; font-style: italic; font-weight: 900; font-display: swap; src: url("/assets/xhiveframework/css/fonts/inter/Inter-BlackItalic.woff2") format("woff2"); } diff --git a/xhiveframework/public/css/fonts/inter/inter.scss b/xhiveframework/public/css/fonts/inter/inter.scss new file mode 100644 index 0000000..ddb4282 --- /dev/null +++ b/xhiveframework/public/css/fonts/inter/inter.scss @@ -0,0 +1 @@ +@import "xhiveframework/public/css/fonts/inter/inter.css"; diff --git a/xhiveframework/public/css/hljs-night-owl.css b/xhiveframework/public/css/hljs-night-owl.css new file mode 100644 index 0000000..932ad2e --- /dev/null +++ b/xhiveframework/public/css/hljs-night-owl.css @@ -0,0 +1,183 @@ +/* + +Night Owl for highlight.js (c) Carl Baxter + +An adaptation of Sarah Drasner's Night Owl VS Code Theme +https://github.com/sdras/night-owl-vscode-theme + +Copyright (c) 2018 Sarah Drasner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 1rem 1.25rem; + background: #011627; + color: #d6deeb; + border-radius: 0.5rem; + } + + /* General Purpose */ + .hljs-keyword { + color: #c792ea; + font-style: italic; + } + .hljs-built_in { + color: #addb67; + font-style: italic; + } + .hljs-type { + color: #82aaff; + } + .hljs-literal { + color: #ff5874; + } + .hljs-number { + color: #F78C6C; + } + .hljs-regexp { + color: #5ca7e4; + } + .hljs-string { + color: #ecc48d; + } + .hljs-subst { + color: #d3423e; + } + .hljs-symbol { + color: #82aaff; + } + .hljs-class { + color: #ffcb8b; + } + .hljs-function { + color: #82AAFF; + } + .hljs-title { + color: #DCDCAA; + font-style: italic; + } + .hljs-params { + color: #7fdbca; + } + + /* Meta */ + .hljs-comment { + color: #637777; + font-style: italic; + } + .hljs-doctag { + color: #7fdbca; + } + .hljs-meta { + color: #82aaff; + } + .hljs-meta-keyword { + color: #82aaff; + } + .hljs-meta-string { + color: #ecc48d; + } + + /* Tags, attributes, config */ + .hljs-section { + color: #82b1ff; + } + .hljs-tag, + .hljs-name, + .hljs-builtin-name { + color: #7fdbca; + } + .hljs-attr { + color: #7fdbca; + } + .hljs-attribute { + color: #80cbc4; + } + .hljs-variable { + color: #addb67; + } + + /* Markup */ + .hljs-bullet { + color: #d9f5dd; + } + .hljs-code { + color: #80CBC4; + } + .hljs-emphasis { + color: #c792ea; + font-style: italic; + } + .hljs-strong { + color: #addb67; + font-weight: bold; + } + .hljs-formula { + color: #c792ea; + } + .hljs-link { + color: #ff869a; + } + .hljs-quote { + color: #697098; + font-style: italic; + } + + /* CSS */ + .hljs-selector-tag { + color: #ff6363; + } + + .hljs-selector-id { + color: #fad430; + } + + .hljs-selector-class { + color: #addb67; + font-style: italic; + } + + .hljs-selector-attr, + .hljs-selector-pseudo { + color: #c792ea; + font-style: italic; + } + + /* Templates */ + .hljs-template-tag { + color: #c792ea; + } + .hljs-template-variable { + color: #addb67; + } + + /* diff */ + .hljs-addition { + color: #addb67ff; + font-style: italic; + } + + .hljs-deletion { + color: #EF535090; + font-style: italic; + } diff --git a/xhiveframework/public/css/octicons/LICENSE.txt b/xhiveframework/public/css/octicons/LICENSE.txt new file mode 100755 index 0000000..259b43d --- /dev/null +++ b/xhiveframework/public/css/octicons/LICENSE.txt @@ -0,0 +1,9 @@ +(c) 2012-2014 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files diff --git a/xhiveframework/public/css/octicons/README.md b/xhiveframework/public/css/octicons/README.md new file mode 100755 index 0000000..1007073 --- /dev/null +++ b/xhiveframework/public/css/octicons/README.md @@ -0,0 +1 @@ +If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts. diff --git a/xhiveframework/public/css/octicons/octicons-local.ttf b/xhiveframework/public/css/octicons/octicons-local.ttf new file mode 100755 index 0000000000000000000000000000000000000000..2050d5e8abf11c25b134e7b821eee52443005ef6 GIT binary patch literal 50856 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ɝiDa*k}; z@`tLBBQw$6ur2_Bs{2|7^U&7h9hz!>@fGsW=znO+=imCk^IRRT?J%utsNErzg1Q1yH>gX> z!0*jI@E|)}Bi-yU5{nKgRv;q)0NXIEDS;;mf2{EA6W@upDGjBvBU%c3foDNuvk7B? z4})D3b8|A3@H1w^rY!CIwOfrosa%7<{~k08!Swlj%Y!CtTN=PB1tqICe&Oo^mnmmXgWXO~4>&tpV$(%mAo{qYs0oFR3NqsD{gJ?%a z9G^m7I1qI!DmBCN#GnA2@Wot#uq-Q)&wMRzE@r{#*k6YU#5xbovQi(5)raCad!b_E z0ipvNq3#O9B7S3ekTvI~aQSEQt)gx7_nb+=@{00sQx_UAP`fT#sL?phS==c1!Mbg3 zGz>V3a<(VXkSB=cw;jab^;vZab022-&!g?_eeLZ( zG{Gf6#AmxRPqz`@>}&rvXf@ZCkf#x;5vyhJI5G`&r;-r|7Dhoa8CC%!@J2;4z(Y)< z9X_h0Bjv+11v623ehw`xN%JuK^?8AKbDTrSftENg*ctRvK_B8s;W_jd6vJJcD@bbw zd$4dozd#q0tqlF#Vy#$3RC-N@KkC-z`}^lLX3ejGDd?B9e!~bG{rxx?(%HPJ0dl_y zD|NYb=OB2~89yQ%VZ2(9$_QTM4+cso4qX%-iQB1!SSN%@1I%Q2Sm;`vMRL!hkM04W zvek!WTLgrrxv(ouwc$M`e2P5i*~pI54Y`4ahqc5v!?auNQyW~ST6EF?(@Rn`HC-v`Rd zo?;gMls69IP2tjf$s3n{pVACK_$hXWJXKZ}_&tTgxTf;5GE1fyz<0u2X;`~EC>@qA zmHN>ZYwl_2W5VNS#bdBa?@*y!1knqJKP6VOQOqlRV?v&$5C9>BEvO{L?601%M3hXV z;qigy#us(h)GqGe)d>(7j+hS{ z&xg=$?9enyU{OGrvy_T6kIwyU(rE}K(0Klb5D4Qm=o`J_B?vOmLutu?N6r9Gl3?zW zj+?sB8koYlA>32o4a{5U47;e)k7M7Y&t@5y? z*!NkhLgN}Y%e+<>{LcxkB@8^UxSlsvDBW0(0v2HEvBW0Jm@X$t_j zpG-@cfCK-RFlxTU`92_zh82dl$ZtM&?9H+oYCzD>wGSu_A@xu4+h>lPAp1*? zKKhcKP8^weyKJYjr=Nz_&U0B%AFg!6YQ@lNVmC3*!FRkR6h0C+RZ&L-hpcEcJpORUpB-%S4pgC35}*A2o95icEW#|0P80S z=+BmHRl2vI9J@tt;mZi~-02QE+EN_=zo zYRnSPi-dV`3;kR`IuorSEKvfMXT`+8y)B$hXdXUp8S((Snql1a+#-f8bQi_~@Mn#0 zi7b%52DT|QKE^g7Hqg&khr^(C^h40E2l&m}L>PAaI%$*CC+!3rBHR

      }0iWEyMyI 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 R_-J-)s5D1yxd)&D{eMr5>=Xb1 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/octicons/octicons.css b/xhiveframework/public/css/octicons/octicons.css new file mode 100755 index 0000000..a7f7b1a --- /dev/null +++ b/xhiveframework/public/css/octicons/octicons.css @@ -0,0 +1,221 @@ +@font-face { + font-family: 'octicons'; + src: url('/assets/xhiveframework/css/octicons/octicons.eot?#iefix') format('embedded-opentype'), + url('/assets/xhiveframework/css/octicons/octicons.woff') format('woff'), + url('/assets/xhiveframework/css/octicons/octicons.ttf') format('truetype'), + url('/assets/xhiveframework/css/octicons/octicons.svg#octicons') format('svg'); + font-weight: normal; + font-style: normal; +} + +/* + +.octicon is optimized for 16px. +.mega-octicon is optimized for 32px but can be used larger. + +*/ +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-microscope:before, +.octicon-beaker:before { content: '\f0dd'} /*  */ +.octicon-bell:before { content: '\f0de'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-clone:before, +.octicon-desktop-download:before { content: '\f0dc'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /*  */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-shield:before { content: '\f0e1'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-thumbsdown:before { content: '\f0db'} /*  */ +.octicon-thumbsup:before { content: '\f0da'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-watch:before { content: '\f0e0'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/xhiveframework/public/css/octicons/octicons.eot b/xhiveframework/public/css/octicons/octicons.eot new file mode 100755 index 0000000000000000000000000000000000000000..2bf20bca0030960c53bee269c5ff742fb6f49a51 GIT binary patch literal 29160 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/octicons/octicons.less b/xhiveframework/public/css/octicons/octicons.less new file mode 100755 index 0000000..d1d751e --- /dev/null +++ b/xhiveframework/public/css/octicons/octicons.less @@ -0,0 +1,220 @@ +@octicons-font-path: "."; +@octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; + +@font-face { + font-family: 'octicons'; + src: ~"url('@{octicons-font-path}/octicons.eot?#iefix&v=@{octicons-version}') format('embedded-opentype')", + ~"url('@{octicons-font-path}/octicons.woff?v=@{octicons-version}') format('woff')", + ~"url('@{octicons-font-path}/octicons.ttf?v=@{octicons-version}') format('truetype')", + ~"url('@{octicons-font-path}/octicons.svg?v=@{octicons-version}#octicons') format('svg')"; + font-weight: normal; + font-style: normal; +} + +// .octicon is optimized for 16px. +// .mega-octicon is optimized for 32px but can be used larger. +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-microscope:before, +.octicon-beaker:before { content: '\f0dd'} /*  */ +.octicon-bell:before { content: '\f0de'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-clone:before, +.octicon-desktop-download:before { content: '\f0dc'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /*  */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-shield:before { content: '\f0e1'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-thumbsdown:before { content: '\f0db'} /*  */ +.octicon-thumbsup:before { content: '\f0da'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-watch:before { content: '\f0e0'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/xhiveframework/public/css/octicons/octicons.scss b/xhiveframework/public/css/octicons/octicons.scss new file mode 100755 index 0000000..81e7d52 --- /dev/null +++ b/xhiveframework/public/css/octicons/octicons.scss @@ -0,0 +1,548 @@ +$octicons-font-path: "." !default; +$octicons-version: "396334ee3da78f4302d25c758ae3e3ce5dc3c97d"; + +@font-face { + font-family: "octicons"; + src: url("#{$octicons-font-path}/octicons.eot?#iefix&v=#{$octicons-version}") + format("embedded-opentype"), + url("#{$octicons-font-path}/octicons.woff?v=#{$octicons-version}") format("woff"), + url("#{$octicons-font-path}/octicons.ttf?v=#{$octicons-version}") format("truetype"), + url("#{$octicons-font-path}/octicons.svg?v=#{$octicons-version}#octicons") format("svg"); + font-weight: normal; + font-style: normal; +} + +// .octicon is optimized for 16px. +// .mega-octicon is optimized for 32px but can be used larger. +.octicon, +.mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { + font-size: 32px; +} + +.octicon-alert:before { + content: "\f02d"; +} /*  */ +.octicon-arrow-down:before { + content: "\f03f"; +} /*  */ +.octicon-arrow-left:before { + content: "\f040"; +} /*  */ +.octicon-arrow-right:before { + content: "\f03e"; +} /*  */ +.octicon-arrow-small-down:before { + content: "\f0a0"; +} /*  */ +.octicon-arrow-small-left:before { + content: "\f0a1"; +} /*  */ +.octicon-arrow-small-right:before { + content: "\f071"; +} /*  */ +.octicon-arrow-small-up:before { + content: "\f09f"; +} /*  */ +.octicon-arrow-up:before { + content: "\f03d"; +} /*  */ +.octicon-microscope:before, +.octicon-beaker:before { + content: "\f0dd"; +} /*  */ +.octicon-bell:before { + content: "\f0de"; +} /*  */ +.octicon-book:before { + content: "\f007"; +} /*  */ +.octicon-bookmark:before { + content: "\f07b"; +} /*  */ +.octicon-briefcase:before { + content: "\f0d3"; +} /*  */ +.octicon-broadcast:before { + content: "\f048"; +} /*  */ +.octicon-browser:before { + content: "\f0c5"; +} /*  */ +.octicon-bug:before { + content: "\f091"; +} /*  */ +.octicon-calendar:before { + content: "\f068"; +} /*  */ +.octicon-check:before { + content: "\f03a"; +} /*  */ +.octicon-checklist:before { + content: "\f076"; +} /*  */ +.octicon-chevron-down:before { + content: "\f0a3"; +} /*  */ +.octicon-chevron-left:before { + content: "\f0a4"; +} /*  */ +.octicon-chevron-right:before { + content: "\f078"; +} /*  */ +.octicon-chevron-up:before { + content: "\f0a2"; +} /*  */ +.octicon-circle-slash:before { + content: "\f084"; +} /*  */ +.octicon-circuit-board:before { + content: "\f0d6"; +} /*  */ +.octicon-clippy:before { + content: "\f035"; +} /*  */ +.octicon-clock:before { + content: "\f046"; +} /*  */ +.octicon-cloud-download:before { + content: "\f00b"; +} /*  */ +.octicon-cloud-upload:before { + content: "\f00c"; +} /*  */ +.octicon-code:before { + content: "\f05f"; +} /*  */ +.octicon-color-mode:before { + content: "\f065"; +} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { + content: "\f02b"; +} /*  */ +.octicon-comment-discussion:before { + content: "\f04f"; +} /*  */ +.octicon-credit-card:before { + content: "\f045"; +} /*  */ +.octicon-dash:before { + content: "\f0ca"; +} /*  */ +.octicon-dashboard:before { + content: "\f07d"; +} /*  */ +.octicon-database:before { + content: "\f096"; +} /*  */ +.octicon-clone:before, +.octicon-desktop-download:before { + content: "\f0dc"; +} /*  */ +.octicon-device-camera:before { + content: "\f056"; +} /*  */ +.octicon-device-camera-video:before { + content: "\f057"; +} /*  */ +.octicon-device-desktop:before { + content: "\f27c"; +} /*  */ +.octicon-device-mobile:before { + content: "\f038"; +} /*  */ +.octicon-diff:before { + content: "\f04d"; +} /*  */ +.octicon-diff-added:before { + content: "\f06b"; +} /*  */ +.octicon-diff-ignored:before { + content: "\f099"; +} /*  */ +.octicon-diff-modified:before { + content: "\f06d"; +} /*  */ +.octicon-diff-removed:before { + content: "\f06c"; +} /*  */ +.octicon-diff-renamed:before { + content: "\f06e"; +} /*  */ +.octicon-ellipsis:before { + content: "\f09a"; +} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { + content: "\f04e"; +} /*  */ +.octicon-file-binary:before { + content: "\f094"; +} /*  */ +.octicon-file-code:before { + content: "\f010"; +} /*  */ +.octicon-file-directory:before { + content: "\f016"; +} /*  */ +.octicon-file-media:before { + content: "\f012"; +} /*  */ +.octicon-file-pdf:before { + content: "\f014"; +} /*  */ +.octicon-file-submodule:before { + content: "\f017"; +} /*  */ +.octicon-file-symlink-directory:before { + content: "\f0b1"; +} /*  */ +.octicon-file-symlink-file:before { + content: "\f0b0"; +} /*  */ +.octicon-file-text:before { + content: "\f011"; +} /*  */ +.octicon-file-zip:before { + content: "\f013"; +} /*  */ +.octicon-flame:before { + content: "\f0d2"; +} /*  */ +.octicon-fold:before { + content: "\f0cc"; +} /*  */ +.octicon-gear:before { + content: "\f02f"; +} /*  */ +.octicon-gift:before { + content: "\f042"; +} /*  */ +.octicon-gist:before { + content: "\f00e"; +} /*  */ +.octicon-gist-secret:before { + content: "\f08c"; +} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { + content: "\f020"; +} /*  */ +.octicon-git-commit:before { + content: "\f01f"; +} /*  */ +.octicon-git-compare:before { + content: "\f0ac"; +} /*  */ +.octicon-git-merge:before { + content: "\f023"; +} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { + content: "\f009"; +} /*  */ +.octicon-globe:before { + content: "\f0b6"; +} /*  */ +.octicon-graph:before { + content: "\f043"; +} /*  */ +.octicon-heart:before { + content: "\2665"; +} /* ♥ */ +.octicon-history:before { + content: "\f07e"; +} /*  */ +.octicon-home:before { + content: "\f08d"; +} /*  */ +.octicon-horizontal-rule:before { + content: "\f070"; +} /*  */ +.octicon-hubot:before { + content: "\f09d"; +} /*  */ +.octicon-inbox:before { + content: "\f0cf"; +} /*  */ +.octicon-info:before { + content: "\f059"; +} /*  */ +.octicon-issue-closed:before { + content: "\f028"; +} /*  */ +.octicon-issue-opened:before { + content: "\f026"; +} /*  */ +.octicon-issue-reopened:before { + content: "\f027"; +} /*  */ +.octicon-jersey:before { + content: "\f019"; +} /*  */ +.octicon-key:before { + content: "\f049"; +} /*  */ +.octicon-keyboard:before { + content: "\f00d"; +} /*  */ +.octicon-law:before { + content: "\f0d8"; +} /*  */ +.octicon-light-bulb:before { + content: "\f000"; +} /*  */ +.octicon-link:before { + content: "\f05c"; +} /*  */ +.octicon-link-external:before { + content: "\f07f"; +} /*  */ +.octicon-list-ordered:before { + content: "\f062"; +} /*  */ +.octicon-list-unordered:before { + content: "\f061"; +} /*  */ +.octicon-location:before { + content: "\f060"; +} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { + content: "\f06a"; +} /*  */ +.octicon-logo-github:before { + content: "\f092"; +} /*  */ +.octicon-mail:before { + content: "\f03b"; +} /*  */ +.octicon-mail-read:before { + content: "\f03c"; +} /*  */ +.octicon-mail-reply:before { + content: "\f051"; +} /*  */ +.octicon-mark-github:before { + content: "\f00a"; +} /*  */ +.octicon-markdown:before { + content: "\f0c9"; +} /*  */ +.octicon-megaphone:before { + content: "\f077"; +} /*  */ +.octicon-mention:before { + content: "\f0be"; +} /*  */ +.octicon-milestone:before { + content: "\f075"; +} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { + content: "\f024"; +} /*  */ +.octicon-mortar-board:before { + content: "\f0d7"; +} /*  */ +.octicon-mute:before { + content: "\f080"; +} /*  */ +.octicon-no-newline:before { + content: "\f09c"; +} /*  */ +.octicon-octoface:before { + content: "\f008"; +} /*  */ +.octicon-organization:before { + content: "\f037"; +} /*  */ +.octicon-package:before { + content: "\f0c4"; +} /*  */ +.octicon-paintcan:before { + content: "\f0d1"; +} /*  */ +.octicon-pencil:before { + content: "\f058"; +} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { + content: "\f018"; +} /*  */ +.octicon-pin:before { + content: "\f041"; +} /*  */ +.octicon-plug:before { + content: "\f0d4"; +} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { + content: "\f05d"; +} /*  */ +.octicon-primitive-dot:before { + content: "\f052"; +} /*  */ +.octicon-primitive-square:before { + content: "\f053"; +} /*  */ +.octicon-pulse:before { + content: "\f085"; +} /*  */ +.octicon-question:before { + content: "\f02c"; +} /*  */ +.octicon-quote:before { + content: "\f063"; +} /*  */ +.octicon-radio-tower:before { + content: "\f030"; +} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { + content: "\f001"; +} /*  */ +.octicon-repo-clone:before { + content: "\f04c"; +} /*  */ +.octicon-repo-force-push:before { + content: "\f04a"; +} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { + content: "\f002"; +} /*  */ +.octicon-repo-pull:before { + content: "\f006"; +} /*  */ +.octicon-repo-push:before { + content: "\f005"; +} /*  */ +.octicon-rocket:before { + content: "\f033"; +} /*  */ +.octicon-rss:before { + content: "\f034"; +} /*  */ +.octicon-ruby:before { + content: "\f047"; +} /*  */ +.octicon-screen-full:before { + content: "\f066"; +} /*  */ +.octicon-screen-normal:before { + content: "\f067"; +} /*  */ +.octicon-search-save:before, +.octicon-search:before { + content: "\f02e"; +} /*  */ +.octicon-server:before { + content: "\f097"; +} /*  */ +.octicon-settings:before { + content: "\f07c"; +} /*  */ +.octicon-shield:before { + content: "\f0e1"; +} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { + content: "\f036"; +} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { + content: "\f032"; +} /*  */ +.octicon-squirrel:before { + content: "\f0b2"; +} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { + content: "\f02a"; +} /*  */ +.octicon-stop:before { + content: "\f08f"; +} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { + content: "\f087"; +} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { + content: "\f015"; +} /*  */ +.octicon-telescope:before { + content: "\f088"; +} /*  */ +.octicon-terminal:before { + content: "\f0c8"; +} /*  */ +.octicon-three-bars:before { + content: "\f05e"; +} /*  */ +.octicon-thumbsdown:before { + content: "\f0db"; +} /*  */ +.octicon-thumbsup:before { + content: "\f0da"; +} /*  */ +.octicon-tools:before { + content: "\f031"; +} /*  */ +.octicon-trashcan:before { + content: "\f0d0"; +} /*  */ +.octicon-triangle-down:before { + content: "\f05b"; +} /*  */ +.octicon-triangle-left:before { + content: "\f044"; +} /*  */ +.octicon-triangle-right:before { + content: "\f05a"; +} /*  */ +.octicon-triangle-up:before { + content: "\f0aa"; +} /*  */ +.octicon-unfold:before { + content: "\f039"; +} /*  */ +.octicon-unmute:before { + content: "\f0ba"; +} /*  */ +.octicon-versions:before { + content: "\f064"; +} /*  */ +.octicon-watch:before { + content: "\f0e0"; +} /*  */ +.octicon-remove-close:before, +.octicon-x:before { + content: "\f081"; +} /*  */ +.octicon-zap:before { + content: "\26A1"; +} /* ⚡ */ diff --git a/xhiveframework/public/css/octicons/octicons.svg b/xhiveframework/public/css/octicons/octicons.svg new file mode 100755 index 0000000..d932988 --- /dev/null +++ b/xhiveframework/public/css/octicons/octicons.svg @@ -0,0 +1,183 @@ + + + + +(c) 2012-2015 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xhiveframework/public/css/octicons/octicons.ttf b/xhiveframework/public/css/octicons/octicons.ttf new file mode 100755 index 0000000000000000000000000000000000000000..32e6720a733413cec27e048df79d853dff41d728 GIT binary patch literal 28992 zcmdVDdwd&LnKyo(8EJI!MV4eqwq?tbb#r`=WlL$2#*;R2b1Ml=Y18y($Ce#8j_vx= zHYwXqDWyOQ1Zdep3z&wozz0~M3-5BD0`JoDwrpWppxxbKZe8|ew`&UJ-Ag_BeV;Ru zqqOO}yzl4z=ciaRXU?2C=Q+=L?&ldrkPyNv455nn{)4^!Z~Xg%`-Px()Lt=me00wJ zwc67{II4wE-ZVLVV&a1zy7Hq!xPBtUHGM~mqvN$LOP8YU)A;6(qN4V3?+Yk@3g6Vx z$b3UhK4Qju=`r~(A@7wy4sB*{# z-+!?I;|Rg$Grv7ERJB391eb21$lCw`=hNaD5ti3n>%~6|Bd>8^Jxr&BQ@EA4DNmyA zUG|xNBl;P7ML5+`%U7HZOza#NdZzf|8*SJz?qg_Df}Ek_2NlCUSC;u+`OEd?a{0`4 z=lybDr7urpz07sE=I6_Gm1VA5@4KR0u9GmLzWnU^bwz(%SNSdX&u6;*?AYZ#xi1Ac zu52gE+#dA|e`Q;4&;6`FgZmN(sISDu`CyZ9b9-)E@m0_tQ)u}63l-evDG=lTxLKjX^FGQx8`N*oWTD#h~8edJVwy*A4-M@Np z_2}x%>W!PJ^Ux%#Qq&#!)Q^~sDJ=ldGB4j@5muuV1}l^@i2C)uq*2SKqvP z_v*dq_1)<8eXH+ZeQ5Q=ve(a+dj0n5oX5MGg_zCQJwJpr|;i= z|K|H&zCZY0^8L>DqVKo97kvNh`=7pl@;&eSmG76nU-*9R`66G)QP(Kx&w8esx#|;SoiC?m;GJ-Z~K4k z|3hFPFcY{h*d81Sezd-!e!TvZ_5U1lgkqtt&|5-Z3_Ta_59{IAg+CL1JpA*Bhzv*O zB43O=*U;5)dBX=9UTl20@efU1P1iI%(DY?nP_>a z<%=yZ#_D1RVh_iD*&1#gZvA-thl!5FskV-`Yuet~_M_ycm!MNAmrV zZ4?S?t{6pXiI1-Oa3LQkWFQfrwSMYU%?E>Zbo3H6n5I~ZGt-_v)Zp+}AHLT?(MHW3 z+_8Jxo44)W5p-*fQF6TFs@i&@y5NJR>a>2U>Nq_6DeL9D>aV)KZrIfsr-+xdF10{4 zjcs0S@>Iu;sCQRh_4NAvy&g5c%NyO%acWYl?oHiW-Glhv|Gg;-Sjo0)HH*Qp@zhTG5tH%`bM++3A)|MO zv@_AJUThSb(ALHs&=*P!0=W{uLCwYBg^~jdsRS4G{V^p}OO(K^9-@)^zvH0x$j~Ma z>7GqPk#=%?=kkW&hE5NeJ z*YjhOndzRlKWe2m#I_&GJ2lOjKeRozA$2%${FIvAeQhpr*xIt`5~520yNh901z=ZE zt`a_x5?!JXb4U*629u%nZ`uzK6QQhYFbVMe06h^SHGu}XP{NOYB=pip5tNEr=E#T< zO%aL;&O>X4VUoUPDmoe(x`}_rnm_{s?a)Z&GNsmvh7NevOuz=EbjASwh3FP}F(h`` zn5_>-Lar?EI%uyrUq^sM&@-sFFp$X(7VNvu9vaB^hwHWAs|t!J?4o3gVYDP^S3#D| zR}`(?@vbhtt84k36BOdELh1tlWrlH?|AG{rw6EvQ`SV>qN!*3rwRU@{pp*;t{4m!N z?rTMljdd4r?o@Mr{0lN&_%W}wjNPrs@b2wlm8VopAXRhK)_e z=oz7y_Svp8cqYrbZY|q%P9vD#NHk?p+*)S&U@gbVP;7E9FO|7wqBRci)a2;0x=#PZ(H0+gWPJRQfq0wa zC)QUJot+7Mi4Tc*SEtfZcjG~4?VcL9yJk0?*t)0ZN+?L^y0b!EN8YXnJp1{@8Vk3!OH6PJhsLZk;SB%nVz=!y*H zKsk!ft8OG%O4#)kbNxB((zsGU^q`L6F8`Xo(Hw0_1 z(hlyNP_(N9m#PETjP^MW&dn_zXZ$mK=2`eCS%@V4N%}v5k4y<4>3<}E5W}WrD*7J{ zql`aD7%OF1RJ4jDq%lk4Ay{;mh`nOJI4G_ZSBvY#7)T4++6g4Gc(z6KU@{GCvlQlU z0biEHwiXAql@z#(#T@Ec)3e?QLOSwuvagcz@1#&JfjY&2sM8_2@I5082sXojeoAfEBJ+(F6K7-n&og&Ar5dqO4Via~km=;uAe0kYiUXG89z|1HqX@+TJZCMrO?V}_5GCo72Bj7Mw zjtWfIw%b}n8;{G{&DL^K)0O6GuXj*tHKU}%V7x*_$**GYXbcL@zzM1qOq`d2aZJl= z%l18KrLhUj{KR&zA?K|Mzi7nhFip;-Fdi{Vg$P?B(1@=b#00A=!gzJ}I$>tdG*3YoLTYwl){$P?Vl~=C-CWB(S^3MuIBMB^CYg)80{S(bgg2;pJ^sjuw`N;{qFqDH7arfH z?4=WH%bK%3+BF;tN4rLPJFkt`dIQ}}>5lO0yQ)>ik%&BOzG8OEYA}?U-8%euz|j(= z^z?w+)6k?mWZj}@;nZTa+f(g#h8k7n#;O`}YQysft_5^hD#7${QXK|I1f{$ZP}!Y? zNrj-aDcoP6{6IFHxsd0T>yLO8ED!y8WOMvOI}R%i5qfide?4V_!L@XZHg|rgBSvqt zzHco<(!4v;pd8-uq4?&A^~PY3GHBQx>-bRTW{O$2Sl?IPWng$A<-2Og@gqzfgZbn+ z81JDpctcvF+by$#qA6viEt7NwD_TLMieToH{B6p9-J)7jANuwunC22Lo8it5lq*Y_ z99E5oQy}o9r3YqW&LaTO#V#D*|>DwmY&o%eFqngbphB|y@h=3|0heq*;r2FmHreZ!GzTHTof;UadCl2h8sdIli%R zr1vrBhHGHBDJBfSt3!Qj`jy=dopxMz-40b(f}UKjb@mZ6Zkh2o={S*i`pwggnM~uf z^>5W{{4jT-SHF3!PZ53Qsl9VfWnu4I4{W$T&dt}KlC%c~z(PY3wqPx6Ttll$bi>|( zFS?F#<-bMly87z7B2PR~I$QeI{fe@G>(&EA2gtbm21U8y^784a@{O%q_sexRq)$9p zww})##<2bo2?3_gdd6pM&*cj^CASfHj1fJMU-ymCk8No~81;L_m_vV2qeE;5Wa@W2l&X z>ViP-SVww$Fx*te!&ww;zpP`TCs6YHt#ApllGi{hB@7tJ7*Rala>a%~F1C(^iWvhZ z|9J6+dPK)!cKpG*qlC1w!(g3_6R=#>;S=hxY9b;5rKrF`@_WcXkWtex-JE_xw1Aw6 z!H5gGKU+{&EZx#;1GGXbfgWfzW==3Pn@-SjrfI@4>9+pH)97Ctk&f-Mzj5t-Pain& zw4#%+Rw#Z(j2O}sH*K0z4S>Pp+jf+*OT3;{#=^!lv_zjtN%78-m7CY@0+9@{o8>`M^(W&1UeWF?7}9w;QWl8+eiQ?A5X z^{!mLL;+d9B6uu=?!SK|ci&ZjM7x~9xZ(5LCSLvx>h3>uP8~#-ZV6*#`(6TciPqKx z0H#BiPOwaSbuBh5BrRfHSo67{=dK=Xse_+o+ z#+zq^ji;;b%Wc{A=nd=W8g2WUX(+-nSI{o5uUH3dywTq6TQ0ql))6;)(|rOT0-cz0 zoe&A3bQL)xYTBu)hV`Ah@3y}4{1ysPXbYJeC}RE7hVNV7zyE&f#dEATN2GoWNGqOEkkFp9?%U_6^c{|0#z$3sAR>t_87Ye{IPk~ka&to z+fCvLOqhirf+>){5YdWG@No-qMU1kqL0-~~6z(~pYcm8WO3hm=M z2AylBK_lZsDj<`M1GRDZ^>oFzUxNY}UgkGb{a@-3WO;}&lEy+p$dFbK0g~uPY$YwD zqoBzbtUaDsTg+qaar;{W>U5yRZ|&iwjVot-e!p*xd~GqmKi1X@{zKo4>mf-q4TygT zC+MR@u$VGjU}!cR^8@L1(y1l&Uw+?~c06VpZu`C0B>*GHu`*n33Ur^=uJ0S!dxko0Mf1qfDDt4?NauF0CF` zCx^mlV_R0u6%vu0s<|Rb7yPiGY!s?L+bEl9qg-`$Yio65ye5iIP5j+8iR$j|?yj0d zV|5}PPgK)L*A}8JUGhlZU$1XW)O2-sTfOb+C$2A$$Y1|Ns-2A}L*G|jUFWOllD<@v zs7YMWR^8TE-7>sqXG=BBS6BBgoLK0sZfvV*yZoxlakT!bJO~f9v!+SjIhd@$Q@sl( z7p(j1*ZvV+b3?H8c?_d`2c6bsL!AjUsA(SFGu&J))HDAsX>B{CcAJbYhg-4Qg={90 z?@@TQvoNR->~xi>E*s2JgnSekBAi?6TH8{aro(|6U-Ol7o8A~dd8@Cvv*%{|(fxn$ zWwIH>nht)@>c7DH&Cpemcf={QiDv5(sr2TBP{6x+@o;u#7c6%7MyKwx>hOub zA))-)!&Y*O^^3N5H(YZOwZMy$G^tDdrm=P-8EJOH#7=W88OtgK5*FV%DCJR76=g&E z3`&a7Aq`|$6?;?H5=$ZQ8h8+ftpf36+LCImJs9(R#zb2%BJ+XnAay&U#vK?F#Dal9 zN_>K(#$VEvE8ql+v)~F92H}!PB6En$#vOazlNasyePhg3m>q1}9EL(Qf?cG$@9i?o zOS_YU2V2{=sXD!7(!KBE{$TJAjTa_c4-O`;N|;91y>6Ylf&k0R#S(*ac^BdZEOP)9 zEIUZcYTjYbC1I_op|FVLkX#~-`W}eC!mit2qMe%Vvi{Used`xg-SG^pHD$N1SzmdH z>S*fUb@dBxJ^c)K$9p@>>pFUOiCS=1SPX)@L;-Pcj)6xw;F=iZfRWsE$gu&L00P*D zVM_@zNycMkWS{s>vPW&JRb0th_zS!WI=fAn3t|`?npj>|V1%D_^G(}=;uS!U1K|`? z5xA{KQ%Wk2FPHEDv_ASUB)^n)ETS^dV%tu50yYbtHRMX>Q+O7-hv$f;#Qqijfx-pB z$tmqkKzKh>QamQhD_!C>j=i!?VyC!K+$`=CZvzBUe3#A4tRyPnR7OChfKP8DV3aNIY`#*< z$Wl6^v^(nGQre=* z#_Ed0!^OH-wq%cPq3gWKFk79Sq5Z8g(M_#HawC@Q1YmW7%FJ zpZQweTq{D*al8%-hXAiiNM!L!lbj1N^U>v0vJ(mAi=Qkj3~%-}=hP z$coOQ`BgXt|B^mpnsIYv1P4=`E1TM&_dBptSK4=uKsKF?BO(ySy9K#|ltrOPxQ5cO zMFB|KNj1baAzT_zCgZ~<*QO$hmp=OFr9f1%X>B{3@W3JV0LocGm5VPY71XlK8sf=r#0XeJE4#`ECfx+4+VJO+4Dyp&$i<& z@=*CcgK8p(`l@y;zmgs}2e>KcV=!8oxXe*mIOGYR zFZ?@-$8k*opU>7QCg`28Ryy|XE*FQyHDUx~vE`nJJtiZ5c0LB1^ezp?MFdc|LOHpU zjb?rs8C_pR1?T-uWGGB$3U=6CRn>@>VjlLRV{gHnxo!i z@J}?auPTz-6ij-f&1<`PcLEHCBi6&g>mh9$CoGK`cob0PtfkT{qf1|hco|9wJYN1H z1;Tt8{0*ph3xZ5QD9)IO$eEBy0`5K;xM@hMfjOK9!aWtyz_N$VxH~8MAA?*1ft7T> zDp((bc+M4Xa<|6bv0=2Zvecz%RmLr){*J9G46gIAENcxZ|J<-z;-CXt>iKh(+Kuff zPyv=6TYLiK&0MCxhQ|ichQUZb{#q^?k7<_X>jqB2hyI+?qa-{q6;EXMPmUWPg$muSQ{y^Gvf}r+*EbJ`)j?hjz&wg%!a2V+ja6itAqI-Jbm6i^h5iW zT#x!l2v!Fsi6>xB4(55F4BWwJoOohw}Et!^g<^!lRGA;G|=R7vHWp$@k=wu-bVoEBeEgVcMgZMl^K? z%N(rdGCD-J(Z<`;SLe_tdd^*)zP&Me?{nKOzTnIA=oX#NfBAxow>@_+x^+6O-`e)F zDc8ACu%#s}n)(PNOb*rw|0{&qKFPp-wsosIxa;`zDOIyh$hjE9%`xO3sb9-%+_-P! zM&%2;RQ1&H<`&$RpG#c0JboAP&3zlOO1v%t>*AI6xx{oK86|8{0+;9H#2~yagH9M8 zF>VF=0JfTGo_cx>)0VaiQvu|&&bMS0SYLAr)BMXv!8T5_({o763aegPdwY z&|?8hmX7r+T?fo^&DJD_4Bi>ACuEF_aMYc^!c6UkAsc}5xf0KTeK1dJJ6B81EDNP+ z;$a@hgkQHp}!%)Gn$+J0eIk~APsS~@8C6W-l9(*v% z)+?lX+33U?^P*A8_FGW|S+e-!5(-l7tXBzQKw#G`bGDO58ASR7W^G#n*Sy%T{o{L; zWoyM+G1ZeF`MZCq<6KDwT;X*U><|HHiK&WtLe!o`54x9Q$Y&sF(^zCS7!@oQ?y)|z z*MoOsGsvwH(l2BJ$KSe!@8M{40*3Z^7YMY@_>g+QCK1jQ020<7ky&=zJ0Ga1=<}1(_cgg6clZAJgA0;a~VH@#Y z*;Ce|jBsH?WP1abf{iJmoc#4??Ar3MpZRr_zg{uwq0g}#tOF;M=YbtB`|g7XfM=Qe zamIFkzQ2{i^`;`Ag1~didk>liv4c4g)VU66Z`hJTRWLYF4|Li%zC+qr?sXi?{3f`K zyxOdl*dg4)p~9q!D=aFO7Prc)3#`+718|d5`Kz&o#d%PA`TUxC_lE)LS>Mr+QIt%B z*_6pNDP4}IN=gql z7)QxRJW0iXoZ-wuNEw?>*gL{(N-C4rC`KHdu&ddbV##UGmsbwnuh4bGbJfM&Bz7;V zv^$SXUggq_XT-)!6=k0?uj|-)Dpw%cH@}<8jn#QPt=@U~@SQ4-JLjT%!n@k_>lEcW zUBB+@x;*hk`>ybw=-f`b+2K1&^X8a0^At1yBI6MEa0OI>1!hCiU@=!JSSC=#G$Q%d zK#B7;gdgG`JJjGV*tTK~)%KUwmnHugb9lx8o;aUHAQa__cvJ-N6!=*yBk)a@*^5D0 zF=U0#q8GW9D=X*@dQ0lhDo}kIPXleX+ytd(6viay9&1VvH*Cye&&ojT7-En`VFO~# zNB-91_`==Wmk#%N{x()2H&xy7_|I^@h$FC4FIIr;l1wfm#~a zVqTr;19kCID1!5xNJRP->qJW*x!o7}d^N$r_Wp~N@TO#mp5g250{4g5dj?4$$89j9 zIq^jfDPN*5NBcE(E6QBmN}asm6c>`48SVp{4EKS*v@a$9m)4yzm5@<|N`b|)3Yg-( zCf;qs;z6)OB2-+#6ex>I`+PNb4uy-46l9OAmlWd_LeByjNc$f3Y(@+LPMymlnqr#7 z-U!n^#|waQP)~)>CnyWEY<4O3&}&8D#zY5|^=sR)bohWac+U#`x#1hx`8}O-9}OLY zJiz|oeozQg3969FhN;WcEw=jDkyNF@gZQ9?(!>WdgqUSQks?Wl-lddXZ66hbCj?#< zpfgATuC-w?O1_LAcuyl{&Y8nZr5qZSx!2Nl1t2Vq$^k~?#V3M*MS>WFRVP6Psq!fD zsH9OkXGtVUu^q$+=4Og6FV@b;(;c?DkwyXT>S%6vRFoF1&bULaAoLEn9ClF_yE)}t z?ek>_PKsmLPgy{mQXn6&UW03nM4XD!x$7wD3-fm3%Ze;g@#VV8YcBsWb*j(b?X&jc#Ue_XkKfx`dxS}{j>Q*gvS%5KjNvqlO~%c51cKW6Ey z>#r^`ewj86DML9MI}HibjrTi50`on`vp$>53UwuO%z#WM$dofL0N>C*!VE~}=Z0^$e$1^InD=TrR$vi`F3E0`AoF{^U2)D_K z)?U#HJPol1hiEtDu9Wd=xG+KJEhr2F!jJu|AS|E+Bn;MtbF^5>u~S&UmxpJemy=dP zi3K1dSW1|>!J8p#5O4T1O(-eD#1}#zU){hEu+4BVAg)2hlx8XnkrX0`U!f`Ac7Sel z$U7Zi+q*+@*3zD58!RE;_Q0DcNkw2FQKnvnc}RW~h)^BWVla&YC>~Fah&iq(W=WoN zz9u5O*ghK~*2CC@1(zxiRY@NR;jlgtMdQ1EboxmBZUHhvFPyZ*HY0s5c?jd);diEbWE^F|>B2~=h zpcO!i*cRt7ufV)pT^->`v&kob@RAMg<-B8r7s{x` z9&Wx>>BnYn7zxnY5Q3`Gjg(H;u0v6$GkU1jyQ^8P+O?}nZQkY8Vd|JR*4{`5YikdZ zXW(FsHZY*o92|hR`5N!nB%ya;#dFpRXXrRVE}T)$*DU`CS{qxp@P+Y0N=2Ci)8%?h z8XxFv`NfIRfEai*1tpCGrO#V52)vIuF*PbUfE$2ndv7qGdThOPX1!#~bRF?N=6E}A zI)RLOo9-pF*b3z{qU5~EDR<%6j~`5j+10lVWyGoBdnDH*5d&F80Cs#!;#)D})`+XA z)yAgIr>3A|5tbmz>V1GcgR)}5B)a2=$Yu%F*@{gxs7 zNgP{Swr0B!l6$iJ`1ln}CQJvm4sgONhmJy=SGtB-e}ZlOvbd~FoZ?t4aA@kB5=Ekv z5oJeR?OZ9DM}w#-A`wW0!tRls9AyV?ODR&)DYWTO+J!QQ2(~G(Rko4XG)3?Ti6s%g zVzR&}ixShs!XZ*O@-zZ=_G}dcXC0!%t|mkF{Z;kn%C}3om5|9uSiw@Tkiz>VVQ1Eb zKjq%FqlrlPg_6vAAsqSF@-4Jsb^{)NtNgX{ zF6nV0S)bGO(Qkx)x_bR;`H%E+300t^GVZHj|FizZ+<|aF*F!E43&J1(HHbZ5#EgI# zGAk04zCJY9Yl&1Act=T*B~_GMZO)o|T{dNj-npR%zqi(rO(}Mf*PLN}2DbCF90N!C zP8IW8AJ$IZeCaV1m`MiL zOr`;*Hoo~y-S}R(O-pG_Yajh)Qxghp;qMvCkm6toz;9Yl_Zen1^q!`!O?~dJruT%R zrp?z!qkYz22**18x};4atvYNmA*`#q5?MbR0&JgvjTMMlR%Zz2V7nKIi7SK?G>2hu zQcn4%g<0Xo$_NCx&A+C457>k~6XgCu%_HfI<8Xt>%|&u_(v{>@=HL&@4ZLNHcQh~_ zSX%m0*fB(oIOaPD4o6OVPBBgMe}un)xyB+FExg0X81GoPDAK~|2ww>QkMcbrRl2*V zf|X?I`bAr}=`39u63&YNw0@B@(ZC4Pw{5)$P0P1AE>Nz9UBmo>oDU)7uf}wUVZ3{F zow!XrARZACk&FXU-Z`4nn0FcKI_kLQ><4e%V#`-xMR*f z2QyCAhyBQOi{N&f7V_7JIlwn4pQ*2<4Cle~bFw>56qL{NTb`~#c}GQ$Op2T)$1l@y zc$`HfV#M`Yx=l;6%a`M1OT211&`FsVP8XM;bDs)lVJaJbTSZiLUAnVygF{`~vvsHr ziH|TAu0ML)x16pY-!?wyByxP|?kf5l^1jte@=z+$fBrDt-@m87e*oupMT(*@vj4Hg zSzn!wO;lA)xbJcAb)o$($IV-#y)_#Bp1e0z(R)a99{RxhuXa+c2ES|G>Cjpf?euT| z^*t`fM%Ce@IWKJ_ul4WbVsO_k;3M@}PxD#%j@L*1d;9VC3op4HR)^Q^@zBrE{xpS^ zwiNYoGwVx7_b6(oT2qZsW)=N|*RRBs-J@3bRjrD6#JYpisxZ5gLfgB!PJF~>vbE$El`KD8l(uK zMI%UaFok^KQ$`%ArtNX6d%UfN>?~JJb=nn9Ra?({wR2mgBm69HR~QKP8h&5L;9!T( zZ{&BM;cKr~)3`>{yxwvhLcV+pry-z(ZOUj0Ghm&~bFrh5oYh_#KdF4Tk{uNmLvphK z8yoDTRD9)zCY^$qqL29m!y1$S4fAkWScwZyDc}7%H~is z^$ix)_&|W$Zkv57SEDcUyf;|<0a+Ds27cdiDd}V}|N6zbxuNizx zFpcp*FlWo_@~TlP@&78kS!n}$kN+eCNM{EV;yBtFp0|PSz>6-SnWWw z7D9Ma2Sre^q*kZB(H&01bmMoA90Hwzai9h(Hqfjw^7d{)Jb~zTYPJ3#%fiH>fYSg> z#rE2d$=gbZO>Eh+rPulc7r{i*&M*{?sq*`c8~NQv4$D^%RW<|_i9e7Ya+eD^PT%Av z71)>WNHK@;zyQP(Ivm_xkkxa9pU&`>FM z!ub&VI|d?Mio~;BgO!06=EcE|e^xEOYej$54;|>|{A@vs{Zj^J>HVOU*ZyS#9XovV zo^#%@nz4VxK&j!(O8yy=hyUvzHV`(S^NHVQepUV?b9p3<)j^;HNn1+XidTpq2qT$~ z#tV1--A&59^XKrpG9q7Pj)OLoYl|N|E%4#-Ot}ZZ*-Pw=ocGO5q#2Zk9CWH!gtISE zN;;<0%fFI_tzi$#S%}h#TbGU^n%b_zM3w^^wrzd8w5>O@T?#b?tpwJ0;cWxX+zpjN zL8ydXCtnxfU*AAjAd-*<7ShygON;E^pQzVV!Mq*_c^=)-qSiWU!yeb9r*?E>yHef$ z`rf|2j;)8^G#k`=>J>8H^C<=4pWYpv-S;B*Wi5xub#ov90=r@Vl^=P>j~=6jKvk8` zL22d66aJvv*;CWjt2s6~@OSZFcyOetn>sE}?K#+zZF&3gQ`Wcm@n&qwoRufb;?Kob z#1rBt<=Nq7V!Zec|E>qtUItzZet1LC+Im#27f$K`AMi>tPdAgt7FIrta~A8&4o(a{ z4j;5oxE8`Nj5jmb(BzaoU?r?2Gh7Yi@QW8=#v{tWQ&PuxQ;m#VH*P3w42;j;c&zgC2uv} z=yN$d5x3^i+`~byyH;}$Rg>a&IvnwkL#fp?htu7zDD@q|mR4s~b%Q@yrB*crBlRlY zimG|cYaKDJhR&oY{yH{?qEuDdg`2ak^%@wYf!YtAx}uD3Wikkh3YeKEaCiDkY%>$=St8rC%Nr}4a>%0MvkDMu$YTVA+9i0K6 z-&2#Was?G6Ay&Jql`4lbz|(d5yuR8NpI0NdQ*qYR1*#klc(j@u2^lWU@Aft-)Z$iK znj_6vHifFX-4u3c&00;+iO%b(TdwkewJ{w(Q z8bQ#~3FbqK2)FRT*Dse9vpv}vjm4s!$#(07%E*qQ6lkn-+s>WaI*0e~U+(Je@9*v! z*s_I&2CqqVMu#sO#xvK@1r^brgz`Wl67Ae}`m3k$(3k)AOZA=IOK(}~>KwRd`5u-< z3Vt2*A$7mwCTUD*u3oxepEU1YG7$czwVhZkwRbCLd~hs4X_zhIhT#awzG6a%?t;n2_N75YD#b zFJ}XDoS$G`<=HyMxqSTn#)n6Y5yikQtKM!x|J>SP zk$LN_CL37R$ckwK#Un znSK-jvm)~|iO^6|24=EQ`sj!IGE8YlF6+EGWSIIgtOHO7-c>*ig2|;CM5Pe@1&&M| z4Rk}USLN#r%M33puaeK<2fgBFcuNbfmJMH4CCNk^c4s*CJeUX~cA^@)9LdJDQ;kW- zuCkf?gJySY(E95@%+u_N1+LOT42KX!6ZAv_STxpP71pC;EqGx)Shu+@$Z?>cZJ!v5 z353}`qL9I7m;viDtPBh%1aL+IX9HFIg9bH zv|;3wlg<<-X;kn|_SxJRre;aMhTN+TiS1&axC*!efb2mqaDb}>TM%I?jsvlc(;{C) zDwp}4_^b@dN#C5Ec~op8=w=kr8AKpUH)LILnh-$2n`U?g4FEcxX|atD`zZHYP~COc zhwkb^!uVykwY1#!Q9fMu*1vGrPpR_VwbML3%d}l|lG8i=%-Wmdyo)an%Um+#C427N zVj-_oemBz$e33Kb>a}VR(UekTn}s+8w-$y;k3y4!j zVU5W5OEdJjRAXn~tpj_v{L^IkSkt9Du+x!9J$MgQ&-U!hyOg~fTBCYU4UWCBqvz_K zQ`Qf*Z~fim$asD5qC#qOAo1kX$rDBG(B2%PfEPAj_^jk7Tb{5i;oKy~EYnP=Qf~2P z9p~}Do#S`xkUNjqCQs4;UdqmE$-L!Pt-(Js#f-@p<}DgAMc{i*)}p%XX4hcBxm-%GE)cq7Jypd%B)m3D5{;k)u% zh@@XI45rmh;G2CRV?A%C3GeQTAd?Lf8UzR4#zkBcKQmD(Bsk)@&5n|4E|3@ilI9&! zrWip?ulzd=3>l0?Zs6k33vgvJW$^}|+|n!W^x=GmC6a;ng$`^?4|r-`cTx62|NBDw z`%?p+s!Wzq2@H{bPv!l+rhHX|QtT#kuN)b|$$HQ}D-YfiNM^EL??8ITh?0qCGgY2} zRR8|aJ?P7p^?Jo^KqlR3phK?x8TE|ZL*ieq6zMGZ5aF&tmzE7eziRcZ9B`F z?=t`{1M9IC#w*RvvNJ(1y`-K(tm~z!YW#Sp6{)M6tqpi!_RhI+M4H+KE{*;Pia20{ z{QG^&efTWzQ&oDuqCDVgaV2kbx1?%o1CKN{Jrby`O|`hMJwn8H2Fl*Tx$Aea7#3h6 z2va+Yp9zKi+8`g2yfmDR$;-jv_?*fMmgGQ$-ALv2&21_zeVeLq305K2Lwj z^aR2&m}>R#2KhxA@P@FCuL61lCG#(j*o_#WGW*HY*Oo`HSR*57!aZGfVv2{LU2hMjM;mGj95(I=1PZq4rIYyB7Vx`Z;*H8hFX`yz{&@gL(6^ zV|jUnC1&95Nm@o;C2leP@)=iJ)@p66gk@G7zsiM2aI%j;9MB5?coG{Kty(gM+3y~F z?xK_V__pAX9>23Qxa+d|je~uG3}pka3z68o6Jht8EyI1)WJqb>)Nt>PkK`}@()fyv zo4&RTNpy85qj8|+>Xt-yC{Q2DTo?ofa@+CizA=9b6sKc%(tpW;`ubq7{f3VEk#HC7 zEi`q#P4_R}71`7tdgrDwmA(n2G&e?jl+=#AlHP$A3){D(z{k?|Kzu+Kn$#pxqazL! zf)UDQD5fSAKh6ovyCkC^b~yuxU*aS2vJ{>+5z-Y^JBJl9yt9g~kTnozFrDQK?5xeP z6n0j7Y)1@#xMB$fI5fytUHur~@Rclg&P~MsIDiZlydm#b;do$hNXUdgA}wV^oMA9x zV-W9cJn$1(y!uaSk}2ioRB~nud|%f3JDFK*%S~O{FizP0Y=iO~avYE4@GRq)$q@s|>?Cj> z#HtH5xQPp30Jf)21|7W;$dlUh%A;goNl|j;(V?>O0mB781J-K#)44@^eHL?{o<8@= zHY(&1XejZCfpze3t_^%6JE7XoJOFNBr~n6Oop-C*cnr|qu`${kSTlDk9#0f0)cL_Z zN-XN}D7#hNux|gQ^-m55MSe+lteKBig|xt6+_r_{g8?m6^=O#}6%&!MD8_sZqT`qx zzcTn5losVT`V{lL1h)1V=p3JbcutTk!Ry7@E+au1S9Vzu_(hviSw;O|sq8@c!crNT z>a?>|)=+<-RQ8HaI#eoGi6GqxElCb!6)F4ZeWfyqI`!wJvLb5L7fWST)WO8C+dELM zE0vuh?1-1j8tU_V<;@k^!_cP$<9dapcMoQW?j zOwCNj7mpT8)$!@s$=QXj_>p3KVQIb?U!09k%uY|w-u%io@yVsB@#6H7Lp7$zI&w;N3nqH$8=}G5+ZEG+;n46I0X01+Vww+417p9xvT}`Fc-d zM`vdjilfu^Xf_yjd7=0C)MD?ex}C+u=S%(0jxA1&&CV=bR-9a#9-S{=md{rd=NG1C zX8?I$x$2VQOmTj6u{a(-aw5KP(`5hR;zWF6e)f2rCtU>A;&bz}Hx$Pfdl*5ODo=y! z88l?ew~H~@#&PU-_CY82%in6|(je+C5mRCjznZutj-aI%zjb<46vYhgEFq713Z+Su z7iI0at>a=Exz3X^-?>y}vb2ahaWR48v}}3vf7wqQy)9wPaXddQ$1bA3 zIN(1D2o}X0t~Ntr^`ad^eN;l-gW1iB)O zLpv+6Tf`h0a-Ojb?<}yfD97X3EQrfc&ucafIOYM-xp!ZAeFa+2OAPYUHqQHA_0%P3 zH6vS&Vyq&t!?bk*#|3c{M(aoIBA{Z(c+BG{l~NC`k1PAqdpbpr!UDOSmu9y0dLeWn-8lXYSi5>q|50IbOl{Whv+Ig zOjpx2_`#m*=z1EVQT&e|V>C`hnxIKKN>g+L9iwSFPBS!1b95uk(*iBh65T{M(=Bv@ zPSUOPM*0)F4ZE%e-9c}nJL%1I7rlkawu%qk z6SMQjisK$xo?BWtS}sjbJC4lG9&ioijHa0yqH+RA%Z%xfqLphGlOr0EEM4vU|#hakox{uEu0gkk# z8EENo=h)HW*fGcP(Wz+iq;`D=H#cWicgb{^|7 zUaW#eQ!LJOPk;exN@a9$e017922z+AADwqF{=AHl?$Pma^el_>#pAO#6~}Aj1$vsA zm^yol|9`k~-_hCmsgtlGMzOdcXTQCU3+Njayt1C-Y$YuM;T}m146^5VaT4o^x*F8o zG(S6Iw{IlEX} ziGiiLy2?vl<#Q^Sj=g0REX`Gyi`;PiLK6Er;ec;SLShc&=a&F4oH#DA!WZFlYSzV$ z8{`VvQJi*8PR|};24&2-ST2CBTyvvi$3`cME-=r{n64YxJq|2PyLmMvaq>bqBvVh# z9GShvy*Q7GvC$d#-00NIA_~rlX;1{%Z>l%}_A4?aPuB3lEP+eq8de{lorgfOFVyML zo82(aj~`iBnghjiK_Y0pxNr<>vo4UeBgN5U#d*gO;M{rh=;GK>ZQVDJ3T7dILZ5Lht~5M&xk#BQEPwy}XptWkDv?|5(D z;+RWlKtMjBFiW=?qtT&(fq{{MsaY=!$h?jU7T%(=z!D?Rr@KM8p_@4;6q*^>7%J8aG=@=r@*0(q5D9WjVWkN%EG(=i zt{h1u1q-ZMC%TkBsl8UT^{|A&-Xd3t`2&yZB(1?4J=;j$aq!pSp;P|C{tZP%-5>NU zPupz}@L;O^Z0YjSqFnLyv%)OBcWje%rQvjgL`$;_;{@_lEz=lR_VD@{;F4@dd2ZBc zv!0tHRnl-7Y;+G*6$#KOmsv4HUpU6W@S50XMH)9T}1Dj zrAJ?WZm0F9tQ&}3G85b*JI4ps2T#NGC7Xwbr2|k^xV9Enn4vLcAn|I+jn+wFXvv!?q7+&Dj-^D^0WlIuPF!+R}BBvp|HLAI9IER9l{m>N!j zx)fQKYIX1@rF@Kaa6vH(bd$|fD@%&3VquIcoKlqzevJN)aum7Ml0_96)0x=IN8}~1 z)njiKeR2#*A!N$D!=0eL;0^Dq0g$q<)$D4s@pXt?enlK6%UlKBnwEi;N3k3ot z9!eNQw^Ju{5dV7;6LL*mT~`x>=H&k76xqtM@)TJcC|mMG=%U~7EHV_{cIWqZL*3L6 z3tbz<2)YA;3QI)rB2B7@y{rxFkVFEt#8{h*@nOAJ8y^UEoX_(zBkXvW8{Jy{5~T2S zW~A`wn`1cfDH&ZfT=0-&kT$*WEBzNHemOxaLxt3qpn3p{upWH?e<^*Cj+!xL$$&QxXX8JhQ!!=A+%7t@v9I+?s(X zlL-CI*6HS2et5_WjNbM&Nq;Gvr1JUBMlvc~8M1@S2$!mGf|4&X6y!JTwib33W&oR` zI(AsbxeG}#j=3#kSu@z1&<4TefO3pIkGV}d4|74m^6BY)W(w_|SV+t}|HgKSJ?-w1 z(ND1RzD~ys$d*YA`}H$>dJpmX%3t3!R{GLv;x6>I%7Y#upM2wvdi3|13oYypl2}-F zRf$k!RtdL8+YKv^M+i%F&lN-1-x5Pi;%F`EkWB~=OC*XlP-qnP3|RWE{WS7t z|3ILkryto)nU#KJ3`g!pl*E76v;O|96uM@Fa*FP4A%B{6)5GY#ePBf{w-tFlhuE1Fs7v6J_z_ZkYffbc z;gFdnPS^X&m?cAJcjFqfo|IHe=Xa!ynM-ihO`B)Wd=m=_oVc4C^@shnmj6M2kH4~- zz}pQh2fO+bJ|cPxLUb@l!0c%6^1cYquCSSI7f`K3(@^HGMG2S~%jpN|WLD?-ka;~R znrR&%$!{Mq8TIi|DX$HKx%y-%AK%EHs#H@mi1cl4`c+Yp~S-d5UaNx`3 zFQsjqo1nLP4jT8UsO`_;7eA8~XzaI{Q02UjX+YJc<1TX?OMFY+=0Vr?+ev+O0fUKjhaop(Y ziN&uCM3yJTM-tED`wB|Xk2u1fCyE|F`3k@I8uAA(f^VKD(6AN6%AMLks!)8)k!O3I zk>Ek}9c*S=a(F4vWy#4s$Hc`#cevzO7efEkl1~XDz3F|5b0qp4C6oNYCZ*c7@X$}&s6r-b8=@os zb<}HS%kPC|+5Yu?4NZdr`r1qd6yfY32zrXUU^+5x&QHErr1b7qvZaum*p>Cvi|~!1 zy!f10SqE96apWm7JX$kbkVccK%7B_uUYy0!E5O7=(nwt1VbMoxP24G+s;e1Ct>x!o zVXH}^XOpEzQ(rEO2eVuviL&Z>%6u?}?~q)i_Am!Idd#_UalZ*RM}zoRe!YW^s$<5H zsktYYO3g%KP2t&JFJrCL?2el1UlXq!{;v33tNtZ3ltId|+cN7_^Ybp&?;E??Q_qiS zA_juAlh4|dORM9|0>ONNTY{{iN1-MAt?%Sew4SXiH9_=GdmG4^Xfs~MFji8+Bo5Cq z7Nw-PIbR+o&?W$hQ ztH2hvR5kKc99>yT<$|Y(1OT({-l?X)qwoGxK@KylNfM#+w8d7v&EUUXi@}c$2TNAy z?o^g>abt%6v^%9NqQ5GqPqCzW@WaH-Spp2r^=@z*Q0o>ug6KXP#YqB3Uqhz4MjE_& zK&4I0q#JY|twpOVctxW?DFXKa@y*&s{jg^Xsb!C{jhz)MG{%TC#A<`{cKgoSIq?XIRw+d$@q|w>==JJhVEsjcz(DUHnaSO+K`FmP=%S|ORZ8JzQTm& zq2p3D?;#wUxUR2Y407rks}G45uL`-_+Fix{4c{r;Ueb0*#a4sYq?? zZJK<^{XH`0G7J8De^iQ5CFTJS1_+_HD{LV%4k)-<;^m5G+~bWL*xp0RRSfDz4XPqK z{>2jILg!uPn@%(Kqs4&QF~c;!%E~b!WrT@W;&plU(WI?7%Sn#`Q>MvBb}EJD4b1xK z*BEQPqvyZA+C2vl~THA9oK2!Rx)M@4lt!))CZZdJxd9qEEe{E;mN4 z!A6RPT@syH@Hnd71qS5-cLtnhE6CZKJHZto%xdtt_8oZ2K$itQW>+ z^M~UuAn(5JSBPP|)>VDJlf2bVPlYTwox3?_SgesYN>Tfk=wF&`qBS+C6~PP-&cBVL zq4-qT)`6PCa%~AQ8@3j_i`Nb7SMs!)^snf2IyzV|ASOA8YG z${GHtTPsOJ2TW5}Ni!hjl+%h0C!6yRG&eGlE1u6x`Uhe@f0xop=vDpp%4_d~_Y1$m zH?})CbA__5pd;Z9(u}Kzrs{oT%C4n;X}mcyBxxIEujk`MK<^46`cWEAPO_y77sz%qf+_~a&sJbjbgAakxwZ8<6 zhrq8@&+nAkC&!Da>nKzpi^u1^#N%=g)H*i*v|eYdn&A?pQp3%HDyk_E6mt&x?6RPO zfC0h>4nxR?9=+}fV!5f9yl?Rj*Ri#2eM@n|}@u&&wdZoRe+*V!DU~cI^@C~32?^+D7?1cVu_!rl z?-2I0!|})xPF#f~ty!FgzXA(*9gf_2M!j!<`2yy;-;@y1Of{n5|m1RJ(Kh;YnM5aNSPc2J?-@l0Wyt1!5(b)R%c9hSeR_Tlg|pcp zoVX|4=-m8SADnXDhoOyX(+?KOcd&V<{^3pK7f10GDisPG2V0c21-aiEtr@cmM@*<= z1v~O2$|}>f9(&$^c7=E-ss*eQO^|F~@*V#BOj>neuVk_;~& z{ z=bkQYkxO|b(Jm%f$q0p(It@MK-SsLpr_xw;U`38`LDcc`8Q5TP-(2HkWcK6vd>H7t z6t{9MS@;k*l(%>B=l0jS=fe)S|82~-ChWLCcW2?TPz4FuakVqP-F3HI7qR$+*v9vo z<|&f;H8-;PS@|)r>Fv$10}_;_?MHg;JpG<)22Sjj;4`*br9)&G2SBHfyRSb4JyV<4 zMS&axlZYW`1>v-SMEOP_w%>p(k1-_0e~%u(KiuX4wnW|c;1};!tM6d#69?OFWF8}+ zuRCaxuA<>}1}6q zURN!I-c5AF-iv$y-X`i}`Q8m?1C>B4j*=JF#_!5gF_<9%A)^RIrX^@eg-!%>k$4J{ zW6Vit28o(SP>2ZmL%lF+Iu=npceV)Zt@5BVo*Ec0rD&QLAFRF2KF8v*7jC=6hXZ-Tk^`rc# z(zur!dLnoqhf*fgUgq&VNvD|!;zjWDZf9m^Zf68%x_Fp-g7=2SWnP=ARxq#oc*)@R z>=jqqv7mG7tkP_}g-8P`X)QKw&Ng#_G{|(F(%9>lon$Gd>9SXxwWLKeUf~O}FFGx) z9=Zzak4UGqI@r^cP_D7R-&JiPdt-4J^7@Q3MEPke82it=hcUY#Qtbo1KJ;c0cw#?Y zH?0cUlEPp@@?GW{FpXe8fS(x#c~R><+*zeg-FzwDwSKBJ+iT{9B*l&ACf~71xHAg& zpHl5@wdp#r5!+x9{!;Edi?TOn-D#fZ*)7(Cn!oGDoAIwwViGmz>^$wYfqb(GFx7D~ z-RWwOfPTQaAD=n?Ns2<zS~iA>O7bdK_$}rlgKJO%J;3gO zzT%p=RLN3Fk7?+!siue)dbK-Rd!3@Hb>u4~HK>vXiHRktfe+8L z&^59>@k2%rzCFunoT{9cUCDaxiCGbD$?Op+0ZcF0D{8`}WUklJlxa}|8-rg+y=U)% zn_<}uLcC>v3JC&{$WWKGhlu(Ng`s0~3I{79uCL7{g9QpO9dA<|SA4Y?>NO@(gV6j! z@o;Bc+$r_MWH4Tc*SF|gVAimH`nt>|T{rO{otu=G#XVbp9lbh`FyS%0 zG%SiPs%n{^{-#Vyr!3s0e9XTVj>EZ$5&bR3Bk)o`&oQfeI{jFfdmhak&MU`9yc*g} zyZ3#8eXdC*Mv8QSjYCLy9wG6!C?qzY0ENAK1VIwbcJjr|MTm+y9_A!wM|ofif2<#Z zlu#a?)lJa_1QIJ%c|yDpFA-@yoExEy)3S_Y%)%a8vV=TV^K+=)VOH3}PB4)%-orYHRQH1|4BI3GMln%+-dQ zT8}vGiB~}%oAR6M{d{JA)I1%2)y@uvr`?z1W;>sAFn$a|zx}e9Unku!MEvSXn;Mzj zu4KN1658Z*2FZ-3e<8nDP(-uTJN2q*w_F|zE8Fc4Z0j%~=Q(ncj~G&mi`^9D>L}Ap z-VWtJgFG^SbW8WHF*^1cA`hPPMUw(~)sauy^(ZXXnuQH@krhmJtkRDI9hvw@IC2?Sv2ZAriX z2o_BhB_4{y5i3{n3NPR+<0lAdiEmBm-o%Cpd*~tFG(eQZ%OwEU=wGBUg_*l&aVL10;re zvJ`GVuda@O}w;Y3`Ek}ja5=*!l7xzp?J z)zij_drdQ3vxxF+^r1}FM`z`Ho;I&pXaTPMwyFsK1b=J3{|P$A$Y@XJkO6J4VFpkU{%gArnXsm5N&VM`=+&{*ffq`Ez;uA^rE2!yBAc0-6l+F!8cLA~68L)!btiMD5@GaLWW zc5ucx=jLczuINnZ_FP*D2{$3>f$If&Cu&W!8gq{^l(yqwB=0T>aqyi*;Cj}0GWJXk z-y^*%co913N-rX5N!%aQ0pdU9>y%<;MMaq}VtNJ}Q>Yfr=3n5qo%V zweg0*7ji_ik0M4!xb!$NSDnO^Ri@cY&u*dWiW_sA@F?}3nCeN1Ao-|fro#P)X`JrD z{V^2_@WlO9q%437&wB#cJ3;W!$o;-gQ{ecTl9LT=O(Dra&;phF0M$P65XKYILIwi~2Y;|y9pVmt#f z5jkZC8$+g$H+Nr`!W0P*qo~Sa(BbJYJn1~5l9Qge)ueCV_!;Z8_iSYNLjs1Z?U;4x zhYW??I^WjAXtudCBmt?5iNtMY%UJ7L0M|yj)Un7uTeAmHJjQkeJ8!qLaO@{3u>>x_ zA%&Ay#9P}44a@p4`D%$Cl*iv%j@$k+j&BvJ+e2SidoE7w_Y#+^&3|XJ6@vFVZ%cO$ zpYst!->b(Z`@o_X(p0RD*W2Nj4u9w!EmE!}mUVikOtKt+#Qk55d$IgUE;bwF=7iuy zWLWnTw!sVtPeV!ls%AbGnkv-vv={KX=E{>MkEzmBSytghHCB462n4n!@%9O;q8`@g z&g$FH1dfJ%a9`3bM%8e)zR61GQr3t~u`iIY4Apx2By-;UoVqDPEg>Go?$d+?mR znNIHW#He+9g~cK8^4hDU;`5zyZ%hE~wFPSG;~j0fVOt(sz$9-pKO;63!UC$!V_ucs<@TO9k=vMPbLNlYfsYy{m?ueZo# z_6u$*d8L(}a~R7)+lvE|`9oK!bAUNJ!@`5bD z_?zGSJFtEa&5Z}E&fSnwYmtn(AHHtCTI;{MXe64AzGcF50Q#w6JUYl@7XFvKUD%LK z0|m;@YftySY@2bEkGn)-op6_!Ilr{MGNSu&IN(BN0nW+04;(F^V;0s>BS=!5PVf6n zC>->3+3|S(bBCbU474bi!vV0!e=21*Mlw=Qg3#xPx2H2 z36xHpBw8`BtWlH`(z)H{0IG#wZ<$Gns#!svM0~u9F0}s=n+!9I;#5QG*jvSKEt%!q zFv+l`v}<|yOdW|g(!VF~CWsEyd+_LCrkg3I72Wp{xqY!y5!11Txa}La?{oaG412+~ zNKC#y&c0%Y=J%XF;}yh163&@8v(m{{`Z8n*ubLgf=0jse(6c823ZzQ=V&MREvkN}V zw(ISo4Um8h49JJ))Qw4kt{y1GONME)ZN_@D>u}nTVgWWF9)>O#O0a_Ks2$y&8jj%b zWG>J*2(lG&Bw0A9If*(n)(J7fYKKqC5UNDQAoO5&fgjHt9JzRfQ{&jafn;~Q8gk!x ziiA}Wn|9nRM&L|tM6-yjgw0cJ?s(|lo3PMdS1K-uvUr3p2 zgi7XSi=*GzjqEclJrr?X=BbT^qq9o!x>N<5yWMi*{FoU}KEl!)&GXGy9{AK4G8Vtp zpaBCxyx>&1&Zt^#ea8G?^P+H2QglW^OZ^K|RZGXIc|oU*M{?#$`Y_!uPDj+%ah->m zH(v+*Jw_zkVB-tW(x+n_e8?;mNutfgl-(Igju+ZLidihxJ5ZHA=PCeTS`J--ZqI`b zH@=JRzaG^*MS~voYYpU?8V4}scsw{;f@$o-%E<4=VIDrKaX9*;iW9s~0 zxkEL4_w&Va4ETP?l?tEmyhjAh_KrVL7#BLv4ND7>3qT)$vak~!G7GnIEdUN{l?}q= zJ|4SXLX)y!0g!UpxF3UR=IUF~SI4G5K-UL%(0)@V30J=m)L<9*rNm|TSP?pQvd7g) zDR0fNy{7xy=&OC|#((R;2b09-B|j?h!T#waM_uV9yi_vVJUY%$F1j-sqe-?}j|s!_ zK1Mc0vf!IhND9OHn3KHBfG^|lwv0%)71fJpHow%9ZOwB{{ceD(=spsyl z=JDk~oc_+$8@s4=;3)TiU%-;5?DgYaffy+q8nQv*JOM_w`40SvdG8`_0 zXZQ#Sj4asPVn(z30tPti>Js9;WD$aU4aTso|LF_q((R{{PC&srpL^Cap{cSA6^RO# z(wik%$`Y+KN?jdD15bT|SCv}6ed2RaI_Jb0X6+V;KaUnQ;uurSiehH1YBE`NaK@8P zp-Yw|@=zR4f<=Osv#<@Q`pi@}+z@Y%dE6>geS3#Icd)rI>6e{noNoP&^F-<)5f^Hn z>}QVu^nJ7ruc)Ep#CpAFtoNvQ!#IYMkiAlU7w;u=i)Yjlx11mF4wQQ# z8pd3GyK=jXm#CMlSFB4|`H)glK3$3i^+YB}F0=_W(_B_T1Xro5T$gB?k$;M;_5q!m z6_!3OdC23Qi)(T6-~tz0^$5!0ZpE#)k*;F7=Su{2;3GjZ-31SUA8E5_?@1D`HzfP$3-xTB4mcwc}iZF z0<7#9FnDa3Ax!_Z|8ManTbn`@imemNscxL>&@5hN``J5bxU@JlmPU_MT^5F4qa-TjvxU~< zh*gUHD#h9d4~Vk-rPeA_#wrm)w+PP3ekK1QOwg-4J~G6JmlUHRbgr2vqg! zliuj$9Krg9{h|13zi#yO%PMb{ug+uD))B+hQesQ3I4=n+l)aGkr;I_N`HEpyL3-$_ zuv4$Tlb+jB{MgmiMMGvDt+9HJdFkG3+Kz2X){JfKV)T9I>;0B22}JMr6w7Z-H=JwM zPk5texL=;kH;#Qzo>&CA{0J(1iV!BAoHGyLv*h2p6_m04da|D|jx^?`=$uqm>xO_d zZN=A=L)3bRff>Guj#c39Gzr`}hNp01sfwnEtHTA||Ax=T920oHzQEUuZ=?32;HjsFknx2+#?J1NS8^C6NDNF3)6?4XoX@WBz zRubDs%+f6&EONlIKI=b`oP&A@Xk2;jdg^-3Dm1z&99^upLR4;V}C>4 z+ZT>@$ycLoJl7Xbw^D!{e%^LB65a1;zbWr$(Spt7V}Hg6n{IYeh^(Lm6u8gmi93hU z-|rkjZDh)#s?xf0i(zx@droOqh+Sd{bBcDwcA)K8y~$G%2AUa3iOsxhvx7^Ce#))o zS%de?g&|b3<A5EkzcTQctnb{3Dd;-x>%uws+d&!o1e(Cm?cndkoFs&D~kX7jmCba5?}(4ht6Y@3NTI6PW&>@QYU$ z;P-d&Zdkw&aH>{0OySWKMO0b6XaO${B43@Y;x&v5x$t^|;>wcBK&A8R6KYb@1qb2h zm2oH=fw4i)jRF-BSDmfNrH~x|8kM_P0q7hR^D4YI{nx4Y_nYNujGK!KEs}p z$@LP1y2=ma+?zwrdAHaH$w-2SQl)oRnNwqy!_<g1xj5#H$ICWFpj1?jSCf0gB9}p^LkJkrCX=fPc#y z2$3U=4uV$7sDVm5q}Q!_89PXxon9_I;5$jwBt(xPg~`(`mW_mx7Q~HMguV2z@qo~Gr(g4%Y(g= zy?-0R3tpL8?f6hlb4Ld+XTXqy2I%$~F=i+WZ{^$g3&%1_7~sk=xf<)m4Tdc`wAhVW zLB(*h{dElvCY1a(E-sh zKm&$eEb8?aKLAL((F!TKviQ8ufiS4=4=-mtUay_;EE7hss35)~X=or~oGvmn{PVNB zOW|U^IU(;;;D-=be0)ZvsB$t8EszRO9w3Y-wHo~JVmJ=msZ`L! z2*W7ChjrOP-h-Gktt|gcD9IGvjka>3J{3-0{Xmb#0(_P2G2DrS@|lYjnZRZD=JlFk zCB#bfr0otrM}4dL%>C)=W=5c>F6Zr1R_+=D4>Ec`K30`lQ|!4R`p$4#?~r!csSYW{w^pwnl=(7 z&xV4fB*h}Wbn|+DSifpKydfc@uI%Tw^&{`=y}@6bj~n&sni|WSq5DQ8VRdIatcW*# z+62OA3;#HDrKd`zYS3q;i4ip&>T?bcP2g*O1>V;A)3k9goEyb5+b0Gu z>SVkCyn0WOK_QoSj6gtxES#SzfO7ARg@~qmaRPFWi*bt=#LK2uFfQ>E7NnS@kKhv|Kj4f6~>3xz}DNDt?+ z174erlEbJQZ9VN~ zP5&s)TQ<#xOn{I6fr&nU?VPiv>|~te*IF52!c}nMW~Zm__?a6Dr=kJmKi=CBgA6R}!ibdwp8S(L}9T4|J`(K8fW|&E1Tkhl=_qHl3f1Mxi;Y zfcLOK$nl6_Czc>!MGo(eq#J-Ew6)DL>lbG;X5rSnsqn!^iuKY|I4T->o!ElyMvJ9< z=U9%@COf4uzr=N=3H9PTnKP?^p;kuT}7xT;$y70yZt_ApD(uT>23U;B)`_v z>x$G@(Wdylzwqrl-SU|A zjVUEsYv$ClB)I@WU3(LK;iqB?Gy+aS5$R!oF7rQdpRd+WT#z} zXTW#BYwlUUY-xeX`@xjgpRZ5)k*sM!w1Tk0!Es;J%C!PS(FbxCyj*I#iU#FjTGwDR zm<8{BL{`(TCAakevvJOw4>iw}k@(?O%9-c~VzoKd1;MZK;n2}NB$>n)*LL-cKBZ8R zxJC!pVO1Yv5Iyghk9NI_Q&!a}x#vf`i^x!sn1{A zbQ8Cb(R_d5t=_VNf0@IVS@Sq+fah$Wrvy>P4>?e<_mxCZ&|Ec9R<)=0>yGPnwpy+q~(SLc-R&=;6}{X()IYQYDSY z#T=bH7%Ps;MWO|!SPQf?z0cVqKHFiL-I1n&_YsWu+b_BGS^OpVgPFWvciNeOA07LQhyWUG3lMho2BuK<1?}U2+ymUR;rem#cM^Nv zRBy|HGTZyr5Cuz%#lZ6iAtsPa~>O`PGkCb$c zOSJ!CEj(UWeaBC^v282J%wRdLn&cxLp6^~&HD%TYVR>)ol+0=d4Yqf`+BKEfxA73U z3EJS--gn^v^rW$!fL@afK>Lpq=lPX+2*=BU1^ZaQRZ3^dRHPU!9NFfWzl zpi4k9lQnHv$}qy`a07ktHk3>m@bvEJi_Rck^536?wM|)(50xgFlL| zi!Me0sqHE;ui+M&KTnOweOxXXO-rH#j}eo^+>}i*6_O z5cblAcxSZq zZ{&kt6=1Hlr$vtUc40ac1#{m>#sImQw>I^~HpNg8pB7Ic*L|kE^W=S&C3qRIn)T8L zncAy%IH!K|L}j-VR8(p_TD+LBuN^sXz}os7oLh1)53PN#9~zqf-cJ%-N3Uj0#Z>t1!Ft$t zNYBu%f4Yao@nokRK(~T0{>Z%R9qN8|yWgDN%l<7#gMm9(on5Xm(VuQ$1oHy zdNB1c@30uK;;^}}lW=fw>hK`&>hSUKs|X+nE(q0#eu#}oKuBmvib$16y-15lr%0d3 zw8(?Vr^wGJtSELU$*5GQ2B?K-SZF3_` zK$zl~4w%iD+gP+%s#uv=_t<~2pK)k$ba2vfCUJpqQE>Tj<8k+KAMt4LqVZnwIq}W# zv+y>iW8O+EfUiZ$CJ>I=#a#cER%AQN|I`m+K~E@#*_XfttBHM zQzuI#`=$`2$fks$)TT_QoTY-J!lE*vDyDj+_M`5l0i$83=>}i`B55&b|I!}QQP64A zmD3&4WBva%qB-f1Boqw{mi{{(7782dJLnA#h_M)??B9BbQI@mDWkSlS%4ysZgr6hS0jlXmf z(UQhkH#<)cub+@TcMsq7tXUfGHHf`er}$0B?|FR%%bt+?Y#_xt;N8YeO0M4yEJD`>&Uiv zF=|%(lVMdatPw54dov7jD`cJ=J*-$&OAlqTzl%nrCnKo*_;Rg;Pa8C7XNB}M1*E#8 zl1p0A9t^ytG+KffO;6Y zB|Q!DVoBAq8PVlYOW&TZAH7Iz%<|xeE^jMZX(ff2^oPsXCpmUTilat#*k(Cc*_aA# z-Kas)ZG&@9YxtK&Iq0WwOF{3&k@kx@{#GV*lhbgVOk$Qoz_^rRgro>gT_Ur=!RpDH zY$DoU7*C$nDV{$!&R`iJK{?&u~VlUTg7X^dIp*O>&{(tbNhrtgf38C~Dt zYc#rE{`a#sJ@H=h`*ey?1s#2y3#3Ud{2)_qvDB~U`~`nEb?-!R-I#NjZ1LEfh(V2j zDJ1)gD^JMLvh15>*NvY=*PZG7r$lb>EMq=`z`Tx(N-y4kk*FRI=~|6yg}ukTWxkwY z6}=#>*0Q0WJ@vP&^0ZGHDx8vv>}zZfZ*!ECCutW?|1-2JuoQ1Yw_|UBmhs7&nS39t z>_~SN&r7qN!+P%&iR-l03P519@zy?OE-OYisP24j9&Dk=N1#c-OO*`bzx-vZYMQpe ziI%g$i4DnP>)tU5r)PG~%<@oe)3a)2GC?|)G)Bw9!txk*os^T&d{QYecq3K+S4MlS zX`VkQ<;NePs?q?H8|q)_VFlFO2#`oL<1ko9T`1pblrJa5h><|-z#S(rPozO96ayF$FdVRujjL&hF>3cFIgAtiTkxkJt#1Pr@koxy2$ zFnS?^(O}e2Dkh`Ejz}z2XjJ)1hWHQ|2yE<(BNP~`{nj4%_PcVOAuV?Z+e6+SgoL{y zKEZK!(ELN@uXy^qGCm&F`@%0iV-w2Ki7DhadN6uK;U*xEH??S{mY zwEJ_Nv6K<>t~f%X{b_2Y{u!d zn#5A%dE`x!ilvH(`J=TOtEvu!OPc=bi4C9EyT|J{V!b+3v{U`N9$gJ*9{cE~9W_}5 z6f&(x7dEkpwf{|=m)gLx+Bm`ss!fJihHg0LD4Y+0cPg8xRy5I>VQ!HkF!{v6Nsz|p z;-g$})dt{dXQU)l>&~2-h*uj|UXe?yFBvsuBpyj-$tF9=yz-4u+l~fD%rYHZEFGy< zG-e$pPX-E0F1-l&NEWLy26H{|*uWly;5Rm{7--RCw#$>tQ23pFASZ#VAKC3lpTl(uvh*fsl2Fl>u9mOBgjiVUd>aQ`)Wijztisf)E##7@ohuCzd(b?sG!AA)N`re_Hmp}t;A zKn+i)B4)~Z1QeadX0HT=PGJ+mj81X;@41RJK~iPAjT#=^D`?(v;a1*>22mj;nrSP? znzY5`3~XV@ZF?co+|z!^=o0f1@*@4#?*PL8&`${aV$9f>V1u#UW+jxrkv}7!{KO<< z&5y=r8_a?21d&U?ct&}!k5mN)9j%GfCKkmKg%B__(She86QG+cwS)ZQ**}K${wbb{ z)dlOMd)v97Mp@mEKcAte2R=ZNT}K(f|1He6{=Y6f2$i zkY751Yr(;l7|^cGQ~yLm$hPatyITCGT708OzjcAtY{wDi#LQDl)uxB<%C?>gN8P94 zr!*nQn$o1>eoLr4iyHsqx7V?H*UcJTMT(>uNr4)|O!i*rq+K6v>1@SZ`T{I6YksO} zC5MQ)2NANVCbSkeeY?s~?Cz^nrqkyr4;QYtp_PA .tree-children > .tree-node > .tree-link::before, +.tree.with-skeleton .tree-node.opened > .tree-children > .tree-node > .tree-link::before { + content: ''; + position: absolute; + width: 15px; + height: 1px; + top: 10px; + left: -11px; + z-index: -1; + background: #d1d8dd; +} +.tree.with-skeleton.opened::before { + left: 23px; + top: 33px; + height: calc(100% - 67px); +} +.tree-link.active ~ .balance-area { + color: #36414C !important; +} diff --git a/xhiveframework/public/css/tree_grid.css b/xhiveframework/public/css/tree_grid.css new file mode 100644 index 0000000..dc26510 --- /dev/null +++ b/xhiveframework/public/css/tree_grid.css @@ -0,0 +1,15 @@ +.cell-title { + font-weight: bold; +} + +.cell-effort-driven { + text-align: center; +} + +.toggle { + height: 9px; + width: 9px; + display: inline-block; + visibility: visible; +} + diff --git a/xhiveframework/public/html/print_template.html b/xhiveframework/public/html/print_template.html new file mode 100644 index 0000000..a631c3a --- /dev/null +++ b/xhiveframework/public/html/print_template.html @@ -0,0 +1,46 @@ + + + + + + + + + {{ title }} + + + + +

      + + diff --git a/xhiveframework/public/icons/espresso/icons.svg b/xhiveframework/public/icons/espresso/icons.svg new file mode 100644 index 0000000..3cd91a3 --- /dev/null +++ b/xhiveframework/public/icons/espresso/icons.svg @@ -0,0 +1,1693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xhiveframework/public/icons/social/facebook.svg b/xhiveframework/public/icons/social/facebook.svg new file mode 100644 index 0000000..9a0c336 --- /dev/null +++ b/xhiveframework/public/icons/social/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/xhiveframework/public/icons/social/fair.svg b/xhiveframework/public/icons/social/fair.svg new file mode 100644 index 0000000..60c00dc --- /dev/null +++ b/xhiveframework/public/icons/social/fair.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/xhiveframework/public/icons/social/github.svg b/xhiveframework/public/icons/social/github.svg new file mode 100644 index 0000000..6d981ab --- /dev/null +++ b/xhiveframework/public/icons/social/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/xhiveframework/public/icons/social/google.svg b/xhiveframework/public/icons/social/google.svg new file mode 100644 index 0000000..f58dbf0 --- /dev/null +++ b/xhiveframework/public/icons/social/google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/xhiveframework/public/icons/social/google_drive.svg b/xhiveframework/public/icons/social/google_drive.svg new file mode 100644 index 0000000..e1a9378 --- /dev/null +++ b/xhiveframework/public/icons/social/google_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/xhiveframework/public/icons/social/office_365.svg b/xhiveframework/public/icons/social/office_365.svg new file mode 100644 index 0000000..77c3559 --- /dev/null +++ b/xhiveframework/public/icons/social/office_365.svg @@ -0,0 +1,53 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xhiveframework/public/icons/social/salesforce.svg b/xhiveframework/public/icons/social/salesforce.svg new file mode 100644 index 0000000..e4ced76 --- /dev/null +++ b/xhiveframework/public/icons/social/salesforce.svg @@ -0,0 +1,3 @@ + + + diff --git a/xhiveframework/public/icons/social/xhiveframework.svg b/xhiveframework/public/icons/social/xhiveframework.svg new file mode 100644 index 0000000..92791e3 --- /dev/null +++ b/xhiveframework/public/icons/social/xhiveframework.svg @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/xhiveframework/public/icons/timeless/icon-check.svg b/xhiveframework/public/icons/timeless/icon-check.svg new file mode 100644 index 0000000..e94d089 --- /dev/null +++ b/xhiveframework/public/icons/timeless/icon-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/xhiveframework/public/icons/timeless/icons.svg b/xhiveframework/public/icons/timeless/icons.svg new file mode 100644 index 0000000..c9ed252 --- /dev/null +++ b/xhiveframework/public/icons/timeless/icons.svg @@ -0,0 +1,1009 @@ + diff --git a/xhiveframework/public/icons/timeless/message.svg b/xhiveframework/public/icons/timeless/message.svg new file mode 100644 index 0000000..b056762 --- /dev/null +++ b/xhiveframework/public/icons/timeless/message.svg @@ -0,0 +1,3 @@ + + + diff --git a/xhiveframework/public/icons/timeless/search.svg b/xhiveframework/public/icons/timeless/search.svg new file mode 100644 index 0000000..b99c657 --- /dev/null +++ b/xhiveframework/public/icons/timeless/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/xhiveframework/public/icons/timeless/test.svg b/xhiveframework/public/icons/timeless/test.svg new file mode 100644 index 0000000..9563c82 --- /dev/null +++ b/xhiveframework/public/icons/timeless/test.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/xhiveframework/public/images/background.png b/xhiveframework/public/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..45b91e94ea7640cd6ea0df57ff5edd1f14e70bc1 GIT binary patch literal 12460 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 zhvl+EeJdefault-skin 2 \ No newline at end of file diff --git a/xhiveframework/public/images/fallback-thumbnail.jpg b/xhiveframework/public/images/fallback-thumbnail.jpg new file mode 100644 index 0000000000000000000000000000000000000000..68a62d776670f9e75147832597cdc5e24f4392ff GIT binary patch literal 2598 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

      5z?_WF;?|$xU|hlc5}CDNmWoRkre#d7R%RyTrs6 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/help/print-style-classic.png b/xhiveframework/public/images/help/print-style-classic.png new file mode 100644 index 0000000000000000000000000000000000000000..e1817aad28b5e2916151aeb8499b3e0f1cab0e84 GIT binary patch literal 97352 zcmeFYcT|&2w>XNTU_pxXt{_c1N-q&<(m{HSbm_e(MBx>r3rH^!kX}OXBq}6;^xlc| z-g`pGkN2GKo^!tU+;#uG>#lXP=8q>cv+L|V&)&1=iPX_jdGvtc0RaKQBh@#GZwUwp zHwXxBU)&?Q>DjnF-%CJ1XXL1$prfjw@I=SU-QLl~j)1`UeuaszzP`mIz(0}Req(QM z@5^4I8&!C2cnXdDGsRofeEP2v9w$5|A*Z-?U-!=aTR#b?s1=c~qF+Ns2CC@Ap&w57RuFk_S}Eg_MUzK->M=YHoo7pD;{mo37jU9I}=EE_&>8FrT; zr*hsSv3ZVvDLHfRjN@I{p*SG1nmQ(s;ME@+g{(&*Ho|YIVj39;h~A9QilOJ3Grl6YgRWGVVRsZz6r z3d1~&d_J=q5SL~xc0OT$4$kCyKJl@%_MQfWmfTLQo?=b@@vl6qI(}9jMNU^lG+|*` z=N-xP4xa$Nk(ojC)E(48aDvF7ACta#tj43B@es?G4Bf;lSfW~?RbkVz=W5OiT;D@Og=W@P#Q+u(q zEEKmFez@OL@XYnmo#A$F8jld<_)4U9JScGs0|>I#5JJ&;$M7g0k=^}bRiUuR`#|UV zVgG}@e93_Cfc0Tkr?Lm;)Z+G=}9!Fis%M0FVB8`rbkU%8s znXyC~z9Z~$SiPYVeV)%doYlOp3%9_N=(ivFgY`;d!b z(}!2RD723hkJLEY^y0euLEH+f_EphWND%iAvb7qFw$_T(im0t%4dHZzi;~!_TPqf} zFKSo=BcdSNE-ky+KM?NTQ)ln*xe%_B#lNoq)%FVmEkZOTzI$Lm=tT18D*jOW^T%T{ zIwi%u*t|PL$a{1@YO4?*lq5{FlwYaJ>)s{U%FGU3w7gEq7PzdLmYGi^X65APlr?T18G_9WL=S0Q?5M_bl|oJNW~)PV%FBkN&##>%rn*Q=jfB?#Mr5 z6Cv*+cc3Wv%+RT+NWAoP>Ap3?Axm}4oj0b>7~hifM322LkI&0gB}A}hDXNB3{VE|OV^hj zFWns{JV+#TvuFlr)+1)4dy&b@rXFEUl5-3>6oPS+KcyGL4ofa-FQh=?*YwxC{QMQ< z6`U2a{GZMUc1>o=>dqY<-W%Q5xWf}l@iUrST|&9|Ssi80WBB7pMd3WT;V?P{8Rc@e z1$Ow8$|2#=o?(-r%i)3{>XG^HjD^@Y@%gj`;@{Ak&^J(RNNmWLkc3-ZPf+hVUs+EL z>U4WVTV+|LC#35p45tXk9Nt@|M#-W4HHQD0}F9+xh7>^Ms?=vrM|oyJp;G0%q*9bhFxi zmVShOkTbqBsWYoHR;D>74AV8!G}9@QwnT{61CI=ka1WMsmnG1^r=`TDn)RslmLKE& z_1*oyD*83o2G-nGa#rLI-W*CC>@4#vZ?2oHtuBi$$F9hB1;_g*#K&qw1=IGafQ)Dv zzzc)`;1iw-Z*hJ1mfOIiWf>QiiwJ)N}Nibc?@~-c@B!fN|4Wy(allJkDVU}a}7Lodz!;-$$za8 z%ALzqt&*p2t^b(4;UEO2SThJrg1O7^zaQ+1#faXP%d@Cg{)u|LJ=>Gn`B&~2N??OpcEwWs{ z_+_bvzLt-UDp^PDoYtIM{Jd*$OnWvs7sp7f6!9#=QpH}yy^A*v zolTPMm0g&{k!6!DZLwj|W?pI@(!|i3`L*Bd96B{K)w1<)O^3 z_i(C4AnzRly&9Sa{AE2t1a@Het*UO}M5L)l_@oG;k%jx-I(={ZTLg zjL-`?zLvV?x=Kpom2;t=Jb zru=*ERxM$w-eJkplK2s-5v}iFb$?CwoNJop@i6%vXtPbWJ$C+;(q4)7ps&pT#vQe zq#V7E)_6YZO&7tl&*bJ)VTnKx5rp%s@5E%gwtro5U^UxJ@ZR%)x9^ROft`9^4NjKZ z(3gu3F|=DzfzjSk5XimBe&lU6vX~M_`TUn zn%Y`AR#sYhVBS-qtlwogI2kZ5Qz=^MTA5VATjEeQ*!-XpWohyLz>&_m&S|Hu(6eH% zW#crDu}{3l{k8b{g1OTJrwlhgca|2rz_MDU)>V{i^K5cOXvHUg_$heh+l@LMPa9567(U~Ps%xix z^^2eF3Bd=!WCHT}Tkk?h2y*r&)15O37uXbp$YLy>NN&jHJ*1C)ti=CKU1-5reuqkv zM}Q9Y7qE!=85O0?hTn*~$U8cF`qp#-VTWqPs+Owss#dSRJng+KR@@fp*DO}L?ti|= zaPRUSS2R`B!sARTJWmgQ0q-tvl7Jb9I5*xPK|xhJy*x30=9}2}Yu%(`jsn+~4kHJI z+s6f8+^w+2@JtjFr&l4$zH;p;jcQ6)xq$KH^xX8cyR_pu>N=BsMbX3Ff6;T*pVE)^ z{L7*K(a4Nl-R$q#=?IOegL!SaEu$SY`4F=NeI1nsB^!Au{Tdw$MFO))brJiP(upab zN|sZb-mjeYz{pc9QIg!Q!y8?>KUo=H48JZU9C9Xkyt%AWF*Nlv80bzowXtq)2n>!- zg$DQ>jXGjL$QJ_+lmpb%K2&g7?Y3{GJHEp0D{7lM)A0{-)QOU>Q^^c0;3H4GH^OjkS$}K66_d?G6*4wu6SU zr)`F6CxUSyZ=gEpm9S;93haHHLi`c24ACW#18XYTT@rPY!FY4}M&oRfbPHg zIwOislY$HN3k`GX<+Q<8n8XV&pO$y_=jB5?S7nSavi?BCa3JqhoE1y^hk$tC%JsMF z;p5TVf|NltycDEv?1uelQ4QqBT-sZ>vHR89IXg>V{~9v*OFEHa`tyUwgh|@05@UmIl8wX{P?H$)-xL$pihP zl;h;X)cao_q})q>kQ%KYWf(agCH}@d)g@u;%U(+06KOv2r+D+lPfOkf4!@J0FPCbM zDUT70vb^_m_I;0D@U$p0Y&3_x0!%)c7h2$+3P|nI(gVLx^1k(uNc~;2?P*`jY@z+S z>MYm~envz3ATCSm%V^tiRj=#hgGTdnMKXxC8H~BL)Bi=V!U8L}@C_KQx$*#!bWMM& zZ(o-PCnOl*U38SIu1JumsArkk#-h6`@S;|O=}b6;o%8HUw3|O zCw}~Ar&|-&{a#igUK0ZIuSCPPNyWRx z^D{*oa!IuG;ttN%soY8?IP_@D>8)cFp8nVyL)#~Kx2WC$WUmcp$W99I zUSUof3=*un*sz&M#L-5&*Y)xO932Q+~L0{2Z+A zyZli0m-NT36r)hGrOXus=NDc&KHKMfd~Nsl^D~e~NSkLn&p4h<7KDH2wLSYi(|!Lp zIAvc)qH-huSy5f--2FkaI4342y)7ieGtH;{^cB{-^U_WJG22;V_2cYYkG5&)zC5xc zK`_jV4G2|>*$HQh@P@5Fg|%t5;f)2aBd{mVt4 zLK5!f{rcBJ*R6u9%`6FjerTu*OTKx;lDqD-z1NR%{FvLe>*;q0;C&o^hGnOspYKClP z-i*&s4Upd^hg;#=X?ml=z71eCWE5DDi^3c2#;4_ggtNf$pOK zTrt6qxO*RxIA2L{+}==n@nLQdX90Nf;r2@p%?QfRx9ysimTtd(#Q;iayA}R}>od#S zzp}n5UXWZ6m@0d)lTiHpob*|&^L8SkV|a{;;ZtZXolaq~?S)3e9cV}BpV~<#Wh%HF*rPfK+vM)(qTD~-R3Gi5JVwB% z51Lpb@-|R^HPI|aG@Y55`u-(g>^og|211V3?O-Lw#vHC-9uCzf=OU_XFNaBa@hv)B^KFV|da=_?LF%Ju=;#x@0xcxGeKXFFe6BWfuN0=dl zmu4N2j^2)t$D#Kq71ga7vl%R8+eyHpVSEbg`7}1PNwT9P-Y*|MQomm&#LpEiKoEUE-I?5G(DrRB`Z9w@B>Y}*d(bNbBKkmtO?kc&_;nPpI})r z$$V6%R&ULLU%=SVRMfa}`eYyEhMi{F{jprUXS>g|nYcc)A+`ozND`lUt? z2!+)c$t^O`npRNRiHlonS=WSq%NpsSmF_kFz-KOtFSyX_Nv#=IFYYZj`tB67eO1Zg z)E0!Zqg>`co~8G1nRS%TZV@D3IIc*{bSL)3Dx>ExLA!mQPUKLDBvp z&}RMiLR~xifV4nX2;kt>sqSvl{z$^05h5l8*V1=laTYQCz|j{yNa<{_?JtfJqitA@ z|9}YLd~y$hP4T!Z@aXF=gM6jW)FL0)a^JousXW|xYE4o9+5e63cfgT|7ezsg+Ut3h z+FZh+Mj;LceuI&}2( z`$9@W(ZYjOUu%eJc&c5i{Jm_wrMx4(Dm=&z^bc8<@(*SoyuTN9zxpAiqEOx&;W`P+ zP^GszY9=#k;cS^4M!bO1s>)}DF0dM4z3FZVQEXvL3gT>&d0whF$Qt8DBpJS|jMlW( z2 zI>N|Cm%$`PUCmze>;WS)#S3Z&3UrN;ST$dyv?Sou<&U116DOfzuhT^%#%qJT%;Wf& zq`!0pwjF-|Su%TB>cB9_LuJnURvK;U;&Z55qB0FqvgSDSnx@{}s-D>hDlD61uzzF#whh0XZE20&bOWl)v@=_vgPisv zOhQYjnI!yoz3D~Lro$OSU4y@?+q^vvMYNCl8GET2)*sprGZQmQEk2AV)iKPgWFcUq z`?cCdYi0&S68y*^c(;5pF=4tnF`-NknhGF#6}$n%dl&bO;UlAhN{Wlhd_M)@&-d{J z7EuFg8Pn_u1O&Ho9QBQTj5Rf+Y}{Q1tZdz_?F0f`J#I=61OzexQa6vTc0N{50$g3( zyrlwU+5TgO)XnqX-GXdS{$q-dvn-pjrp^-ucQ3mqq5>}kgxKUBJbCg&#>>`T>aC*k zzrb()$+Ero@$rxn6!iD^7w~@};O^xhC@d){DJUc&C?djtGlSnd(9OpxfZxrV{XdcX zUw9PlyluQ3J$xM9-Jbl7*UH-6*GHC(?QcT=_4`jg?E)PCo06ONzr?x`Q1I^&L16(Q z!T-X211j@(uau5sfSrr6qNA&woA(V3Ibk6oA({UG_+LZ+P4a($n*2AYxY)lz{*RG= zLCOgJEy4eg=s(r|C(!uhUO{a%LFiqpN>T`8IyXQt19wO4g8Dd9!=Dr)m3pX7oRmB_{eYHd*6l zRE&F&pZ{`B=XhSnQ)~n2A@pwy{mtJ;%iE04dlqy&gSaN1TdBX%pY)B}-V)cDsE7j$ z1x^k8FVy015v#`ia2eNy|D z?`aak`JXD7nz>)@d?c-dxZc5R55lxxG&G}wdNQ9MGIclMrpw(CKSZNOi?y#S|H1zB?et*5&V<=56L$J0-aP+G5ur7YQ7gTSXJw+$wMg-PK2APXD=(g*ra zw%fkkD|ZexTQe%`H5uQA6?HZ0qF-6Bi!mxLq(!d1LOI#$!Q8)Lg8U@AKTfTj*&T&{ z)}@)0`d>24-6MI~a$^f;;#&FFHEut_=|JKZ2d{mBf&zBQ+woN);tpSzvp4drUzT7IK)rK>EU!aS$ zhuDm53yz)_ZNm^Gro%;30>?a~6=gsZzU_ay?K^e0A)uGpzIjv#(r*}64w$?_NL<s)RBfk6V+P(f5KnRulh(r1xQOcev6@^(df-8IN zE~#9JMh3u)J*Oz=m1VjArNqZKYmsPrvJK;>X|@nZJPMAsUk&`@hHZYvqPL_wnIf>R2Nx}}Tl1DCzi;4w zulCHnmufCZw#J@SWt@_9y#Xv51}cvB+}d2xvxmV8w+)}ApIWDEVn<7WqFiacPN|z? zsgvDb=j2k$+Q;$UV1dcnp3{_n+VeYS4jnVc7IW-|9VmJa~(kn{Fc7s8DcgtKJ)|J20#9sllgIRH@!TU7J69%RF_KdUXyy zB|b!F7c!Cl@2;BmV@>UwWlibj*<}39DU?b5(uyBauD*n$7gpShhYGx9ncjqdniW*$}0?^on#(iSBgOBHK2;3Qr z{^fg}a+6w4eWRElAEopLHMjUUF{SF~u{rpmRVBEcs9#`A5Ij)EHWoN0?!?>hw<#eD zD=uk#>yEEwEM=kAAmlg01enuFRBSojDW;l-7ume#x)Xi)Qj3MRF@db!dbL3ba+xxY zq&o#2UvzgmHE{p`SpHwri%js@jv4%FRS>dU2E=U9VtFEQF$C}H%(Hf4Y&K- zi0yWRq)u=tYcLPjZV|kBYHz@r>x#9R*hp{T)WB{O7DquXLr&WtJQ#cKt+S5o#)vd4D6n z*|YwIJQ!a~S|_IO-0#S$u5H1$HOi2m0<2Uh48X(M*Gg@F??YQ?Bpo(8O(r=Sbv4G3 zax)8fXW+v$ua!Y)7IO{hbd;y%Iw(5GqR~7)hKyrh<%T1G)O3&Fq`_@FJT>+`toO7l ze%=3hHY@(3Yw!XyiMWnalx!75!&^n{G?5>R8JHT~@I`1c*o<2A*X6Vumd4 zYrOGvrRFJ4ZmCU<)cWyM>{#vW=|b>6Zmwb0E%;9K>u6Gy9#f&@cd`#f_f-nt>#nJa z1ufNhjKKl|L6@TuIr&W#)w$fHHK^Bx-1crs+f+%C)Zj7-%+d>5>VA{!Ku|xuSV=t`SMpK9THnIr1-hySRNE%r_avUP0|s&Jnd| zv1PM+ak;;an26Zps!OQX8Y^7uhSt|ufrpFo>I?l?@->Rm%6szM-Qu(2Bl%b{N0LKD z4J&Q2T0z!N)|I>mmYv+j?BqsxXoPLm+dUS4p?4b5A^0ro0S)3GNBJW9g}e=RW@GEF zJ!4tItXjkQd(?q+}Mvmo(LrDn7Th6uk3%msk|qP?-2mLX|CKTLz>1E-F;)+(;Q2~hwIcg?>XvS z_7NbftB8;d$_gNkvsCQsOl)wZ7&a_ZRD22Gn1 zB9^007`!Wes=IzydP!B#%w;Tf`z0r?J1`;pq_fYrTMBOu-TqwEyyoQ=RxVsjZB%ph zQtmA4AxA22)CqJTdu9@`e`{qMXz{e1cS0Gyw;`{cz+!o6`W7Hn!SB)=h3W=>skb~d zf}(n{k&%zQ53XS~l zkh?F0`L{MYSW=ovY@&8b#s_>yC>561g5^?qZ;e2vbc@^Z?f80@=h1Aqu8h}3;GFG* z0<=CBa&fRI?77t0F{00+9%1=MZ(YU7a3<(h;<;*yrimH&W<8YuwH`nd>Bb7)Nqa`$ z{)sK_W8SSXLrwA(>(=7b*qtN4bUu7g59VtZE96SN?Ssv*NoWP zzJvy>gnCqzvJF$KVnwvB+|j+qbv2%Uc^xjXArqXP^iA?lbo|WJn~CNjd(b9Q%e9{)K0Yp_ zUsG(vS<6XJeWK~&?R&k?Tu_1%)hq%kQry1S(9Mt{MOZ}$JGD-ffH9(7(Rv1 ziVMP7kCp&zSkSwo81-N3Sm@2DDSqBne4U+{I+xT>1WC6W+4My9rT;q zx;}Hg?RwAko6?{hBiYrO_~FF~0JYQAv_~PWmHxPu*+-}C;`3x@+HNCk<3gKJQ+PaA z>@+WM5ETZw(ChV?1|#^+0yh_{`~9R(1|qfAa${kdl}#=e&ByDV z+2;!^WHKS{GM6Vw0iEBo{iA93;gGhY4@O;Q%l^fkI(+?~Vk>D4r5u)qVR^^7z~gU* ze70gr3(B0z!ZcFMG1*-y<%~(zI=rNC2F7;Kh6CwQ|E@R0_Db@ge|LxiD2A+l&O8k{ z=|#9`DM9K>M8kirR(oYHIl)gH7ivbC#yAayS$1VoX5iQ3^#UwoRK}`YmB@^cYFaaw zI?m{8Cp|qcsElF33mjMQ^yAc}ZD9NP=etbJqzm}uRb3Nt7x>GPd7>~U~T+&0iC=U5?vRLMXx{_aDX&gw1}v*RnMh~4jmlGY641dU zJf!D^do@E{q>MkcI8-)tKBvYV`LUaTOCw{-!~9OEouFFan;4&kUiyKk>*JHmI*-ji zobtm<_D=_!&KH`tBLFc)*FyZ9u8kHNGge){HLaD^W*qu5X!aatm4~+*|Kc;)l0Z?5F zzQ!?I%@G3Gfkx(MyY46$-dvRK#p)l4)w(idL9h%w7+X%|rg3#D$r_K4^_g5~@4f_pmK&FVh{N9BC0deO zeHRYKc;TOsOW>b+qsIOBe4nm0Iz-(ne;?K{WAgFWF5N2lNGJ2APR zvt6Qa24ypo!Zo>T#s2uo)1ekSKA)gR1&5pK-=F~*4{;l!aWge4+QX9XWlpAap79of zs1)Eeym4HBlkSYxt08thy%nJ2N!`tX$HxVHWUJ5SNYn9vw9QnN)dR|B8(qx)=Ka^` zn-JGBmE_G(^nS|C`(C2~5q4Nrw9;UpX^?tOeY(XvN{h7}t*XSec`1x848UW)8`Tfo zuD(ge3*7aOopN;n;3<-u&kQR0*!a2m>&A>sJ`4`3?rT8s*y@WM05T#DAmA4?uRH8F zm7Cl<44&ORJh@3e-7O$`bJ+sZ#jcI9)zW)Yt@4pb&cz~GK6~2*N$B-9E9zUsepKzC zjBtbWNfVuz7*c)IqWMpw@Oc~%MJ9lA)X;Xe8WWpU>Y6%C{?rvd96oAZvM!KDIXNHX zdu6u(T&k;GVCdQp_HjX1vyR43XPOSWFD!sp}|i_OT~Nh<;+M5@Z8QeFdUK*Ou(yE=6}v(17c#7c*e&DO}M1;)F_ zt5RhfG9}ghHt)(3&)0)amw6d`ri96&R<1Dlsbr!YP)3pKBc$@AVo)~B2{jQ&t}P<` zSw-`y;a>h>wGjf9$Pe!x3p78)V$RzMxwPQd3-}$bGZN>IIK`B1J^(raq!ws z(~)*-CjfGZuvxL7?c#!;hCY9pTtOTBc)ZOC}d9^LWGi=|@-F6t!0%`IB{s*Cy%h_AN+ z9Y?!I>ca80e9yC*Ef4Z8u7O}n%q7s`VbFkHt@4RRiY3<4cmH}nkXgT(_E|bM9*PGu zJV%z12;t;GD_)0moSP#&}A3XXbU#T~*$O!c(*`nHMLAKiICs?>R*rd)~TENdSSdOL5ImjoTg;jd5!JWf0 zq;@FwLsKJuanf^}y|L5EJ;0=G3rXCyv=P%KwI5Hfj(@B^in!0i1DvkryB5ObOxbsD zOxcVqBnPxizu=u(Rkpw8eiTt z)~N&8E4w(FoV!U?v6x!Fqtnp7pHf!4L)!8xnS|3=#3c@~QsQ{nI}44C2XBvh4mHen zvJkUS?qdL0R1~t;`ZVLx^Rx?26%U&cz1}^7p>$j0BI6GAmZlmJA}rWLP0z0RL8Lqv zTmx55V;>k7ujommOHt%ohG zvg7H*7Y^U=K(89ruUp(5WesSIF@ra|{5oB5xv>uxO3YouvLR zS0B3wHOPcnd`|dV_H(8nE8SBG{$h9#goL41lf=y()(7!5QmfQUgfExw1@wGQ5cbEM z7$2{R8(kgPRg>S`;n(a_(nix_i)i23DA#wDatLax6W?IRL8@{elsI0xYc_tou`aG8+o6b{F$&|=obR7WRWqe@s+fZ2FCcZq-3TA62 z*O{t;SYo3+4P=@J9bb;Zob#Ov0vNdBqvJwi^Dnn###I0j)=nhI@A0qA;8ligY7W`> zmtx*sK+&}rn6RA+BV)aRO?=L4rUzgq__YCN6*(=P=LQ4&c95>#7*|{i4g4C6t!tt| zMw!ag3s_-ZPvaf+3XZp90CSk4G|++8W}lSJ@V67f#5zAinLvlRDxWtyzB9y21!3#R zP~^C8qiy%VA=x?;vp;0h=&(yg%4V`%`E1XIuA0a|1!J{?Lj-dZ?O&a37{3&hGOCE= z=s1fU^$e&Vm=94H_0HwBp8hg*{G;9a;2grL?aR#4S^?tR z1G#xHh0LrN*S&WvH=U<e_i%E#_4I&T5)YC(L-EDg991Ic>egY+1{+L!s3#S)CaTA!-T; zTbH9y$f=T7Wd$n1Qf!RsB#}2Nw6YxNM_4MZ7%KPWqfe+^!^tYK*oQvf#G5)rp{9xY z;HQ3|ac!i;*Q`pAwepT^jjU9PKs3einu;kiMTd%h=pz$2UpOZ&$ zAQhQQo7y=^$D1NUDZSg`O5)DSIchi5hBc8b!coPR`WAmg4~4c`@rv$%GK6(rw^$ND^1gVcVVeln8kBs|_s~G@@HKru+-Clt)UM7{;S%4tZYor-Z{qO! zSzl%v)k&b4#t^&r1;!{aGC%DmX@-1T#-Yy@(m=2pOYZ(1{8!!6U0}s3eX>@XBT?b6Th+LbcCR#?__ucI2xAu1Dy^%UkfXjnFI~GE~$m zuZ=FoYCr2`e7;n8=g|r8YjeB-dsopRaS;SLkeL86wm+jC7~ zNVB9pN#}_Z4(Q-uz>~e;AT1f+^ZLVWY(!@;!bh!65J&-r^wsTOf0yOT$x7OrKPZ6u z?KT_kU$&6$7)8wll3ek0{)%hZ$KQkz$|oR2dkc?m%Lm7Zmi-DYyEcrtjaVtFb$u3c4 zrshy|(oGpeq{V#jnB1c9ec;9*)G^B%>4pY@bUoTG(1!cpCE!<(sn zde%7$JlfFeKh&>}v~owIQxjC@IGNc;qM2x@#A)2jTLL}3^F7!Z7UW)r$1zeXpyJ|k zZIrwIEb;`qsUNA^h)-eb>wBBZ?K=OFDt-_ODcL1Zp8)bi7zkJ}Ul)cXm1?X%XgKFL+};x{3=YpTn&nzbja zOB6=yWu}MkX`=^-+t)gpE`|zG$|AxWsj}dy4G=H0Z1s?HT%ab&+RfSOv&ywsOq*aF z7-s@tGNmNS#bs&JfOy9=z4pXu(LVA%J^lP$=}Nt`lPqYHqM z?&yQ)i@A2h=P+%|ySC%cs+;V49nY(YU`y=5o9E-TIE2(A09$PBlj)m!(Mx+7F(5V%!?~D~0o?aRVVP_$psaYn!Dl+TG+9&G z{j-xv*>I9TZg3k|lY~(Z2Txk%N!Q9Z+;=ETF<4jyw!M^{ZMlgpKuf{G7qDyL0f3Wj zUwTrAD=)`}YsXYC7(l)QI{SGtfa(y2C>uq|auYu@cXt z9o>H>^K%fflk$Zr`YVUFMYW+Myhit+&KG9`HC;&yooX+QMhUWsS?K54i zhnj>bmK!vIA(>7z@|a9e^*gI_M>Fzh!-_Zx>DaQ1!zC@jxgZ+#{pgN_**%8n5fx&t zSSIQ)Wk3BmI;p5*b?CbJT@Y^y@4grsl!(z@To$O?%pV$* ze@cXrVR6kypisc9!^L;zQK)1Mm#YJLRQ$0u%hX5_?et=bH<(Ja>n8tUimxsVIbtl)t$ z;~WePsRcPuVx8TyFMS_MEh%?M$t_daE(DNqHD`5_}NV z+x}NDg^kBX2ud~*O95`d>w5pm$L88QL62A7JHTuW{)o~)N$wjXb9PDZE>sLlk+ZOL^twj4`u zI&Ft^xmK_LQ4;8pKm8OSYE@XlR29@w-jMvgS1jG&>hsfV+wrNDnwhb{pRBEq48`>({}Bk@p37#tgP%^ z1xDJ^1!&eezt)$TD{>Ia-(Mq;H4VAK!MYAHA%`LDWa|7>*4#8Lx|C;e>p6I^PXVn` z$4Dz$xQ%^+1YKzTg1de8i(F9}hN!D{Ge3@bc3&@0{@2p-T@vrnXlk*?$JuSWEQeE9 z!Fga`vlum~9jmmDMvNM9fhz}q+nAzvr1eSfx*zi9R2mrgQIS=g=PI4jvmoD|(t z+uOG(vuw_K`Z1#rr)6ybu@Xt$r6`N2*k!cf&P^4ZrlC*CbEQnuOKWQTom@<8_FA|G z2x%vCHAD>qG-OEQVk*SsI>$g%@gbOlew--T(u(l2b#G1q*Guwv&zJ6)&R5G@ZN)sD zUo;rebI`P#5ZkcoLo5t}<^mhPC}-W}uU~v-5?q@1249j5Q^nMw>g}n+GK8yAW55g0 zu3v^&&ET+*{)Fu}X2iz{*=x!qOS}Thu65gppuoO#(HAo_{*id>AtWkz;e0i~Z!eE7 z^(;DIWI^X#+|g>!#1Q3H#`fAw=0o5K}vU)=1O;cGP0z1m2IoaEw>Z{NFR-3=QBj3m#2$}x;FC9<8-r*bArF8`5T2iQo};tOdhK?`nyQWobER{ z>|~=b%gf$NLy?p>^_ZSMMCYjL`R_yK;f3p&fc;=HHAF_el;L+)BEH~*jElWk-sMN` z08!GkG`JqeVWAJd8ml|76GKlG+_q#Od=Xx>)U4ENgKXL&0~~L+8d%_S>WgqcM89Og zOeD529mLXTeA(bAe-IvvF`8&Shlp|=Tm!L}01+^nc0UIQ!JSrbSGE$l=IkeNY45d@ zZTbF{!`vo`0^iDMi7)IT{~{TX&=vMpHPDjF7g>nOD|CvG4cYLL6y;Sq94k~dGAt$A zsK*XZ9`i)&m6pK`OlJ&A$80xtvivfvG zjNz7by2nFfpI9WVKz$HDKR(N95!AjM2Urh#L6-&R762Q{8a`rq+&i)Xd4u4U1AsHJ zq$(`Qa$1(S%{c4t!q2OXZNKVn+Wd9ACPS&7IvDNB6%#GrpM5Tj((jheIz4y>V?+|*2gc>>|kmQZ~JZGPS-~AW7pYt1G zB{OrcS#{=G>psqz6%rncoA_OKNQ5eV%1Dy-lQo+=j@^KSc>cnSP)~^2upXMuZKk>i z$qVkxV6LVRzL#=d`8+KB;8TH$5Hdd!7i&-9Vo3>z#}>}LOv~f&+mVJxWcR`uLAYDg zq(`EGm+CfjmCn>=2Er(_lEk_X<2l_43Z2h8cJ;~RShkmI{}kKf2VyRJg$ca-9Tplr54=(5$faZUnU6Jgt6Kb6kqzP` z8m<{vGm&%fVz7}&(`)66i{nnmf`$yAoabf7-;-7>BTK^Yh-Vn<=`NJKH@7ZKhQOEx zkCXG`Mr~0(H-{uT{)PRxDT_t4N!wDLV>`LpYQoc!NHvu?UeQ?@2q&rgyP=|8)3#d2 zO#}40qZG;l#h-F8IVI+Um4k-t#h<}N56+{j9|^M0M^cDWiC?syttJdc2<&x0-^v08 z823Jtv8;KEHa#c-PZHQj+PvLq3^f{56B>}Zkas>n zgLy9&p^GKhV|Lt4Z?l*m-Wl`!?9x#Osfs$uvE5is;EA02-g{XZ$G7Xem3xur`AeXh z5hK9nZ*h6@#TDhJvg(qY)kk7xOhZ$0*@Ng(Hj3#p%eV_()1-?N;e^Ml3+#M z)1cL*?6pTeyDL!kYSpvC0>Tq1Mq{w)K2qZ~QDCZ#qK1n+(@_Pue;^iHc}VG_1uN9v zKj+}KK2M%C5avBjq`Ht zB{|9#(j9s$E_gbvn?9WqNm>y-7>^mLwsvXsfNFKQ(QX>VyC@^S$YjHx4qmFm`z{jj z(U0R2apo`dhFK>A5oSd}FD$Amj|?xROP#P&jb>^kXCRc;XDW|^%ccJw^)&ZwK-0_g zBBYi5v0AS;RY1nnd9IQYFmt;^@B@#^fRd?Pj&@k)kz`;KYDp!@rY)XqLyCQ%00#43fnenz%Dab2y)@j7a$@30PS~^x+Gka8Ix4yld0Co9y-0wG?mMP`44B< zFX9-1W@KNW7eXz0J00kyoLspWq+M#O6hAieWbG86k`n*Jqd8!OV(`}~g zBY$b#Eq#!)f}ww99r)-JK5=k>z*L*yHJA=!%xEfnEc*@Q%lTh1M|d)G$^&OF%R;14 zFQ?LTy1>n@L&s99=D*P3-Qw9!1Q>HryU}VcN`c=Y2oe^R8S>&pHyzDfV9Sp!XT{D2 z#5)_rkIyz$N;P77JjY7H6nv5DopDH>4FUx6GfkPP7Q6n)Sy-pBO+6-=3!2%RrR$8* zS1EF}-Ykvd5b}N=UM!B@d5H4}jmFuq7jsK;k~mlh2&+pB5Zj#2YSOHBTg$Ba7CA}A zi}popUEEGOZ<7s+>f6KeFM2yONd|JEMOM$z&NC*V&0r{2mdqnk?S?}aJ>om&`Pk?6MvFOcQ%sKUOEGoE~bHm#zxOE<|6OmAeS&YPzAYk;KtQ4Xs zZFV^VVRLm9I>_!cp2YNYhDGyG_-l%{g8j)nCF(@gxSfF%bgLJ1?-TXUX5O=DFOvgj zV6b`0kTD(Ohc{w?HZg8D2;Qhs7M12vCw{$-G2{(X1l=p|XIa9U9Wk4Io~fx=4|e_6 zlm(B3E#B-zV^a5uH%(8Z@b5=5Da0-r9{qscmxU=SPQx!ZSNk5txnDDUdk@I~_|u+OZa2 z@=}O(_cwT;%QWaQ^|9x0ktmJQ5zn-C8L{rU!rWkmbYd#{`R@1v6jjm}V*GDn)1zH7 zFcnY<(3K-3y##o2J-;gBB)Y_AlEUV_4I!01+2m`$OnG{^A-b-xc@E?|4X^K}^xF6$ zT4lRlBG#3?G}KCmTR{=9^CvAYAMPM%gw#Ehc1E)!n$wrkTd6X~{myMyG0KXK?8x{W zK^OOu5N=Yp5Q5F2r~!jg5ubLY5OIJvAtIH&0%Y*0&ucKCOpt%02$x%8Wg`{^-DsLc zHn=t;KGM$qUK9tBs|@IUNMM8U=lbX?rV22m8a0YYKc+LCiG%d#z&b-xoe|35oaXj5 z1C!xs=`xqj{3(lzk+S_@mQ0)BnKP^;BTL7gaJD#?&}t7{-=N7TY}!#ijJEuBEQf-4 zE}aN_^|ZZWVe*+`zhP%w-XK;rruGW_T0U2Yun|aTACDBCzEb;y}J|@9!tAtvntZ{Rxh9ju4IJCQ+bo z_xF={U0_7xQH0@0h}B5*I@<1s3Yf4_XfN^#FXg zlM`C<|4-gyBZT4zG<^=$ZBF7QrqYMD0Zy4_2HYO_=)0lOoPnLrWX$HXrz6uCHOs|2yS z#cmx#-+ll-=9?e8@VzB>7I4=xm2p$}y=FF7gxGN7jQc#5VixG#7;Td@<%OXJu`Clr=D*#4kYc)!z0QjEv#|!0_#8An`X3 zfTX;l83$md10r+pJ=Yt#(cqX_Mjm`?q7dtWz^Ulqo*NbsQ=9&{^D)R&_Y3&`Z{q@I zi4EiC$DrD&d`%Fl%#sJTSe#oswGyZ#*4$-W3>PfTRHC{3;mX-z8gcHZS{pItX#Y5X z?;9WBGK%+hGhP#>V>CIB3bakM>RN|MZ3dACb7NvaG)e z^|W5V-HdqQ&~){2jaceM9Imb4jDB8CJv4Mr1=n;1i@+jr$5e~k#M*FVr_hSA&whNOZ15BRtWXd@>+kqto9^_!U9Y(rkp zx$ArNAQiP)$~~V^ZvIPw;WGaq1;V?$tC=yhc6Ws~txQAj-=bdWp``T*;iBH$H1#Dj zX`@TwbM?(+;PeS&5w*MP0}qx~Vown+8_rvbp@**r_@+UPxF3ZcX?PiwfH!1Ci0(qe z>Ks2uRBn>qn@Qy1s}yt?yiU#C@?C5*s0f)ccJlQ0H(jWzTjldadcfW4=(4A&-kUip z2UctmE3ahlIwtS~ti4@Q%AWpv!m>=}*ufNSxccj2>*GVoK+)z94x@=M=_qkiM`i1O z-QfDN>IUjE7Sa~cyKF&Ie-H!idd0dz{ORpbKXe=_^QyY6c=+sWEAh?PXv?Y$N34L> z;c7O-eag<`jU#HMzryYxtSN~5gsA8_lJQ%1;{Jz&aG|!vh8}-3DC5(17#+(V4x|&d z-esGap(bxX22J9O?VpwShA4q-bE!fZfL$g^1-lpEGx6=f4A)%VwwV!>WRCAV>3TqF z`R{owNC7O?5BiYAW2wB3039*qHhL-RUC6=m(a{y7Xlw{u@wuE38cBYKz*I%)S6@PX zv38T>xSN8kQe$Ir)sBso{)CU{Hfz-jN$AZy*O$_Jy4D573q7dgO7k3D0)8?0Cs_GT zZYbodd-G9>gT+I&!+QhF!<`nA!?M(ZB?n`nG-h|JX9ULCpIug#m8K7b>zSsFeNhvc zsjnMT(RUe#I}5L*X+?$4^h-HIg9{vkBRPa^$+Qyi8W4-iIrK295Z} zM-NOwYDJQR3hJ!&Q6149A?Zh>QsIunBPHjR3HP{z+v5+Vx@5WYHl-qoPtUDH1{8Qt z&rNf_6m3WpJrdH|*@w-&zAdyPKt@bOO}>(mDFd2)h~E@qJ3fgt$>9#=nvt)%gA z=Cw2v{1?&!8%6)YW#kjgX(6zXf!exL6|_~}45&WT$*E6BtFudBr*(a=DXfx}@Dio3 zWm`}Z|D%t{$37sLeyRyRV^h-f^%i8~W9$2|Nd`0j58C~sJu*opCc~yu^eAtsZjcyl zM~t}R#3ZD`b7_tL`^r>hLJ8jk%=w4?F@bKfmAp?CWqJE*Bi=4V0Q@hH4&%bRR%31^ z;_(KWypkny=}-mHWp})p6qxFzoNfF>ggl>e9z1qbPRzV?5w%(kOsNe@*$6T`9bL}x ze93i8W*fyD7xD@n7(QjyIA>(4X37RM$=k(YkRw-^tD7-ZHj%uUyov*;0}P@8Qf6NX zimIZo!tW%Cpn;on(faBJ?n|q*Qs`XN;Ig5QQuKiExCs|No-_?Ki&Bou7>P*CJ zQq^&HWb$!#*We$-nb&)a5UwTw%xBW{`TWGl)u=44mZklx?wMKb`Wa${?idZ!UXST` z`GuAj|BN9-Aj6b4Ke8(5{^bw70~B_^!S{&D>MrrCaF>0xh)d%yql{y+4Xk}KxVomb z8EQQ0yUjv}IBdE7voS5*cgFa-PO}-wHF7_mkFML99d|N98Mj-A>cBS*PV>zpvCUvy zRR%Vxv(qGfE$LS^MGS>bv)pD*nwogoeh+^G#nWm)5JSaFt8M0d&j+E@x1@z^;~DMj z3vn049XU&x)P&U65+3W}P^T_L=ff&V@#7sa{UY&CJt9kIi}p=ldE<@ANLsVcQ7wQ2j8S#k>br3%JBW zF{Agn_w0e!JDSy!X;@cVLmp~jm6`NY=M)xi-+Kg$jv2=)dv#fvxb{Z0> z>}03_s%KkO9aBkHkrwl8P>i)Nsy~H9*|?q+z-JB6iLsBDN*+aD*pS|#-m|AURBOnk z#x*yAKC7_qak%s8lQZMe^^CUgF9G;LcS9M0`)jzOH(P5a@AI)(E?0Y5Fr8nw?$`2k z;)DaI#f&h}V7fqRmrO}5QlR6zq_48Mb1Og0uW99_%n0bwV?AE((lO7TNCEg}90vv$ zTGowm(Y}Hy4LqerlEZ$UnM%##PPmM`0(sM`ewNTqQUmVVFe74`CMl}UfVVWbDj0+j z*e#(iB#m`MaGfPOaj@PlZuc@LEW# z&}B~9WNQ`R3!bal6Y?Hh$&i$$UU0>ed?oN&qfD`TY0_bHV|bxsl+iIfGtw~Gn)2_G z!NSKOtdwI9F8@tUo#Hs{O?`>Sx(%4Rajmpoq9vCCP*hpFI_K!ukNc|(xu`PeIyiD zrUxCZM4)^-e9}lclHy%F4St5&bxGx9E_6ziNOAAG{)drX5&W2qg5|*tY+bsy;tKVD z)GIPa9M^>@myM|Px=f_rKL-|&xTIuRr~Js~^eG$Mg^xXLnyI6IMfp~!r+k3xiKG7C z)yFM2`ABaJiDSL{m4MVf?@@Z2KOI-QI=>Q-W@Kh&3bdE&M@Ck$v8bCwfDR?sgGcjX zi%1-IoKj}$d^llfIRCrp9|Qp3733POWE#fTy~S4GOzYGnMOmm#37J{5^Ra4U1_iNP zt)6Dy|H7EIxVkW>Qdm*Jlj^Y&4A(EuI&&Yk97?5yyU*IyJkm(YiouWGj(P{<1xp$B zKa9!O%ITn{?%5i$Su=?uM(GdkZf!azUGLeBN#hAN>2V$CF8@79yn@5i!hd~L&?=lz zY0~*ABj=gfJYaH|`t@pwbc=;TTKo?fQo78+StwZ0c|$$gvH2M%*E9GrImxjIgiWFM z7pX~(`{Zx6ftS)ca$bggMDr5sbYu0wji~KOJf&el|NA;25@dR-KPL{KPu9O1h1r=! zC>b8!kM2_}oLX3mIr&GCGwY2tvd*{qyXDOT%WLtAUvAyoQa1lMB)hVWu?GO z&tCnDF`t>N-21S8N;U~|U*olXy9b`!Fu}Ou=8za7u26S%crX$Gm>%Y;({?z#X+ThG z&79Qtuc|uEziL+XxUMrT2wr#bjEJgXibJk*^L=adMjlrAmZo_QYq8?0iM>_q6qzV( zzUAe_IW&DWz;q^h3_LqP&k#S8qVLCm29-FQKG%yPCu)#Emlxh+`oL*YkP>vD8$9+4`gxG0Kd5k%BIbJ$NP;L zHUV7C1J-;LRg+|5II4gDU6fyXf~y6FdQ$7)FaiNy=Idm2yA<-p?9e_}`Eo(g&Av7L zoUe~BUQx}O^w@>^Y)>qx5(tsfyz6<|W7zzs1h%e_Svi3&WkDLMOnd=wCr$ z8i)pXcezH>2=li8UkY3O?uLB71~JvEtY{C)7#HQeThUqky~uIMCd0o>Kyx#ohrij} z#~k%4rOPUR0jHf!{t4{Tu2mEG&m3TGQ!p`U9lL4Xd|sJ88>ThSj1(4F_x$|)ueIb3 z{hqq5Ih8V9F)QM~6t=YU!EnQ(e1D$YU=L5OV7KthjP)3+BW8Ab|5idY2Lg}%bRGT4&?+Gkd~Sg$hHX~Pcq@(aJ}$q6NW zH*3@`p!YJ9Pfddz%7|Vkwz$%0fw{DYfc3d0L$q9NRG#3we!1SGHGMfDw>7fE`8AeG zds5a7Piw7GzigJmV`~^XoMdONnMIw+f{}iDq99n%7>ff%00JmZ%voY*$u6v*V{%;7 zRlzIIn0 zAUVE%KQ-*8{OJuf6{8`}Gq#v@=%~=*&2Ad`dbzeJxn4mc9Fi|~u(S2AmIKv>H)<9D zHcZ!92v{i7*9hvPk2u)hbM>7#8LYn+oJbAe`#m-2db}R&Ru;Z1!-l$W93bk>!vA2F zQ=h7$0~DnN;KJ=>=wn%-Dt_ytXDO0$gD1*@1ofd;ZK5ARWym$bz(ZoEhw`bQO6xfa z2*-xIHH=rSd$zmtm8!uW3;l$rQG$qS!{bF)rD2;duSehx(Z`f_P6kcQbKf^}i|T`) z+iR;eg(?r+{+PL6=JpTo{E7TJT9-rcU206Fbz?Z07Ba>9KCXIeGjHVm)=3`Ny6O~* zJ}KEPqf0zd8ityen;VDZ40l^R@hMMVfPcF$kv?fL+%>FH#)rxhNxQoI7ELJ$PkiK^ zSBMNY@#{AZDRd9@Rh*{t3T;3pyoYg0I^l~#aeqd|r5pl3NMpm5KU8%MTsZ5X$E87y z4gr0-@Xk&ZJ?em&e>B$7m3}fL+UrExrLi}fFIf4+p1Y>cxkZ04Fjl1rGe@E0CXJS{ za_GHjz!H!5K;&e-3U@o{X3r>CV`av25j#2aVEl4T9*j3SDoH!{m$58hN^4{sk~X~d z@^&I@-7VQIGg;N=a5^jg585F{7ZlTDN=O#56LNy;Hw{2q^E^;3k3PQ77likBw+IP; z2~3om(}NJF=r5bGbL<=b)@Sa@xGo{vdVFugrc?>)m9uR>bjHN(N?0Rb6mMa?ox%5g zBGBOmGbICQAhTbOb*!!`p79N8Kfgnw^{3QN);1n|nd2Nfx&4Y|{l@@`+_Vp@Xf$5r z+(6!AQ^0I^yE<3T@&WbOd-2wpcvArT)tJn`D6mB{)_F}Z*K9)HWj%bm{#x(l(fRgc zcMh=TGx+1IooDcZoH(NjS2~p+A+8nN&{?s?2hn*4m_5AZbOh80@Yyw9^E z!tu2B&K)1BXVZCuyS+Q)z=~XC-Y=k2m5%;hm9{H8ZHRk;Ky;%N5@clSO2oGQG(4&AsE)iU)wrG@EgMo2$ds*>{;kbWw0f48{*5>b>#I2o!Vd-X*v@z}Em394>b4h%PqfzNr^Zp}ted$l}@bi}i zhEk&cH0Hnq9F34;Hu104CAg0gp?3r%9Uf{2{--f5{?NY1+Wlqd{wyX<`et%k&7H#b z|1>5Kj>a$5SN+eOVIF3))AWC0weDNo7KOG575;}Y|8M*LZ~Lx_)BkDT z|7qV}HrfCGjOYIu&#V6Z{~x|Dt%%BWHnAm}VYuqEGCO=LqPn{J(Ttc-ms_P)&S=$3 z3)kw-jsw~xYikbbngZvKF0Q}#MH?!cOl>Q@Ak|?tcvqTgwPZ6d#a$$!?s{By-4vn^ zaqvEFckHp_vEJ?4qt0pERpP!JRDrC!gC6oL3-feM>K>16g<#R=;+@NQ1)24Vn(`8*#8mE3L#~CjSAJKeMU4;Rg>iZG2 zw)e&{Fv3gL9^|3!*HMmZklK5IrGuqbx{u7*{cYU;gikkeiR%3C^f!qri(=3R*OpI3 znVP+qHxJ5#8CMc6?8AnVk-_l*$KNa`_s5d@l?dnPyy)Ci`-KqE)|Vl$XzlU&w?X%9 zXLyfJYxj@OASb%EuuyQ%!vUyGb1*+-<+#Fk{1@c0+kZjg%hcMi`ccBZDdBR^7v2UO1sBJoIL z%&Glq%$oM=PmCB{Y+54hUyQ#Bsp;|Y4&(COX|$71zBp0QSd=u3WOq7j^|JnquN%lFuoIGzcufbo8(^PX45fbxO92F^Lm_&xJk@G&}ms>4X5To~@e-Zh} ztN)whW$`-L=P*QVL6Zma4eBFM?CH(%3WpWq)$9`TeUe`H(e|&#lBE>>hwR22JZy@_ zt0bSOxeF~?tSG_aL>JR#2S^p4mxdK*Ul*F&2Qg0G`xCQoF|>QmboHXe7odWb9R9h- z-a|7}axo@Da`>Iw3+LG$=xw6>vfcaYD)3ZNe#6t0+8s+M1n>FOlw4p3wF*G55p7oz zOfbEdaFS!Q!UnpqpF_5Uz0-6&r^X5qO`FSHo-f38xr1Gt(A*8#khl^!(g2kzsF+-- zMkf9;ZL?|@lkcHstPP%?MXO`J#n02W8_4x0Xri1 z63MwOupWynoIy2>wJ=lke8oy)i7!+0#h9?EV_0x7mpGr83+g(zSf~N>U>&yWd=O|v z0bM+mSl1^O+z3CY9$Q>+HmF}MrZ*%3ThuP(5r#rR2dAGd(Ady>&8M01RRShh(xKqU z%Q1oxccwm>@~eGWt2uAzgp|3a)DF2FF%LIyhqBRE4KC_LP(dMq3%)*KqUS%)&xlhD z$GnG+n*qu2)&Qe>gFA#q~ zx;~n=m+GZ;*>_;sSwx=Xv)ag&Oj-kS_SN?tsdQ<4p1t7at09oRB74nekM$RA$2O}zh!nhhsT|+agZ`BA1~%8>-ga%`B3MHmVOe$=`UPc$ppDMKqvBGH8UcJ zhqxbDIcn+9b4BxTeL68g7n2?nHD8_WXSo*8_OC{&`q~$@LD^wA%-aSy&9cBQ{Lym55mk%b-{-Z2lzr-96x0)xw%cSMzgD zA;g-%@;qe+l_;13h-O(N3i z-?-fKa`ZHsnld*zX`iw)l{rIkHtr>QN*Y@@QTb zhV$$dA;Tn?+*TKJjVM%Lh*T|+jjj()^Ly!=7kfvqo{OD~39oD*d`q?J5Zw50FZ!uh z$K22I8HgHQDhr#LR8}D#BT#FBP?S@B{(52U&LS7WIk8rD2r@Qn>VklcQ+7q#dlZ^f zln<3NC;?V;RwAtUA)ZZ(iC~9DZ+@_sVartyYHku45^uZmOG%%3g@5jad(&SlM`lD&S*AnGE6 zj*q?{j`CG*h?wr!thCV1W~_E1-q9n#g7%FE)wP#}QmZ3L!1t?QgqZLNnid%mTxO}{ zppto%7axmmVUXdB#HRm%b^rl>9K7g1YyKHtZE^PfgRBLbAbLz8wuKTUI$8#~{Opl)gLB-G|Gm zM-9Bi$kk+lvy$u%Ww;@7V`${PSq%!61dnLxV>O znu-zaYHcD#8wR`hE88%%fw#}j@&OWYsnwW0$X`xs-_hOdzDC}>8R@Gc^x|}70KPMZ z`K3)N3%PSk2`KJc*z8%nKX4M*=xwR1}w6-R|27OC_y(lu~Ujw85dcl z(esqMGmmgrV<7OdtM`lkMZa)|7DXq$4jZs}B+Q{}1mVk&t$bmkzKD+q3Q$%A&-$zg zo1(B;Yx^;3HiJtMn<}!G8mK~77>{E+uF6xzNCVAK5r3R5a1kH4x~(-~Hq6^>9xlye z4OBt@UR!%C3NO_`qZ{8F(WNjAofLhis8HXfrXw|Wz9A` zml^i_qv2GYPqNp^fMXA2koA&C;AwUz2l2+HyVBIB*#iwnz%t{uLM+sik{VjfvI6}E z;!QpT8tGw&YA1-GWBCJfh^DiI4+W3S&qm|*GT|Y6)zx9LUMo@2&e-mf@6ps(RzLjz zPNa5aSoTl)fNCh=0b2ZK!NEPWQ}yxpqdxagaqRxlH0XY)zzHKF?^3X8g#~P*oN+o- z1jQY_+JMm&iQ?2@{C#Tbh9%!bI^zd4z#q^C)rW?ePoY-0)wBT*dqw9Y& zE}#&wiQ~cAl|JsKbLU{X&;))%y(7v=lTj*1)I(RyC;b$oYWf-OW{XA^7}u_idr4U0 zv5)qUFwBN&0opYIE`t6m z2)2%xW|aCY4UhS(*Zj@J0trl3e48Y-@d_S&8pEOT6zwmkMu0*q#H z1s6P5xC^#j7^^#TbjU+qZwpf>2~QC9sWEc08X&$#S9`YnqTn=OB`oHMUN{R}j)|m? zpr&V$LqsHu6^D_k_yWCAfn#i^QczSYM3>5UKz;tN?)O2TzwBw0PLsw17M?q9<&=a3 zT5uI!rXIB&EAM{GNtsA`=es}ZY%0%$E$~F}Tj(F8drUl)?FuZ3*L02`)(2>g0$*l} zdn;V-)L$Ab9p_9J*E5Z}k4c9CmGplyP2Y@n+YDTRYtfRF2pwjfAK&L$`9Z8tf|#<= zUWeScS90;$JR)*nH>~()%c6G;3D$IV3cYoC1He}huz(K7d60$i0?#Mt-P1^YU$1ri z8>|f$Dv_beqD;kzDuHKWSaI=iQUKVmn2oM}ee&2JRhUKpLO-fm$@k+T*Pxd0SWN@g zW?(TM%%?1V!g!E?9a?B=k$ztcyoSxY$5}s;Z)_s}Eic`(vg6rU<(tS@K%-N{W55Ub zQZQv@Ok9Y(Lgy=T&LmK) zYJW3C*;->0=^^kYYne~rHT_S2Rd$gwpDpIEiKP1)whk6kB%61 z!sajxK>*DUfb^Wq&m*l*!8TJe^!$mh$EwX5Hv`{wH$V|6fWQSkoc*?r_ zMdIDNJ2UQ)Z!_Ewbq_@|b4=R@k%ulG5YuTUzeO+&9)#uR3o*%5W^tG4v)`k$Z@J!h zyHHxzKlfqOwC|M}f`oFB+`R2BF@#x$PgSOJfG(>qE(iJ^PA|~vq=epKS59CeZs##| z?U#7AyoO!_y!I&Wx%SoS4553I(!gM@DQ{Mr-BcR4ec zO-zjC)vPE{Q#(ypOVCk?op!(ri4Ge!=DFMG_@7X<^^w#fpHB%ZgVA1II4o$$Rr zJ{o^zCw)4Ua4vO-94`)S0br_GlJy3<`vt;oq!4dS5`O{bDD~Zlv5TY;%-@~u%i-JN zC-iHbHl6*_kCuH8)_HJDL0Vpc{ozB|Ej&gUp7@%5HF(2(H&7s!eJ|xzT2fQP-`Log7K4x0EYQ5KQJ^XP>xD-0 zDz2wVW&Jq{WqqOLI#p#8r-|<=sX;X#SUoD?u540p`Nezu)g(Y;GN;EwrmNyKFVUS| z1EM5L2Wd2&j)Imo;aH#Qtdp}ro7szqcUNSRW0O_VNPIk(Q*5Ps`;5{ue!2NeA6aej z4b(M)!B89Hm+~#FtKaZxZ|r@_n5Q&kC{gysA#9FFwgXJ*y%Hg z_O1UiAOD^JHvpbXGyXqkUYGE@4iFR|D7^7czFZet#QVq0{!UL?t~Jpl-_-Q~2UA+E zr6B%C!v32D5`qtge+3BPp5mPT*5DJw+2Th*JoTf=%nhaHVJ)^c1eh{Snbs)MTu(Xi zbG|CykJi2%W^Ws~=thIFNz^UR*f;89(6>q@)~OXnc_M?Op+DTquPv0TG+l@dku8sJ zzDdjHx87<>*8BYZ%8FRAe;AGxtY5bIGy{IfRzv@$0+K4Bx6_spdA52Qu?!y_zmqHy zc4C7GHDYhef=XGIwY;dfjc3fq-j;n@H^o-gkY&vsC`ee636R8O-&y>lo7xGJd`drYpVGT9w6p30FzbH8e7s zSV;*~iLq@(8r2iEsRiJh!qrrk(wVEC3B*l&-(n?F8d}O3kkhW~p-6_t7z3u%RF@EV=8BLwe(*Kp| z3|Q{ophn`L=Vyu?!@9Tk22VKS<_fa|&K-J*DP2X1FP22E$jWCQoGKX6yYHQ3+)@-^ zFY0XBtv$(pL({9SwCGz~R7@ug-h)V1-MxMDroc>3({QaC*Vl8o@@nNGeBVW^k$0+N zTlEu3SAMI_>dqR&+U=;lPu7TKUxrm@Z&YSaY)K)MXL>3%v7m@|oY>D0!~02FK@VdN zr4%S;xMceUsUkv(e?*_IPcNdjzlO zM4AXrv$<3(16-dosk(@e>b}!OXpZ-bhqS+d^YOjq2reEHVEoKObm=l@uE$mRX;xjg zQ)N~+a)<^($?0#G>6MhRHb33`iE6Z^4#!a8{R_{;5heW7R*KX74H0)ta8-}Tw_o!)E4R_(Z1I!Ew`K#C;u^P1N_pMT~oR;QEgfq)Go z5sLR8Od`1=p25b?CO1Cm*PKVU=NUx^;302w1Sre}Am)O2t{4M}I#Odl!#!mJwk!g; zgS0l}!IV(M_Hd=0o`8@&|ECcO;%YGc3EJL~AYbE2GyS#}d)r~8Mw%gMcoN@meHo~` zxA|rM!ZrFh{hWKfM8o%#8;(=lr@OcdVv^c%lBlskHCCg|X0VlXa?WO7Q4=kEs$ru0 zGmySnKhiwuR5->J01Mw5eEqi$sp0GyQiQAUSKLEMrGU>Br^6M1+8lGW@yRc0;&aYG zg}I4BZtrmS<+9Uod7Xsyh2s-83gf=(vrTKA^Fs>eu5zsQ;ab6va_navn2^_9a7vCV z4*x`ty)~`%Y{=2mqJaQZJa@|=^JOJ*?)Queb0Zp7A;)WaXTKU{(ZjJ5Q$BN(1uZA6 z0xDN5;ZO1F9GB056DHyIF4xJ`DQU3;f$Ac<2tE&x$jx2k=kKV`yJ?0w(~OUH+}Xqw zrbCS8?1wQ26(_kyz$cAGadc@Y^)s2MO%~hvlZqq9P0K7!_sS+>p`u0aY0GZahKL=t z$+-DDk)Z?5W=FCD&wTI!qK~q6ONTMDyVk9MY}E$H!idbC#+jk#3!i&57&kUd!**J% zQ4@{OIrYrA-ID$W;T5e`6~pm4IcQvVOtAZfjpiK zpZE9EN!SW_38cqRx9jNVG!3h)Blf3wllUL&M~P6-419Qx;Vv{dF5V`p^z6gE$9Ab^ zk=w&KS3wKNH8*+qknnIqPV0=m%KKK1!YL7}#sktTUd`X>X$$Ax`pWB45~iEhuir)% z6Hg(;p{uEZ^k0h5T7ee3?Q4=&kxLZ86$22SeWk7IuSB-<>+x;=Y}o3b9>Z%%lJ&=lgyKfOjBUS!u7nUlg#X~%rO7S+eS zh4asUQx0!_(-_?pQ%Kjh3k9R&aZAY`rF{Z&kFwfY{QSY3exwd486-1F=U>mYF}MH1!pFS z|5)_kPoK0a+J}M{Y{0kD9^_Qr-5gojyH+Fg&&RTrUI*H;5v+o$F>1p6x)lWaM$1(WNakvoNsc z{^KQ7YDbpx-0%L(MR7!`WHsYSE zLK*EHu#X-?KijuaU3C#O_eyUy(SVYv>#SLPMT=>DjmPvl^5N=e+u>+q_j%jk$<}#-Qy1qPLfe{SrH=KSx{@Fz5{p&J4F=Pnx4WHCcs5 zd3Rck`ipW#C8fT{8i9sY%1ZOF3F%u9AznoHs+jAVmGAyiE9}{#2K``)Fh0cNbVnyS zN;XQbNRy15JYBW^jo#nM(2lTxX-`0BA0_X4Kjk`XS>0ctZ+D&7Cp_14VG$ck&n1kZ z##Anb?iGQld=H=0tr%lg*2jI1%)o|X&HF{mbzj+j!5XVx2z^^TVn0m9YxJ>h^N2y; zK0dPV`1QYB%`XjO+{;<^4-O>5AM>U`x0iQ!?{jf+?M};YNkaZ7PjOOpJvcd;I4#*K zwSjUL5gmTXe^pOxCne>0HBHH-d+|H5czk>;H*tvu$DIgsSzpMhweFqq!XGJu;8dlbyLG{^IRC7jCU-mfL*6%W^Dsi>eHB}q(dVg$$7ta~K>>Y{_+%v}- z5ozz%4Bk5R`hA#OlWNHOJ|{wIwNbDx`s$wK{9gw0-&w)GQ6TppG7Q{uyi z4|%Vd|94)~@^lys1VY$z$Z}ubu$3%%ofD1!zp~i>8jW1Xj{{DQ+|gL6KMMK8BvvQ# z`idgAWM0!77A=2y)dc@ZxIWwAyquAq!(RB6jrv1cdhd6=UY3IS!#|S0;v!s@WwSld z^!^_tk-IM?&br&)UYY*Sw_2*mjs1M>n(FxfLGqKuWpi{o@$aq#41btVTaAkaON#i< zWd1?gXKlGQ&v=jY`9Jsx)x;&URVNz@SKQ*i2B5<|te4qGhAWQ#&j3p^xOIc87v`w{ zLBjD4&n1#wG3(l`7Xf~qfaT1WNs6^Wwv?Pff`4XE|8TwHIX<((W$p~1m@e->aM?Nol^^}A;u!n$ z>;kvg-vx)30w=$x4edYbc-w910DAPyCt(xUKaQ%ejL>W?mPm4_W}r&PvaNjiqsPYL z_DfjLOGV{s`bckIPHIyBU_@b8M%?X;p}uWV--vuL|HG$vWq@`QLc-v-h`AM3ZwK!( zoeN;$+ohdRE7kz!BN;bauwPoMgf^5NR}8oJeKnq?PHV7dmBtIz{%@AH}P-XHQi zE@N=T2tT_A|CNeErEr^aq6!mx{#ohfF`U=#7DJe|C3F8{KCazRFdn3c0seqt@0aPo)v z(S2NnF}xafYn+!=X)o*`>k!xUtBqQ|<0aTBwe3(AzTQnixFhy}+LuF_jmz0<_2Q!@ z=5r&j1*^%T7p<4kU5GAH*hE<`SZ7wxxc8h@RoVBOex;CieVcsTR z_uzA?QxS#7L>+CFx_O6f|p|q-$v?7o&IrW$l{G}~(whLF=lD{La!*IqEE)uoW=KDuo1K*f(`Ks+- zv}_kxr2iWETII;IpMu~xw>o(mrpIP=+m7Em9j8;j{fPJ{DrvPpM%A;4^t@|Ks~!6HD&)*LO604^HB^BoQ^{RWL?kll&**kCBM5~8qJ%&v0bUm=` zf8?*(pea|a{`PF)j_LNhMq%N*zr`4eFpmxl*$Dv}84o%3wy#}k(@q}!6wVF4ejiaw zdOwSkg_gWb<*Z9QAk)W!apUai%pzaSgg(wbu;Nasr@k24E8k*!<6rPp_3PNDuOG)2 z%4)K5Ow^|c&3bDjeOxCRjoShlEkEih3-<$9=tK`cJmg=N(tiB?X*ubS)YOQ%7Q=Xq z%uPid>W3F?&^sV8(x=^Ry*1)7Mb7)D;tXZ4a@a`BFDQ~WJ|3?$X{}0d=JhwR;mW@| zR0(Oh>89vH2Ou;>OryKNV+k76g}NdwmffOp{8l&H&vK4)V|AMH29Z!pBX6o4zNT-4 z25L8{iJ!QV?hl=-lx~E-v(D*4K`FObX zy5*8I_8Iw74}2sDv_OQkfMB>bU&bfgAj|E(Q9pC148Mpa)c5RM$EKL_J2X?OGBfN) z>;^{ZZtsv~KJr2JyMe9AeaF;qW3OOrnZ*m4+Gii|vp9n~Yp0EtziHvi-7#<~)zz@4 zN~Q-&&#d&^U`v$khYapM*QTK)*pZX)(3f}Q0_@<2$L+%@Ir@<7 zEwy>qmq20qm`LIuhNeo6LV87Y3tCYIHW1H0UWe&VMt8hKa_dEc-`zHqObH?42t#r% z|EkBD42@o|UsYj%w?$|u1A+!yP`nE1^X0H@JM5N;4G{nyg+H8?4yrca_k#@Q_u#=F zu!p_?A+=`tbSfU498&AD0VW|vUX7IR*_L!0wRv88w(`-;HB#8{=h%29H=nc3rVd=} zPo_!`5>~kGmXJRikaQ}0Q{{GFX~A#0bIC2 zOO`!iFZCIyB)7Ul^^Lbr&sb;fnWA!Z9eZ)t4EB_yJUlkSC)^r=( zU4u(-C&8WIZowTA+}&M*6M_VHcWB(*-QC@tV7EDvlkkG*&I?p}MX zs+zNA)oO8lWHz4MpnjDw0L z7H_fIx(0&b+=!%1Q!IQ__JjKF>I%?*r@iw@leh9@HJ?Un z{Gp}=zqa-ybiY$jSHcy)5x~DBsqR&|RnS>uUCq zEy~2=!ok@G(f^%r3ybOb5uufUGbAg>vm55LVZm{vRd9x+-fby(CS1bt3)_lK?N$tu z%)N%)M!9*ChtdE$h>7tmq+|~>_qnGXtxH8#O>vQ35o@zteND;sdrq2K8!+qIc7KMP?iH1jb2O0V2i9nSR#zONxDVh(@9I{N zlv^_QjZu_W(wqJzF(Tq;C7)U7mTx-imS+%M;|r~5S^=be2(-Y#BCbW+{!HiHyTRbT zR@MqYVIE;s&Y|C!Aij6}fyoEx(Fnruaa%a~E&~t7j)VXRLL<|l_$o65sU9C_)`aMr zZNy_`8UTqw%u)e;TC<3YwNV*UWl0$DWBx?l35^iPQ1l)C{YaZz(WG-p+_|6vTzzg@+(?I?~ag6lS)tGiwexeF>ENd1ET4`0K5*4L( z3@Oe@5rFSWuZ3vUL9yWeb0@N*#nE9%ag~z^3C_{JCmVtZo7Wt32jP&M%4cHOt>3{+ zU={WD11~cOf}>GZ{}YIMO4aSjbjx$@U}xeEn?_LdrD(e76uUnyi-bmmAuHr&l~u<$ zpbDi-C_qng*V-QDJAKl!(c1L-#IV;SxX^y%+Fe6Lo6DQF;9^=tK-RH6O6 zEiXIu-sVcm6qiKWHsz4I&~4;Ur6)XYIUQ+Fk>&9z|MGlep+!;pl&3k#@k)!X{e)Ah z52mi$u^DQV#iu)`UUpF0KN*bpz(QwM{6!}(_vG>*(|188r^rQ=;I@EN_Rda66@HFX&zPOa)@a4+k39D{^W`qM^V@6Gt*PTJ7bBpd=q;+{(Z*K3Z>7UE z?bSB7h<(5x9(x;Ry*PZ$>w1y~jBM#aKYPun4^8lB8qorqEpa?Z0TQx3ZAa^#NJk6u z4lF7Twoij@@*Rv^``_NXk){QZz3}$!X1NCrKp7S=OIo_MkjZYRGVsmHul1m*0!%U7 z-QDG~L=f&1GlTw2*jAqg0@;y|z?XcE1jep&iNBJT`!)xk>5J*o@9El%40ei&Po4t2 zrfX)<5UAW1FLx+T!ip7sK_ zKo;-pz$PaP1#B@mm}=f$FEM7)ev{^+*0&G~sc27A=k;O_zN>C||8WyLq+}b^8*Qc{ zP7ydcdN3#-s+1Y9E+pW$kKTXM=`s3I=yaZ^^Oe+EEh@AnrzU<>v~&Bh!iGwjH%Bu5 zup#vi%>;F>x9R7dS2XJlY6&`H(0HzOEp|_Lt?AyxGHF4F^*d6w-iF|=Cf2%7DgOhh zV@UI>lp$ntFJq~VBl=(G{Ng6i^BG@B2gbY*jNcCak*W0=pateZnbWig{tGiJVFLj@ z#*R`&=5H+4A8+M;07Ebpps3U!>z{l4`E{_Qz_05_rdECp6aICQx(W!F2izT9%>UyA zrJNox^QMxZ*d6kpBnAIxV4R`-W0XGfzlaU$uvheLSEwrgpG-vl&}-`Ha(=twlPw6% z5T+R!U(X;Hcj&ykCvGSV2e}g$4Smq0Idi+M%XCv$$(DOk*>I|a@(`?IDa?sF*`?;= zx9;zxH#M7#No7g}Sgl?=ub|-1C$bGpvAeqPauwr3jF9R+vx1~FF!ODgFI0+TJ^q|KN;oVd5sZ%RnerPTA zcf&toem#<u!nfLXzl%J0onT zMp3pR*#yncj+5FiZ(cNmrlypN-AO@ljbuC7)X&U2E?{UF^-pZg@7_Tqu~~_h1#<4L z6WrTVlNW=5T3*=Z&VPaRZtNu`BSX1dP4(6lKe`=AN=Qra^&dY*=1gzQ22T%Mv8a;N zTxukOyi^>FxNz`1M&_WElW-_oB|F(usKQOuy{i+OFXRjX&;yV)JO@W{k(0iM-)#vE zxxPo~AMDLKNvpZdQKL_l3R!h?fv}_bH6`S}fbPdLs zTh=k)hotXfOY7dE7A#)o5s}E^^K-eE_{Pei+`8kPJ0joI&;nFi?-v7!piXBIU!AmE zmI^B;i>4MwYW25xpLG^ZHoas7aI-9S1#SU;C>qtUs?4l<+HrVieLe{kdN-GnJDDo) zHIyu#)OM~BaW%zz?-*jdVesPoC zS$lb@%3czGH0ytD)k3>Vgau{$$f&o2?!TP#Zja51HkrvKSf<{+JaDRgi=4}g5suazZ=f8>$7Evs!PEFB zcidow>Nbw>xo&#|L<|Z8!aYk>i{SCfBr>iI7w=a$kTUba$ZfMM8zMbw{TLsAK$S!j z(iOmRS~Ih;SN9?r>)g2N+4;$2pVR9dgdbE7@gR1vZxhJf;jw7N(Gy+%K2r8cL8$-m zrQ~--O{|e$Y^KiuMH}YTfRl`i5Sw>fTz8t@#f7uITyMiZf;sO_UHq&+nXIsB`~B4x zS@40qQH6*lY--dRH;ps6GOdokb(TRaK?cvsM`c$|R{CYi=sI(QpD+|&T9GT?R!{fY z7`;qo-LKT&#d)@1Ue@Fp^!ckL@Lj^7Cx6iE98%5R30UhkxnwFSL%EMAWDjW#RLtUG zS7{u&H${+Y2OAUIaPWwEo+xZV!;Kpi7;Up}V!r!qSm>($9_3++ErHr6KkXj08D_+e zqiT?EwT+^GG8(W?cfw$y*V8VoG3*$>3dNbev?a_sgN z++UR8 zaZPA60({&%F9Qqc?LA;(%kS`EH~88U*+?oZ+S!XA?wv;O$030oijf~YUli%ltJ|wy z4ybxJ=1!sSvs#&fm zQt=dj$)gXP;k2Po9XZ@*v&i7*ooL228s&Rns;Z@h5%KUV zIV3&~?;O^;!pit+3r*FddkJdIWl>{&Y0b;G*>|*aEw-ef4>f=v5=49rP~|q;1dk@I zlvx%Q0Kl~wL|rquKfK_;PHgZ#Uhq}6s(oUewws9eAtq4Jf#q{iHV9^cJi`V5Oh&V= zO!7};!oQ6Q^xE|!6p=fS$bFC2_0_&fQS6smkD9BP-bQ5I2DNz_30mq^C1)+^5m$z@ zkj>gbBqcPpw2-|1vkD3oc1F}a0elf0FA5o{_IxrSAMc8A&)4d@v57&jq5}NmNzP>o z8{n;Zf=CmV7znZyb2Gofo)ptV7pnhkJc|PFXZtkVk+=*$mRPqW&KJ8hmc&*o@i>m)Ag=Jj@BO7E>NqwxY%!n_Xbay1N} ztEWF`$dXKwq%~+1K=(npt1%Bc3an#-caJ^dR8+zP7TDq-eRsta`k~- zxN0%Zr#tDChp~=$-h z;>h>}sY$bU43e(Dz6J4lf)L06zio_E(~T`%jp;C28&wY{I~6&4oX3n- ze|Z#+BkV{~SF9Vt#ORmp+()La>etvB@WG+w6K6-PZdj(I+so56XMsK?>n6Ge5Ubvm zXv#4zDL8u0k6a}+TQ!*=p-PEVH#W-uxSu|ea`XD;6o{cy7O#OHmX~44H!JxDQ86_e zXUsGB3Ptq{B_e-_`RIijzx{RYkN@+s~ta-8(_u8&Jb;HKcnciN3ptS70iC z;eX_9fMccS4cRwKou-EbuZiC66n5y$7>9ADb0JUNBM8r*Bt@>Kc*~Qf5eBtoW2MM9 z@(t=?xZCJBkXTS=rAbe(&x0PsfM@G;kK(r7ZE)LP8Vxz;)OYicmBjXhvziHfEtUBq zDTj*(vn#`+57yqej9xfbq z4edxW2Whie$poYFtE$@szmkZLj}QU^VsEx=^-~#=lQkHe>X0o108u0h5wl1}ojsIp zh}^_w8dqv>dQ0oKk^8H-|P2{U*}+z^pJJjpH@sru|z>}8fw7@h0$k?KMAV5 zxTY@LrR3BNe;;`(V}B=D8PPsclG@y(X=Xl=z) zGpT$XcXqju&4Qp>d%e0#i$jj2KJ2ehDZlO?xrP!6$YI6uU8!p}cM}5^q1AA)kZ8|G zU;^(id>gB=wvjkST?-QVD#PK=K^>bm^UG)IzV%Wm^xJIzYc5mr`$*6NV`~X_TNmU1 zLi0ASXdc-9Kk>iNJlQLn|0+w5^xtSc0*K~A>`GOE%*mhY<*y%vp5OI~=Fj`LGk}4; ziUz;ugT~Vx8`STYX@UTW3Y9m}!oZJi3lKpau7(K!t2qQaGRuz!kBZc(%uW!y{QWak zI$DYv2r*@Ttw~DjO(3t{YBRzT`5iP?vyCt^;)gGa2$6N07<>cYS|LH%NEtp!5X1m) zQ|W=HD*NA~1Y~r>N!wNh{`w?apm!PG7d_yvP%L?RJ$3cv;L|-b(h$xb)3i&T_B=a7 z+CFI4xcaLN;l`Jv#;T4U@Jsk1zS1<&A&3c=X|rP^qak$XXcZgIZ`@m!zb>YE0<-5Z zz7`DB{G;t*yvya2trQ|s*r8Fr2-qAJQ0hxRLOtSI61YmRwi-bGH4w=v!1TO}OC3njIL>XrD+pQI88tPI6K-|wZs^_s{lkL7p#M%(&l>`+b0dn! z5FeEkc8p?+>d31UYvC<*=(|Y2Uo3;%nki76+^Swl8xM+3sR9}pC9hiLP8ioM-Er`) zV1d!(g>~sG}6bQf!lXK(j* zT^*Q4L4{VedtZJVnzP4ajhM*CgHiUHD1umFMjvo)$iEXmDjghY94z^fu;4+|g18sT zwj2JYQi=X8tSm@LqZo8*FwAM*{KS?+?-2-gB|7%Qiaz+p_hyiXB0(S>&^YK4`)Gg|E#3xOzhq|lg02Va{OVvlG)2ryrjyL(oO{j_+ zuxNki=w~enKaVv(_ld`_0u2`*F<;rvwg+sFvfr7u0=$9_u5Z9ae1Sn3$DYz4M!Gy<9x$6(%PE z^!t*FI~&XA70}0%AUt4T(Exk&l|l}14KlBpY7Xe62igMy?I1(I<4@3Pw_GMnT9$V7 zIvgm%V{)gGB;;i(c&(qSP+3$US=vP*Za1IXzq4Sa0dNDMUD6-^@Y|LI4UViEvH6VV z`*;!bKICDCykIm=JtVwLvN}_)*wP(*m!-T2?qMVX?z`lz#)Hm!yWKVH(Nvz$_eLXarhv@+eCU9H zfO1vZzR^)p0RfCdeR)FT9Xln{V2QUsy64v%TkGLq9<^FYJ)8)~IHC zti-9QA2{5w-haDZNBy(oJuluLXgBauv}@`+c)vuD%)C^p)9h(!Dsc?~#N*Dfs# zEh{Ul@%j|>e{nn7%PPOhjkc3g-vjw;58VG%#Yew?Sn$xJnG3Yqa?#-S7f|g?n(Ef2 zHI-1i3r7KboEGi%7L9Pb%J=NQ+0v!Gf4snNhkRRYDlK_1N#l*R*7m6B203) zRSD34jXls8A+L>IDu+z=`2m+$GUyKc5Fr~VjGOy-jwsEejV=RXwoyiRYn;K%Y7%>u z&A)1BwxwTLTQ1UKB%oC89u!EOjys^s@bIE7uI@uOH)l70l@ER-R*3^@=y>UC|6zO4 z>lT2{r>ur?5Gd#={m~WyQ6VNOTl`iLFS#hCHY6dUn=dIE70YM8(VHFx?achNL6!9j zJ1-1&AhD;3YLh;h3t=wMyvxHdAN?BhA&(>84tMR%3JVG7B171;C~ESdL{H3!PS5*xeFe8-8zKS zonAZ)w7Iu>4sDpRrzn^|4-YJ=PiNV_#kWqv+PkUlD80xG7fGG$!Ca3t7qCUXucBhE zB;@a&L()aqh1lG}*p6UglPn`&L7ee_yFaE{UP=5QyJTu(h^LT^J^956+78F+E zI1T1^XJ1zi^x1H@Oi;jSNDQ9+QEnJcX=)5I<^k+7H>b^1y0h2Ip5Kj+XzfG}e4uql z`YGeq%rAP}r*ENx@Y!j#D96bq9ihWDXg$}i=AwD3t5DExQLfcNg@^}J&tPYBXT>Y# zEahKyZuD9}|CWbyj_bFu6MIQ!2X~Hv(G8w*^Iy z!ljX}t(VHX4XFDOz7FaMrF3r-ueuW;0!Av98~;1 zw)A6cK9Msig0+s87Rw8Y97A*0Hkh2% zme6x)6Y`zU)f_3M4d0`K+^H77*GS%c&x5#bTulrnv^osvnnJLJa;L7rGxXOUS*0!} z_%+8V<3ZiUH0!zPih=XqS`9Q%S@qp*l&l9e4v*k2akd@ST5p+k;lA}y0ts7?nID0k ztk4==nzHf61&pd<$MK#I)1HDIuBS1_TgisQ=&K7U@nOLZH!5c)PWYbmhw)d4(;X<#h7ucB?O%VUR$F|pb;q(+R(3l`e=i*USo z15sD*QI{&5E~Wez+-$`1Hp3cyIdDHaET)m)yp*Odf4@~pd^vH|>p{v?XVjm_?uj#G166qrtg^a*;UN<>p1}3Bx;?U zqI@?e&P*I|Jlb5qK?MoA_mFGh8{S;uU~b3t3I8kpbOWhL*KxBu`EOyaoCMJFP@(=y z3;bt%TiOg>Uv^pIwWLe91N`@U{Bf!cfCZG@91quv0`rRv(D|!W@q~=6 z1b)-B53KPERnz){GNlDdt~S0H0VP#HBNxL+?MWKu({8+uWWHzzyPhSwSzyU>W;<(qgCRXgi0E@8gG$ol`OW=cW3Ll`P7D$?aGJ{KVY~yyew=EWC zoLN$Vw;2AGG_!(@5tKA_&yR=>gEH1~m|tp--@K}9mb{&5t|=cu`|NuvyO^irNT(-M zn;l1d>?(jc8EAX|4zAAL0p-Fmu#-Rl0!uY!sY)y2-h|R0Dcx4AH>?L#+^z$YF_Ug& zB8YHZ##42m1U2w0yhm>A|lo-jf8WR~={ ze8arz1;)iC+mlFaAvd@N)+~Vfxlddx*i!zI(X;+jwk_KNNIk^6-oWFP2?k_Y?C4hH_=f#lX6L60^(Jqb&JydL#;@8(ITjFJh? z);CM!RZx{)P)%kX)dp|L5nfB`p})8%nhY9_v>mOZ1pmPHaT z2)eDF@T!Z44og*As@6^;}$W5Taf1|bM) z(Kt1INV!<++aTz=b$HBwxNDO(U^v`0FOgK#%<4`I9ddaU84~8?x*=w+#hve;;q+dgt8==M~$gGE1|4JNQy zV6LR95|+Uec;m+reV8DOe`qz!d?^~N*dgnG`8JT|$Ch2xbQ-+xY&K(XWVwn+RWabXm;maxZjsk za)#~E^BC_+_w({L9Vn$=9-JgIhIDnTX0d>>KMK`_>2xAw?i`h^1YZeqd%8M9vfbkI z_h8((+YLRxnK&K$tO`z0uuJCS@CGuRkWQd{N%O=e3Jye8MyM6SZd?NJ9q0q_dx5gb z@7;SQQoY|_j_iuPSLTqLdl)*dM%oHTy?jS*$t|>AT5CVb#V`(~;DVUz_=Fv#PPRI^ zV6NQu(;#zi=@mHiw<|V0cxu(wq26l^ZvIvk%WvZIZI~x#_WdiM;txP&_mJ2)=q2dP ziu}N!bvh}mu5JTYh2>7RJx7Q-J`>ZY=2TQ&+X(M6aj1#uPy>`Z&R2H(Gi9w)E`Lh` z!$6A1NGu9>Y<(ME-OA806<5QkMx#h0J;2J^%LfU*k)`S8YnS=>d9+V%t{NS*?$rnv_+@!a@uu-S39m$R zFs)V0qhWIed33-1M%i_6G;N)9e}$mX6|4^a1h}|nEBap&SLG4&e)DI$SdEi(ID1L&V)trS;jZYfBx{5RI+1#9%6w2=;T?yj6<`E2H zj5c6g8eFILQJ@!M!cs@+L`1c4zT`ty_sgbuTpTx3s?*#{yQntT;-`;R~x<8YZG&pw}+ke_%HS#L2|4U8solY+2=+g+x z|5y=LG(THV@X*wdpZ>pf$AB-M7*PJ7+heRF0^Lg}Mmf>$98B991D}H<$}An-7nhI* zM3K&W&Ke!vRpn53^1h&i@9PbsMeo#UmG^(r%jzm8A zpX$l|p&9jF5+C+gfoEOe?>JHi>gw#~CNMr8%bpXz@KaGB@RcbMOFeR4sN9od4P-BM zbSz#95gQ$kCk}`h@~N{P##ld~@4l-UVCWO2GbmB{?2#B|WXNrnR_JmozkIga2kaX{ z%XPt7qcU09V@H2D)nuS_kAE3a@XlmAzd%CK?_r8XdT{M|<7e}j2Y&pkQClJcy&$S{ zt(mzA<%!AtfwSuciC^ZYmj+<8%!a{EtQ@VC+^9q&~q3q|4c>F*=|OdMbX47XQ<&;|=E^x+_{dZH|jN*mje z69=cQ=U_}bbypwmi(VCOeQXDfS@3x*ZB+AQ{vCd_UB8w-c|6rS!D`m<@5}`30rfZS z40IhAPTKrwyePp3lNc{6W(`NobiK?`(QZGY6Iq~4_;xjKTFUwYXUyL<;{^cqLH_>2 z#8fN~A?R*7u=6NZMH;GI?}}HgYQJ$ZB+ZE)EJ2B{Vm{!{bK)L1I(7#ANCi6s7K>!a z8dJclCVIL|zt7WC5Oce7J#q1`KJ|xK)p>r{xu9+<`FmV||9c52&T0&`{sD@?uj}Lv z-qAbqad|xSygg}D|LRWdPPZTY&hUrzPv%1_j!FoyMx?Gl9fmw_JX*H~+(36{HC&w$ zL>{XwIR2Mi5faszK-nJ=&%_|9ms)vKj^kE(u10O&J>V8TT-Htt9xUyQH0<5?aP6&5 z#C?&_3Kl0^@{dhYWXk{AZeRg+Lg-J`3HmuDzV~`py84U$^W~uR>LZKTk0(?SY=*Lg z%M(AE-#jY)Yx@$i7#lF)T=7|=OQ_=qBtjafp6+zVGJWW+&gL*y6s0i1JQ7QOS8^af zQMdvXv$~3~{}}PTj#5ZSO!A?tKX@OFyrf1ip@JM?#(-*X#SO30aIi}cEc&}%plL?< zTLMx8@Q!ReBeQBUG`L=&^vLg%6tsfs9$o?pLTp0|q__+okn&c!eW|6Jx^x{>^UYQW z+eGbW&+j;Pu!o#EKvB?UCE`!uX}>GqFc!ECSx?*I`@Ze}QNDB1v&BmsEIt$!?r89B z=r;QCKvg0Z1g)lt4;MurHz)QSeI+F`ndE~kKfP+$zF%-1ojQJqj{V+;4Hqg#lqy~x zec2HqcOV=4oX*1@|1%=l;|Hv0y7;SxarZCIft#Xpr%BWC9Icy>#)N^%kx9ph=M0 zX;+r0HuVO%J7vAJ>Wv}$js;SChcH7{DHXm=nb%jSw69Y&2n4JMm%aW1UefRAtI+}s z+M%*GqL#PSxF0qK*3AKO3{`s-Vy9S+ zLe5Kk!Z%gKu#pm4H3OFP))i=2*l$=Ae9xoV0Wt?PJWG+q_gEtM&e)f$$sunI)Wtm5 z=2pQLMCcdm@$$@PZHB7$(rsttjJgP~O>5iU%Q^|I=_G=K= zMzs9ZHXlga96dTKz<%c&N?#k@n0XIr$*mngPx;695)Fh6e_b?;CpKP^BL~u-%U)Nf zqt$Vr0myhyNY$p8!HK5CZ5rzGpeglj^& z{kgwRnBDMIWfuOzLb_jF%bsl(AiZt-ai7^c&}lW4|9*DchZXt|AJPlvfZ`TvM_STn zIx#TvWzYM@lrIO8ry(4hxPYd@@&(1*!;28|%Ifpzwu#Oc_s0WPArXI57tn^$j+>M$ zJzDwFykn(6A|Vkd{cW=(J=W76QHT_qw5xE)dkrz_nP7?B4FD1L@A+ z0hQ}(&W7M%i2~vo3gn|M+A)EdSY66#219-Kfw6LY#0qoKAf<-^CLgfgGDdBVr*^Jv<2AVunle#ke-Gq0Rm*eif8%pMvg@ zTPRs_oHkSht;@}(Bl(<)QmwQDJr>^p?Z?W78~s1;~jt7}) z7GekCBwY2XyOZ9hsnv>AG>xWj#!2dsq})K6_5eD)7`!gd$I2urvEERP>Y?dqQQxX3j% z2+y7KHuzUITkN4vbcGIz7)Z!IdO9A@A$AG1hN9Vm2$a@~ZMG&$MlR0F`s!#rrcIeF zref}mSKj&91&{*@I4_w8xqJ z)fl508s_EO8!K5n&g-x3TVD_KbeGTU=4^)!mXR%O+3h~>wbH6y&E>?@Cy&SAi_xyG|65+uzc|*J| z#+mJVXYU@pewYy!e}41*2kMy-@p?0Q{6qUSv>OR%&q%cK#snw>L@drD;St4ckdr_p za)22j_ly%HPJ|m4O8b5SV>vFTL!@ZgY|lpS(;-SL30&?pjB1i{=SO@aB1JR!*{sP` zQe@PpgrxvW+?$pcNK`{dgAG&n3LBVj(rp*>H1bo*5rnwgCeKWB(M1;KtG;OeM0Gx9 z#K6psQvi36H zh+CIm_~^Ap=rAVGj}NC&kRCT7y*x17KQwI^`e;&6lwG4>Nc9B%fSb7uMwWC}e9s@Z zMsFI3glnwu?f(4&hQlgEc3131 zd3HSl>u=pgo1N6Xy+5VaXg`)*-5!il5xw!G7s#lu#$MgT0l!nZNuJ5pXG+qYe!G?j zF^B0Am52f9S4^|Wq-M`JhiQB&X*kdF44Jp0BhJ86hGAm55H%ae5j$^!iy4aH+T2lL zs!T5>L-h&uT9uurdP_#qG)dG_ftL3zJJzO~=E4&7_C&pY;PCh=--Dbso z^O)3`qPVE{f5(KzH=r7|mH}mD3=6eZ47~!b)fS7Gyu7@&4_Sy73)KRLGbQ6d&=I2Y z(I>ys{%B5qv4y%*l+YtPR^T}u(4-mU_HffJ{Iz6YU~sH|%%p{ukG?Ez%b}fDd9svY zdU8!{Gr_URDchmAtS40)DNs6n7S#nS`e43l3E+S>u1V-~^$~pNUG2s-* zt&5PSUw-6I-{l`0)$zUIL{|g$S5Bf4C~QAhuF2ZC3K3Xwx+!xuN*WV)-Tgoxin~*y z_#=hY7;a=5KBaf9hNOO~a~a|d%dVdAOfpkKeVLr)P?|r_IKa;uE4j$lc_B{Q_c9Su z_HeoyG5(hz7zFkM0T6_Q=h3O60y7BxuzO>d(PBc6QYSo-cv^WeB+@klw7(_!`J3Sw z`OBOXz5rVqR15jM+3XBvs_9Cs8u?{Tyg{$Y(oZJ;D%be!FpxC<^jabri|d{A8-Mv1 zDxn1aibuTPz|miCabCj#kuhl6|FMf5-~Z6XPUN+V9m)sL(EmnAYoo>I@5d-1fXf5t zbG<6%Zws-97?=fEVu$TekV}7UrZ7uNUR9_AV&%1881rUjMdT@j@z4GaYffzZenl@{ zZLG$47S`92?Wl6;&k)XE-JX(wo3;PvAK*`U{jMKjH2-RSn%RW8Tg{F3_|6r8vqD_nues-FV;4s8$zL7>+fmDGX91%$3>@LbjF*gfWf4 z1{0p>iK*TI=tJ^A{i#e~owgflN4UrpApm8^wdgCvJdJ_gcXxn_G58EV?SyS@a=)l& z0Vi48U%}0qcAGmjTt$Y6zm!bJ!{Uqo*s}*p1v`HCh)n|kyjM21Zq(b(j7nEvW*|T0 z$-!8NR-aT*@B7ouw<$0HC;gh|vPcR~BVVdZQF`17LR8obA%gdi^athFDG&IpN*@Ud zvAAP!jiS5Ww2w`QA6Z)!M-0v9Dy4G^;R>HziCnR4VJ1Q^*>U#(sz)^-ayMq(^zv!h zZVTs+c|FDqE+slkYPdI^Ppt=gaWwz@qS-w9{jD3sfM5%I9 zFh*)WFaZ3Ocf~oX(G3_V_?J;}cX+8@G_5^H^*Xhe2}q>dU_@5?%ILF{GjeeEFXM6( zOER12V=N>36-wx5v~rfu0+@-&_^5%wSgE~ry^#8sGmW}*-v~nM5GaMA?4PC)?jHS| ze1b67W#0>C+$N_4k$r05(B+8<$RITuQ1+RES+CQ(Ou}|K#!`I-^5lvfnhzR}diu!P zX(csCcdJ{GlY~c|^XUQ!KRdwYTJ6alu^{z^LPHrn4A}sXf9h(C9YM(-T}Dhxl%q#q z5FtgM8m_vtY$u$AR~i!@O zS~Ms)LfcVS05*2MX#Y6;P%3cChI%m1vnRS8S{$UbRx*;4YxA{IFG46sur4_t+ z_M|K4)g3e3p6PK!!qbrpB-1|TGodG4wgly^QZg#172R>nO&lS?s@#5%56QIdcCje1gx9l2y1~(Y^Z1#8LXjL0yCmIm3~a?LJE;(b%wFlT+dSJO<5cVS$F|6f1DMx0*$Clcvnd zXcpXZs~2W=ve7942LR6EE?3hc&9UB@jAd3CqE+|Nat@z)8UGgYp+COW!cilwVbwu< zx{IT-R$AqVSK`IzsB=}|kdQ5B<7snM(C8Uma>sD3DN9i(FP`9=V?>AXCgG=An7^!K z52!1A66?nzorR-e%8J=3YFP)J63Yvgu=cGC+xhEzyBo3`_T+jr2wRzX>seCl(qVjW z6c6*;H;ZUCN?H8d&p$=MiR@2kY=wyYG{eu12Ec5)7xSE1Y-+DSMK_#oe{SL)e>pro zZx-?e%$KR8{w%9NmbT4cd9g5D`R*UhST%mZ(diFFapz=Pl}mm;6~k6O~B8JZ#FyJ$Avx z0NU~1@bF8}Kp+@|dI&Hii<_7jE=--xvbK09NI_2I@)<9%*&RMcnXZjt7?_?F0Pc}6 z*JOVG50U^TolFh}r5`s!4rn-jm9a$dk|jJTRQU zmWHQysaVLYFzlz=6jcpWh0HGM zLyc#$+rw5@=IVLvexOqgf5B?O2?&~u*2*sBtlr=^ZQ!QsWgT*J1$nM<102tFfA^hU zbRscu+GdjJ;W|(Tt@pGUbfbjF^Fd&Mk2iQ5uEjCqrM)Hg6xdJK}6b)ug914|6@^*?$L~&*-NTo+(C}dkG_nUY% zo19T1W&_}`&L(B~+Nt9~QsT|LUfTqQcKcRVGJlVcHvl#f*JhAaT;T`G1%3VemX+I0 zqu-a=9yfHGchv4&?oH)fe&!L&%eAOmE+0}_tUiE0v%}+7qhtI*clI9J*{Yu6!^msA zFBb_Kf4Qs`nN;>sxm#5qk9@RS`J)r*R-hYwkW7R5;o{{8mJ>^$VO#yEAa7@1|n;1D^? z(9_nOc%G{ab(wR1s^4UC$O}?0Nc<%8!@I@whOdXoB^tFIv$I&oi?u=8Wul8U7VDdv zq$-SiW4Y3})t1XRCF+1e=lasqlG{RBd@jeSnzDhjaZy5(&ry8FbPTiWD=i*2^7=)U z7tHp|_K6IGk%Na6E?VW0)76KP9&30ec#{#g5!Dgb({q!DnUl;c$Ib;;5hlH=Urg@v zD-!b{XDcqb>3j|eF}iHi`D0M!+Us$yEbmx zX>Q>2Mru?-K*-CX-GLw%ft7@oL@?D=OSh zWO4r#Lw^7T`N+^!EY)3Q3y~sRnm@V!w8Y&sZ4eo;G#D{!v%ry|GL z85mI`SdGyAt=t*&1-G~5Fp|b9!Sx)6Lwd=C@F_3+BWMOT-;1wv%U%clZ>-b_6!}=O zn^}JSmD&6oHT~JK4~PE0Q&=C8fyOAXYA5b*WdF}4fAqxre|aM>V3|U3eGVrqIy(B4 zO?U%^Rj|cWvy$eY@@`YqKcHbEFEK(@n8g_5$Pz4sdg808oY|1hZ)tCD*QP63sBm?| z)h>DS{&hu+fkNCZTfhN#Dr;yU^z=k5D{Woil#SASV%3FdB5jjgYTg{s{4E>w*I#sa zEhVI&Cw~>Y{u|>uivk-PI%)m@wvPGt2~ELtpyZZ7ajJS~Q$pGu1ni1@#haG*-GE59(Dj;xwBANjP)N^iqMbM+Zo;6-+!XXNz97_RQt%D|3|pYPoJ@80{b>UmbJRquLrj5+3*O!0eat}l-8 z@)BEU&``&8R4l72{*5Dmf+I|B=bqK|vlMW7yRTke69f7lIZboIV!cXp#8Td^W$>Tl z#w-q^Pb4y_u2g0bZ@1v+_*8tHqHV+zpVjrL*3`SDOdr$FYgN}1P8r)N?b z3UV3w`CmLCL;XmPuPz!TDb%6LMjfA0o%HI>JC;6u0=V4qqLv@DJ(P^hIN6w0eSHiS znTJ{^FINoC4#)z(dLU<^TvbRURk3AwxeN0Elfx@35gD!xo1-DdvxOcVEOQJ2B^j8O zj_)siVdxDQMk;|?2&v&^@zZ!fhZGSD@w-SpcNzp7h0y{(I#<{149 zHO#1-r3llE(4wd-zo_}5OoU`y(g(NuvJL!-5 zQ|62H+t!O89>imvnF5bIgxHPvF08@7<~?rMgwk+)cR=p!Jyv24Z)e}p(FNH4TrV3EVSF|68ru^~wzwdcd z@S5YO$0>AWGvsDhFNRk>8b{gBdN0Ok)!kCTTn^(ln}Mx{z)pPMd9FJbHYL~(nyydt z)0EIJfZ8UmU{9r1S5g3(UV#aDVJev{I%`K%|FO}XvGDs#>6EfQeIfWsk)6=P!0#{3 zRuHyn{Tr>T6PDpKM2LK3#*D3AlXIvIm> zAek43%ebzP*Mm8AYcsOJ54=KA;qZmt9&+y=Epu&d$m%|Kur|jJMEGa5l!qG+nmmUd zR>Kd3tsUDZAKv4BT;~xJ^!3q0@TwL?Fh!FZ(rv@a_EjgA(TmWi^@-RQ?ArpO#4Vx) z>AxmfBJQ=$|CvP7!9PZVEj9nuQBCFMvg^l%#%b^YC3NUUEcxv{-s~Xyc|h-HilZt| zLy&;~@yz+WMTBTK0~h2pl|&Z#{#SeD2E~rolUY5OBY*F?=Q1gnJcPb-Q*xoVc_Mb= zwH;xlNx)psaR%|9C98%=qdan<^QN1FC9@`S~<9z0a+FK1nXKBMUk7sx8zseHFBH#(Ih*1cw*?vm%a_n7lYngH}m~T z4X5~PA7VMSqbGwf3?j-{s-qb@oNt(zqWF$H4ir-YtZ&6ui~!0>iGfQ*uCTm|v>M}p zh|J5~P_l>(!xd@MFxV1Fdb|P3onZQwaNBT zu_BBpnC8i~3$UPCF~eMFF$r%YdOYzeTTxC89M(invhqmV80ncc|1O59f6@foOV0N* z1BBMq;$dP^%Y^w=9TQb;nXLQBl4vc7CVp4Ub+aQpZEXGHyB3n>_k867$MLJjZOq%Z z)Uw~XqH6R=prBR~HAM1g&E5zVJW&vUuYuBRyJ#9DYkQH0&EK!kCCDDpo;U ztIoU4S%N*vs;n#`p$m1_FOe)<;Q5GX0#B9eej=(y_<_D_NJ2@I?Y-n14<$b`(kM?@ zDpD91o0TA9XO|q~1x6FJ$m9q(Te2UIHK%qWJeP4)+s|?>Gj*5(*d)GJbIsmP)5o74 zoa?pJrZ&;bsv9#KGLP4dysLjW7$moYd#R}S-{7WHdhE$iTL==6x6lDd!ETHAVfs|-l`>b3<_Pjf3k4r@hh1}KtXJ>d+ujn* z)r3@8A8LgWO$>ACr=MS>y-HNh;(tDCDVFy)x=F642{<47u)#aknY3+^?BdR@nZx!d zri5(h1xCf5^Bh@SqCM7f!0ALl1CBs+wShGj3Ob1BJC`=OW;$CCBg1VbHo@zC%z(M& z3Aq-dR|!K}L2r=8d#{?h6J{P~*@{wi7b^nDl(#&HDIHm23az8;bZMO%(QB)J9+E)U z0jdUfnt+j1&W9gyT$-`l>{WXih8xZ;j0L7tNeVa8o>Nc8!d`?#u`?oZXrYWL0P;1w zMh9wyj5pBvQ&YgBwv2lViUlzDwq*IxhmC1+RR( zcS}6ZoKKPK*@$CN+hv;v{1`DhQ8|1f?PD^yLBC}-f zkMw=Kav5piSLD7N)0YlQ4sSo7)Us%hlB!s(s9OOpv>uuv@8dH4zuhKlw`w%N`M7 zp)K>lGWW-FJ)(MW^Kl2jMsz(s^QY3v&w!&*)Dj2(^jx%!)t=#}vHWwZjPiEv_gRf% zXnO9`*pBFMgrQ==_cj(2o(|^jzc{IhOeyy|GA=b=UvgQV8S=8w!*OC8LQth@WS}%&wYu8ee^BAb=M>oL0xKEKu1(bYz_Y6332!@ zw>vzSIcgw%e~pks4;EgSoIuf72ZbM=n&ceD+_c#!^@0wkHg_EED@}+TJXc;I#rozY zBPyw2b2>$KMNHAZm|HgY@stEg4}bLg2?gKl@yU&o~RxG3d=} zHcH~*NB2{WcrabXpt5Z@_-s8nqp>jWS-yw>Tz;3F@I-^Dmyhi3&w$UfW{apY%^=^s zh6Zg0>Q>;E_}(sF8hiqe zw5LROoP80%u>Mf~cGy(PdcJ*fy9KOAaU{A=^=_w>#6T20*44sH@0il%GCrS2q5qN7Xwmk+n+y2)DaLXJ?W9_? zMxkZ)mY`;^b-HHRa7-jGQ3rFd*y)0-1>ZP_PF#=1rQ!l#WkP9P?Y6ki`^e_LHs!tU zeYq}YkwoDasGEkguUvyBP%98G2I$8lbr+U=Nm6xF|0{O9{@G9C9jiq}mMX2k;hz}H z<>}HPC`X%RBDMlpf7we3hAy2H`BAmhIO1Bijn8WVD@|d^&&r<^W-Zbt9rr%HqiA7u z1VVOb<2K9>jyn5nPViI~J){V+j_hXpSnMC2IcYG~`YLqCCJJm#@26*&mBuvH`U+|$ zZ!;*(ZVVTr>F%o=9M{<_Awv|jHMT4ImXWpA3qN{$r7Mo6vK3N!%$6FtwIN0UrzOBc zeMYj&pF49^#@$zHTB`gT7}}*(sPmo#rE_~Ul z)+ynPh}RIl!&6$j_s-&A*LmDLImM+})uXq@n7O*fYf5wYgWvv~dqzV0W#r(mru~o4 z@4^D>Bo=DX-Ol@1=rFB-b&>|oCvijkW*R_oa|vp_aaAmSv6H@fT4?X=jfi+b>;=A+ z8`Iy1+1YIG&d@w_<_{=#W|e<-+0W`m?6Z~~p$Hauw{KIl@8#C$9pA&uZeNb6c)FbW4NXt0F5Fzw&)_n7Cs5A>6f)Tk_?J(gg zTEC$C3LlN@YqT*lL;o9!UR8j0&gA)nF8+(Ocs?9ge21_GrHfFDf6F`gFRcH&Xx;W- z3f?~nk}*U`RD}1dlp*GSAb-dOkP>tR|NemIYakR4BnX;?srk>Gs^_RJ{J-ua20%$V zj~1dql`M-2CocWj(X~MoRwGB0!XRYE#FXQ5H6JVtNOwH_670{fQXQn zE0-6wv9Z|kHa{+vvA-=7I%u6r(EWyrE;)d73@6aL5+bqkn2k@lz@exc7-Ktj&A;toc4F)^|>s3d>O_oxXCdeE_eL`fwDVpYKJ#v?Whf0;yEaO!cR?R(q!w zlEeCC9_e90NgyQ2jJ5Z>>9T2ac-eluS$8vpKk~J!_RXb`67V~t^t8E~U@eWuMImNz zKI$#C@eFJ&Hm4m5i2FoetBB4SHJZX0%F#?E%@1*KG5zO)F%i+wI%B4+cn|Ffh*lYK z%-~_Rs>L=2L)nFFxuwd5vZnhA2d#JVV&7eo@X(+KGp)bm;lRA5Wg4QJz-)3$=Xv@v z=qkpw`d=jovv483Zt2@a&GMmrZxU1Q!t^~V*)`OiK3|O~2BqgsgMqidW4Y<~s^dj` z;(>ngkqixAs?t+;(42fA=Ki&@(hv}6XUSb=18U!H)ERr%y+ulIc#CsNq(tQ))f~r- z;tCKV>gS(E)~Uiwe-aLIr_uLpW8;A9WqB0}^?|1#A8!Da>hlPI*mqBCW{JGz19qY0 zK#a>+jAm#Qz3-Zlbv4@RT7~>2`>2-_*MxFV+{Gee!(}QvUP&$Kf*9`f=cO-T(%wZR zacnRUc)P=-ACMfGpG7Og2xOsY}fZB^C}mbO3zc5 z1yT~kL(a;1ra>JhfVHFk)&m?Jm&$#1T|gyr_paGpl~r7nQFsW`XyUx;#rug#E7b6A zUlk?2HpnhWm-8b}Lx>p~ADSQe{qHND?()M>?x6id3_EeIN9 zFU_00r8vQ>`$a1e-wMhpYe0Kz2? zB_gJd2-Gj8XocQ-HNfDB}*TV0ecir;JX)^GCNribt5 z`Qgg(44O^5DVbj*6VO@B^5FoIm=|=u(yZ;*zsWK?7I>==7UMKA*xYib23wnwbUJe9 zLv`iF zq;RGM+(?n{^Jc=tjB$;-A_#xOY8)ji zrpT@2dwwSQ>W00RjWnXBpNfNbUx$D6bI-d5zWr^{>IdMWu)JbhK3NBg5=$3}WpB1^ zAJNI~#+m<$khA6=ewlWVb?pe|M;(qGZV^@=A6wLJmY}Ctrbf`QN#i-2CskJcOR)UX z$v_23P*m1=yc1H2Fsk6yA(r&Q`eq@y z1T)n@OasZz(N6x=4>tU|y*A(eDd=2C+4QSK1d78=l4)3*UUesE{5E=0Xh_LL!7EJU zuOF){dOA;VU}Z(4z~U=t9Kok55#-GGA2{`s1c+j(V}nfI1v%)@dGgF~;@=4cC(-4> z%f&_ka|F+!sR2_6(HJX?w%%g`p=wHp$7D!9s-C>)c#+W=~FA>9& zsrOOaw=O5g_Y-9C(tjoH*xbPIlR+ub9R9f4jGn7=AzXfXM8Cczv}qr;M+LjwW5Yh3 zFk)qRyl`#M^64#pGMtS4)#qu5uFkCMJ8Qc1#a$^P9-E7{S(pm01)`GQW!k1}*M+xy z-&pgDLteoP4JhQvHL(?0>VnjyQkCIB2DTya>m>^f__ibCB4D4L;A!PIs6Ts9i%zzG zXLMIxH)z^R*6|qA@XJ)JUHSf~VlOiS{X5@ggDLe6y5v*zUP%z*x0P9~o{6-~mcSY+ zz)MkMXnl7xGOSvF|Edm9T;GV|YwysH96ae?QY{H_KJ;VEa2F!hnOav^Y^O;2urL$7-bUK=IXqcaRS42Wy)~5QkQU33JGVAm6h7Y!5 zfD=OALF3i9V%!&eM^AN4D1{o3`Jxq;4DH8HGU196mL0DZRc1ovlT{(yhl^&3GpPfa&h0Mj}(GID(H_P?kvjfg)qEGKFmJAjj9Rpc46hM#;!B^8?|Om z7Kw$+%f?JSV7cXSZu!uCGj)vNJ-n9kaK5oLJ}s&=uP6!3u@8>%r*hz^_Ns`1=!+sI zI;lY8RlSFklLz6|W19z`C)j-y-(RrSkI=AW@q;Z2eD^eVk;LB$zY#@O*5W*YhlJ_- zHAWHXdktm^4SRQWUY@>XzIUDmxUgx5rTO@LO;2t*1(iitU!dWrD$&Z7U?hSn_(vs3 zS|UA!jT(Ws1S{Wp zjA3s$MmTO`-jp_hV)^~N9WmZW2jn<{0-V~=M)JjCvDxtq&ojBAW9o5xHy<6iFH)mJ z`U2+%>Y(jxhd2#)&t*o2+Rxr%W6@r9+}`5|WyS^D&M!rrtSnO&|KSoMCWpP6{a((} zerDDZhB4s&w0ZB>jE2j>Og%SKtJ?dd7vja9p&Sp7P?5#X0U0+BbL7oB+QRQZ`rhvJ z(E>PAYC6gQJ?`GS^l}8#l@nP=FD_dNa}+e6N< zrGL{0pR3-zb_)^vaCmaAk<|cr^4KcAFTUdM?=k`IH=vKfQ3><}oqU69FK@-K9$I=u zx+j3ZkG7Fpc7gaB9n-H{=7C;GQ4RImQP<}%uQQ;`&)5*~edtNso-78~t~G&-%riJh{#CT@WljcH@#B;xf_r7Y394x)G{ zOd>xUboMTDXJGR+oL{Gd9;VU<2I4~5FLcdU`!+%9(z z=2n|^X0p2XKFUVtNwuNM9b2lwXaWMDTGB>nRIT*3VGpsl&FIg~?{!!X?804gwnLXv zeIJcanMASCkNRV=2^|{R9$v`BkIG60q-8WDjiK%X3hV-7to%Slf*ZAFMk&C4OLzQE3 zH?PWzi3C!ydwHY->Nd%9-fT?|ZQIzvUi(iRcSOD94|1=twtm^tiFE4h=SQ3kozoOI zoHT)e7t34ZoUaLgI=pl7AAFBwI~^Q~@tM*h;yx{Uir+zv@hJMsq+Y8h#=V*jo*)+J zi#E@y(F2tHuxGd#>6l|pXIn< z%m-t-nA)OId1TIrJ)xyZ}lw@E+}9wpd99G zp%x1wM>*6BbS{p+?@IQv%8IolVKY8t)cGz$u}783nC#(YJUvieH`}0JTh2^;aSA&^S{Rn+#SzKvKxtYI}NB%auEH&E!JD)19i6+Zj&yS2&t#7%Rngs`$-kOJG$WC$Q<7Q977Bb2@6M^-_nDBlVe~& zz`1a{9nZg>@FzQyMFGhc;LYQyll&_q^}l|30?BGvf<-m`TiQS5-#p)&PzBP)|BD~w zgx0`e6AI&u38~>yBzHC_yYOG+n)=Ucrk>Tc>GerT|5P!N zKv2T6y~%$nULf8I0+4VV5E~EspWO=)0zoYaRt$llUod~>f|TxnwJJWf`2nfbG2s^4 z8}rP+nbpF8ej+3*(6Be$##HRze-71@=pK^hin9NqD}mfg$+Sp@toh!7kqj>X==@~L zJ7d153Y74=LV?Ike`~MzV}@5LSj|?nR@|9p#9Rx~WF2)+-pG0F|J0iRX6_Nd z#{XDYboPzXLY*@r)e6L&sTmkGru>a=GiRrh=IX46Vr96)Fn;Lv51TqxgDiD0IW~_1 zYWKPAL3`=wH%f}?_STlhk+%DfJWJeiQB{1nN{UH=?xVuNK@9!$XZ5jll$MGHsDMMe z_&r=R@M4E%)js`5f`--$H!D#k>-F#6p>8^%Je9acOI_?jZJ)Ukgnr^x6<@7WM7AES z>MlfDuPO$gYiE3^*i=jAw%LOTG|+(&0>ScI!YEhrVj35)cJ)Quu9LRil_ZnG7M8s| zh#c`ySD!U9Gdcz@hQiudS&?d3i%ZABtZ87N|=UI<7om>Mq^c1D95 z$d*~jz8bBxM6xM}W%*oMyaLoiak)Y*{Q$nrYqTYT2qbv;S}EX5 zf#KpKVZI;90{GTts8^Exmwn0~I8rRZ?sskG!Pd$TlOy~PF}d&#OED<*onDg&H{Sbe zI5QB^(If^uaB6VVsb$x)sMT@RMVGEuPJKU8QHPs~i2)t6q)?brUU1=fXOmT+UU44@ zBcAl8TRKhM7fFlB7!$&X232Yv3;~zRy_YfX>p&!Kx(l1ziUgWbmr{gh15cqJBv%B7 zM?2~tCl=URj;olem4!75^M1#QA#({u*Mm&;>j8)sH4_JhMPzCvK-%xiEGNda{iCTf ztB#{)w&N(Yn7)vdKU>eWLr=r`L#};f>gu8gBZ)LeE5q<43yc~LKg!!)j&SR9Q zh#SlM3J<0dskPPzArQerJAf$3{-Y8Fs<>!1*yWAVZJ6!D{c%flS}bUa^S zKBVrhZ86Krw|#4NRhRfguV#{hBV&8%%`y+*&#eZfPQKUrZ!E|;`EffcOgH2(jJD>N zZhrah%IFK=x`RKkdkz+gnd4?;G!yzS(HRYHUE;4h(f;~}=)^`8TDj%Moy?SCz5G*H zW*Rbnm`u_G;(eC%ig+D`sSkQYI)kHj{};(7&Xa)`R9Dt%!Enjb7>a3Fj*vYOvVH8) z+w}vYZsk77Wz5!Hm-lI~l#O;(Ld z{+Qfm-0=hRgjYj9`AW?uqRg~?6+y!8dU&N@&6cX_6Y%h&QDWR0+#!+5uWn(qE?4JE z=5c}Y)!drUiq|%79bEl&Y6QUspcgs52+AycO2z#=x~K9-bR%mQISJixP1W%HZXBz^ z!xk|!{#%;fpkbUs-dl*+gCQ}s8O@b~LI6f$;gMv2rn&T0p5HyU&!s=^lq*I9UixLa zeQx=rK|<2WxTnKmb6DcAYQB z?6b(LzQ;`$vzRl3g+L#X)vxNf7s<18x47D8j93yI{M>cY4(g=#CdZ8gI!*xYrV(t zf{-9k5(C=YXV|q&)A?W#vL2W15wh&*4O>H95EDiI3RuE74vX5pKot*p{XVljB3^2w zHQv!=Kmyz!{HpGWS8ZmK2)PCovYD{trl%UQg#Jq#acQQb$NCP~OPKkA*BQJL3cX~V zY~#MIIlaiTl5?WTqh7eRI=|4Q&Rm^i-R(4u#84TsYGV4@WkQwuSm0Y4@#yuK!3vn6 zY#d$n3qY>3G?Tn2rQuL7UhZUxW>x6-hF$OFOL!&TxNje*dzAXDzW8oS_W}BmcgFe= zbjUPg`wqLC?u_PJ?22-!i*l@8c*F5;NUtcG|kPYY+ZKqE2H$zTBsJD$?xH z=3s|+CIWVEbjTe1#K){BUgEkceYQ6j)!lEko`XMH5Ca8dSr8$D&K4uyxt5iD`g zQfsnAcXZF*;tziSOPXe782k$q;MPaglsp)*CdJ`W!opE$z9C_Tvk(gc00nc_h`@M5*TuzyMn<{P4Q0F7K zG{lmndMV1)bJckyln-JB2RXHAFt56kss6jR!V^8{^0mt=!k4$AEqi=z3$GGu7(Uxs z^y)U-|3GfLI;?rH`&sAUK-F$;P;eZ)kw(m4W$kI5^zua~HbdV#l1Kv#Ql@KQTWIVR z%95g&E}}7+&X}1c5lGZ!*4xuAbAiX0sK_|m!dm&XV?rG0F_5dmzOxCpldm^$G&1WF zRsiY{1zwJMkKd5hiu*IuY1e+)U#elz@dMdn{ZaNt~1|n zj+bj*bu}f`+S7&w`jOKGn9xg2q%7S`)=c9&z9u#&D$G8XG8{u8s%+mSEzfD$XaA6~ zS`qLA6fJYm0Lu}oQYn`~o$2Sw@!7?-ISeK z0Sgj>r0D%#;-uTpMf$>otGA6Vfml)14w(9PZbCVDuzI3PAr)*-n6T6CKck(~3`8Yu zR2nY+CVlAZ*NGHYqBfj1j>youR@*fbHLZB+Hp^>C3*`89wmQ@F7;3dbpvk3DuJe=U zS-~J2u;Z-}1m;EiV$75~Cy=e?4vNas;8GYGmA=tBGD!5!U{M6gY#B_8!NlbEx zcd+}Et7Td3fK$XeR&rNn;g~kbSD@EherQ?z-=%S=i2pBPZh!o&dYY&Y>2r|;h#i2I z7Qw*aAcw^iJutD5!8R)^3+nk)UH%4O)5&<ECqSYth#W zu+20FYHf0Y!@gg?SSbvUGjW1iap3ELZzB0&h%I(nB_TA5DD zh>}=)8kbvGYp<{}vCwY8D)p0!019bI8A*=(S ztU%9TuYDo9hvU%gSY}_-AY8bGRe!v3}W}pruzz^kkkmKZy6((4|XR_|>g{ zrV&7FSOr88{___QAATnB3Jjnkd9v5wvs$8_QHwxU@Zk-?it%W z*evK;eAST5SCFd+iH!v@`W0{CI?E2fgY;z;(v7pkMqai`$r3(Z3p#Bw=LNp67B=PS z)SP~Jc@6V_C7WSE_S^703eB({Iz!fh?nW)6AWipr?O)&VM1w(3uh(yM?Afo-Akuoo zFwB1X5?m#%XabUuv930a!Q9r*nu>>Uq{)zqdT5e9#`v6POK_{|#G1D-33(3$R0x6O zw?$2_HIymkL~`;p%$AT`Z)X4t2s0t28+e4cz7Tx?CrJQJ@y~f^R^|po0^$&yx`h%w z5?O}{%vTmuXW=PW`IlyrL)wAs`3{u7R%ySzOO04cg49-Pjw-oX75G-d*=#?A(!eDL zX>Rt5AyePJL_8z6Ws5Lvcd>;srNo7?cXY86of&+8lNRugdr*>8h0dCvW5n^OGBl9< z-u}(xpS$jZWhHaC>|fG?M2A=8#w&;T8(;fG%5#G|ro%?3^a+wD(ACq5d+i%+e=mPek%j&yxJr6910 zZN&%^{30)DFgB_N2-TVOwFYFGE&eyE0v4^Sd%JrTY|LLpREU5oIh#e{ zW-UdpnpAY`fDp!5;7A}}h)(YQh%>Z&Q9Psb-9{1Tjf5JovXN~(h$b1bw-J_gYtmh_ z1q1{bEd~D6#QOZUYc0K2|5LBP%t+Ejq1(l48P~9pFM@ZdQ8b5H0;Y`cyGx2;(S8y* zl(dw{a!>UyV`mQ}28PV2k6B7&)J<}D7HWY}sTV@6=yGq%x|&4Q``<3tTM2GWJ(2s^3asU3cBM)y(DSGG&IPgeTLza) z3%$lg$ive`lk}XWu*kWH2wjOe>;<`x_>5$NJ9f*VMcc%eGlUEE1bmiTyF9j_K6d7z zg<VuCaTTBS9agh4ej7X>;T>HUt>5o~!Q8Xdd^xH01|%#iwL@ z71oiv+fl2P8CQ(Z7ZZ`I35B?85uo)k-VL-Hr^fr(CZ zSg)8?B0s4Wr1WJV=gV5+PDtU;AP@REeujIPMoO8xgh&8e^}|>D(nq2sc@lPy$In(i z?k^|wm)~=*y6EfReK`$X!!%^T%)QP6Ve4`6=t_mS;E`Cw`s^9iep840306;Q7bs2Hv_K|3G+MD4|e%W?gLC5l_h zT#`muRD0OB58zP48hhG zag0+Y1uadTxoS-Q?~(<+9uRGqJ}_;l>=p%wTu*OVfAh?Oi$|CXC3mLc4O?Q+hr(m# zmXnAIXOerhD7+~ISMdHgzvi&(?x71@++af@o%F|*p4X@uSDNvwcR-7BYYiDSeZ4G^pbwHWv9=ETFa9!`82HeV@3_ zw*R4W{emUPjS}}!!1a=)x9*8SqOgh1uxXL@rD)9_5^nzsXjm!^F_@>gf5p!8(lrsi z&0xODGiZ-CZXV3WaK7>!Njj~Y0=R9s3e#MPqQ+7XUt5@5b{=OWl74TtzPU*P-v&Nq zguiLncvSSfgT87k;qVXALMrqo;0zjzuaK+9!H zJA8&upi7BjufiDFE?m9MllPiDa{2ajh5PQmYQJfxe35h2kSi70m-B5znq#$nBI66R z57dxazcJpgT4mLlDnjo}d#Wmd%R3t*XHfvb9Sweb7@6thwAG@82NJ&rxh}Sb(D-0y zc$d;~svuZ6mZ>v232dv>kAhVkE$3?+*bP`yMkQ*5oGLy>W$eYRGQa)F!nkG7(-k=Z zAin6Ma=Yos;I1gI`1*9(>>-fZx>j*|Ht_`05=g~(_bbFXl*TP#5T9|`@?kPy<>TC# za$UhIB9xM6mU0wuEWUVm8Z4C`h&a{wO5AA z-wx^~m^`#<-tI**5KXSsBQ03)x8=q;{F0Bqd z&Ii8Da3U4>UX9GQW{P4s77rYs)gI2S+io>>8fYX`SxkBUjtG#0M3I9U9;RbZxz z|3I~kadiU>5d#){1tZBg#U)|rA+z?f8~2e8R}y8wwp6v zx<{L36Qo$N{Z8EB_~Xp+Bh9f-sG!Z+>nAC#jIXa7>hx`vdxLe&tH|2Uy(GWptwGMA zubrWVQ6JMw)2LPJl+>0Rr>6FM&wg|6VtUU;O=@AgI$>WtptL4Dv8eOeB+deR^A`lk zY>(<2*3Mi8i&4XpGhuNm5;9qd0-~uptMFm%BR|lU?{+CRQ@AJ9mcQwFeCV5F6HYaG>S`c?Fxx;Zg0^d~mWAVa1?K4M6w1qpFnCK#gNsn=FXvrj1|b zfT$zc-BOyljJ9@+@Od2a?(;?y@j1tCm(Q@QCIEBFe|iC=JcZ;jydAJ4ps(6{NqsF+ zAcfX(aGjre?NE0}FA-mL$T>2gRk4r|NO?(2T`-#MGsF|OKQzbXNxC+x@j%enX^CB_ zy@OI$R-$dt&v#wEiPa;!liG;ZEBez{KG^hwfmUdzv@4~1?)}z1!h+ph0EY}d$+x&- zKn0H2W@@3x{J--ANPJsU zj_*~@^;QJ8imM7Wh`^lOF6rucK_!<{UfhQ=>jEx$yY6Qj{dhF#vw@*_Y(GvL?S8>{ z-|f=vwp6b*U#MmXx+;30vd}XzNw~Z7ONA3|UY@<)M1v%Jlk@S-m8unid6>_>@IpKY z3K5%0<;*yAvSMwpFSD;64dX|1UnF@f%n-UPI4FwkmAE=#YOzchlm)iXUurvt{(MoY zcqyeEac?KxCn&2_Xq-Hg|CFmGcu(Nw;)1&(zhnm{=Xkk$d0bn0dfB zKONb4I5(>Xo7*u^9J84cxvku&=ok{|$|BA+Ix~3z0OPFyPyVgRT17u-C^NHaA}lqSN{!; z9-|b2YyO``&VONUh=CsJU*GpSL*Wb{u#Q4m>@U- z$2pDv{7Zk*09j&(&x`JI2*W?w34Lsc@|Iisv;049SM*#ZW?qMb<{#nnc1s90ZIK!b z`{(VRtHkJgG8O!-kk#)&0*&kjDTLKHYnIIMkJ~|-1QF5OcE|lmQvCIDfeWe51Sq6SQ z*&geymO*0UPl*DnU#P;7X|2at+ngk-@2A~@FMj}~W@L3~vb%cJJ49jQgBpY5iroBF zyY@A|;>GdUX4PxUHc=EL7>DxG%a$5pBlAQGv?EGC_$+S^dEN{q9w+m|!ln^qW|FR) zw|d)*9$(Kjk6wGCQ8pOzd&PD+QrvZV8XM!IL*DB%fvoC z@F*t|5?2zw$=XsV8-N4*xPRZG0^^c(7^AZMuA$VmGYU=+Y&koqm(H(?Db@^VFz(36U;e&l3uZ?8`p5-pKF83)1av zwa}Fu-3K?Wb=>Z8TbAwp2$c7~FTakRUfZR5Z85LY`0!J*@#DP=31`+DIa_`$uGz@R zaMR4}V$o)8IX2RKI3{eeBoHYg3XEO~6r4w*2!NRs=H(Zt7vxaBIf!xB)0Ly7`6UBU zE%!Wt(*y8^&stkGgMmEI>vM#KgKLN7>%(5Tc)b>x-b|+o!Z(Qx#!~yOug3LG&tSQY zx6HKk+WJd(!c!gO2+oFeoVOr{OIMe0i=JtVuAp}wPFl3i&1$2Ix$o9#{c~v-eSy<} z%Tfp96Byrg$*dJ|Y5(g!)pQ|`#%1(Gr?y<_y|LDEjq0a1{gV07%Tay=5}vnLwK0UC zgKe}D;+yO=n!|iQc-iZx*-*IqHp`L0Q0~#iJjY|pu~lR6UNDiVx%k7tOU>NwYAykz zAIkAs+PA%z@$6gSk@vJK!5WB85->PBODB2XX@2)P3NjBDV5vUlJI4!Y~QA}eQV766WXLh4=nIdwV zeImw&1|t|Lb_-o-@d(0pKuOZ^%-Iq$_jlM=*J`+Jsoc^yk0*AqynVs8#H)?X+{lf3cK+z%bbQkADF`Nmi4me-H(pG@kO5PVcU;m4bfdpj-t^zQ)>9>-sVtaV?< zvFu{!zDdnC-aRwpR?uHOU)<0X6W{KFKwH`LI5x~iWSe?;KrfAif!s)~ zm^RE*LCqy4KQUJtNW1h#nZbXy%aF^L6O1DIq~ZOPOSf0MCAfb3t&GRo!h034`ZRfs zH@*MUbf5dw@qy=>O=|zAqDoF*X4(rzYv-DD>F3l}I~wL$fb*2icx zGB$-267MAI`kIsCnaJGj#443Z#a2mdJiTn#gWGefn-ZB#ij>A3E#b=pT|EI4D?9X= zqHI58FgCbfsHN<-l+J|k=JbRa+=EO)+%>WC2%?$HaNBKjJ;o)1gINlP%t%+k+yjI^ zXCw>K>xyxr=<6tsS@Zd-TicNoFcktxTqvv0YSkiJBhcL@;`5M?LTU}`isfOb_Wh^F zj-_VfbIZuBA6`rIm+q$BA_8+YYDDL4RxKQ&J?$78HKfOdqSKevq>1nm({04d8a{e9 z>B}<<0;V)#e>##gG@4U_S!3X;vOhmEf(2YDVxc66nCOI*y#bft^RJ( zW}!T7gkWK&5Wi>ZA>`F5Tyv0l5gQ;I{;&;;wcqhg?3$w8=dx+E;S=2-3lcP9-pjrU zZ^aLE^3yRVbGK!F+m>s1@ZZWD0J{SX9=E~28YuJbzW{eqIOLp;4o;YkrA3Hs?!%{l zEFds6xjjWS>DHzDB#gA}Z|j7$GTv7%ZU~-7_VO;eCKOz>=zr)%9oM?n%TJV2oN-UQ zS8Fq<*Ue?kC}ca-D2VqV3wH%C&5L?TRW|?PK@&a2BQ(pzdp$IL!5@FOLblQCwb4q) zeV4TA@mo1Z*<-+R4-nWN@SC{6Eze<0xx=m%Y+BDnzf9$Apgn*19DkW;@EvOWwgv7D^eVssYxuU&cTk%&2`>&h&@Znb4?WnY7!Xqcc_YpfpXm0a&bzDkCB z24JGn-z1x@nMA_VFm`6*nIYI~6n@`9aq0TNlC?GtWGyl@1EdyOLDbd~5+X-@P`K=V zqQg_?w7|5MOKn^+Bq2@;OV+63j86jnumC(#$$e z`UrwUo-0G@s8t$DsKaiVo1=&rC(I5BwaITNvk?aUj?m9=xv*(wic~UZ^{7o$VX*l@ zKjz~2lGtUp!R9FO((&B<;gyXKOg<*;(A#cRJQ!yNNB$RScyPor9% za6dtti{g#Vlg?=A-l&m1vO!4lMA1Z&rcNgnl8YDgY-wV5s1@F9l&Dj2)2DC8zR**@ zUvav2OPk%uqtP9EMz)N;U!IJZl*63fI}$N&$I_L9&61Vqt9+&v#<8xq#@{0@AtBF| zkUrh@)?nG=PLdx9sy>UQ7Y?~3IM~KWHhmy==a`}6xls9vn7KC&CQsr^AC(PF>*N(* zgqDXi2F+Sj_m6)RTsuFq0|&=Nb-Bde4&>5#u!sw+HN4uK9ReMUd{JDAkjGEmU`b?N>tl-B4 zVNZu;$C*Kfo}Q5m9COCk@V98fd$;zbJG}Mi6L6LWJ^kVs)tQzS78bd=)dnd>;b$Fs zZuTXIetsIPJ=u5c{w+K6v{G7a=b4&5Ua}>o^Lcr=Z1ha)W$`I$`D5j*=jk0Co_s|lwQUOBwipkA4*wWEQX z!3J*K!&%&M(S;oMiE^DremQOcw zoe~m{*ErIDHS09@CR}eEwvA4=z^po@=VO(&OifK4D|17XylKG{gU9bi>rUBAp5vdE z>{LgPT{h#98x^q^5pbp?-G#La1RJ)8_e zFq#i`Hz?c^6bRTus8W^bR9W9{Ly=q#@=w^jx&r#dyW)1O&oGzYdsNZ_&A^z0!y}1Z1X28t4 zv{z~WHuFgxAMmM;OIo8TYp)bLye+G96_fMm=;FKQ_tAR@M7$!VM@E-#mTMoM6Lmq5vUrrrmkgz+H{FcdIjZ=34!fFX9N)x2>z z+SZ30o*c~Cv*VEU`(INa$l=E+POT!nfI9m!Zfi>I^O364V$uNYW{c1rt;mBEqN3W& zYhOqZc*aExJTSu6T+M754ardPZ+AmDk2q*9gi3cuX-z+JEpW6gjc7KhzIrT=M{6G4 zETr{Fzu{5LD+P2e%D||RUjHt{02wvn&D}q{hDn(2Y<-@pG+A^cw|6W<{r(_90yrk) z625K3Xv>aR*cwKxoWKIFQr<81M5xiOLDH)YX(Rm^ACDpX_o{bvj~K*sKC>9N)z*5u z7j_`mzU$_yC*nj@AoW;7*E6aJcq&pTjPd(JgW;ND}&R=_cy zDQb8U4AY;Bqdy&ucz=iMr4z!Tb{ptQrAG3ECh3AIB4Sl5m%n=2cC{CZoik8` zd=R@rKOk=Z6 z{4troZ;s=p%0MR!ZLx^cWC&kkCn4+Jx7|1Ejza6AWd)pBa^8vlbeE}~yRU)tvis}K zAdXfJz?lCvW1V#Xlk0M{JP~0>kVui1bTSV`7gq7X@2!>_5sj4020a^!L_B5bgOWKr z?ScPnKCASMBK%TMbHAG}VU3t-C*5)4o5vH*O-ky`tj4^ZH!M|x-Y=U@9%R<b`y8q1^9B*neo87rb1^ z)RN6Kpiv5VMS44Ck{ZkhQX?LI=s0}#>*Tqz30%==4e>4xJCI*i;az2ZjUBQ0iWCcNtZ=K zkTnVo3n7#hj=T8k?GPfz{o>zQLr-#X`r%2iOBrZgcFW4Wkoiw&?`sXYFR_!0p}ntU zr21bah;+r4=9R-)Ap-E?!omaTH&Z)QYs;+F2?hNl=mEX(FPeiGI~Rcx`^C$(iooUT zA7DdSp>f(Ssb8m)7_j-1&wpbzaCx%HT5zm5FZkM;O4W(Iz+#I2kn1A*N$ko@H^m%v zFGBjBgo1qVwEt{ zarN`Nq@0BD3Crcg`(JU5r85$;J`Z*oh4TC3jp1kxrN%!p8dx<~fTUfMc4KMdp9y=? zZ&R!9@Mq?@U-^o`TGv8k_x`$MX@3zyjqR+RWxQh7r519xxRZb|_bzbRk)B}IIl3{W zL4t3(S~JE}ky+Z>blJw+lU!<$wNv_joY$ieZp{Ne#xu?<>mxRLGlVULNAwpspwX3g zr6t{mawl{pSbKM@JP22NAJ|>4cz@n3G?(rgj+<{3p$OVW5$yh9(256rcJfkKdc}G1 zO@4>N^yF%9`OCHCzTq&cMe>LZySI<6@{e0~zSB5n+ATpz&EJ?}2_AobIoXeUOuost znrwrqGig+)zZmhYAy+o3J7qR`V!`juTp8f5HGCJE8Hl2X8`jF&D*56Z26qc3CXKPRil6`S#(E z2n5$WcWc&10!{Blsl+{W4)zhuYPM_REuyTGwOoTsem!?EcbLqiF$dq2GHR|VMyKYp6u5aI4G*fXIB1(W4PlIB*G3p)iv3U;I*aAeV8@Th zd6J+$oINVDdU2F9`H3dvt%@+sTIFo%3R2+RWXzAc+1}dft}e7?=h|}yZ)A!`l*;?$ zp*LK3#|fP+Q9s1PhVroUTn=HJ(kYf=rTkwJ6R!r5-LS&{i^ODKNCx8!f@$UIw&DX` zOJMe|immD62H@ephQY4gPB^->pH`I~8zM8ZGJj_^b5<7>9z#wBokU>~?`{j)J+QRn zS+qlo(|MQ>^>0wl5&-1ekem^?8MB@b3mB5g1CDC~Q=6mj-@o6@W7Es;H2#_vywE~I z=otUNjFT~&l=G~nJtWlL_ou`2oG7h+4W((&)^|#FuUnrhyGWe7e+F~wItgs!LR8B= z8imQuZ=Lbv?yNjnU?X@EbZg1VWfAUWa%acj+%fO{8crzXq2u|&XeS>qK4gbzkj4MtD*IG)2D832oJPDV6oHO=T`c3Io z^OLn6Gj(mGw+CP=@6tdrtJ|w?m?|Y3@15j zs--dg^1`8h@{&E%qoi{_WZyZmNSi;|<-vHf5?B{vc#!Y4twk(U_PF40JmvVdTMYHX zf`mTd{0h018_e;GHkX$-Ht0J-{7=OeoUbI_)cA%Ub874JXpmoPDO)Idf-lhCcNA}# zgm_w{@yD-WX@(gT+I2nJpa!-!@-yO&Ny~WzzWv-Xxoa|H z(~QEo9shv;zl5b3KNLd9pnu7Z_zR@}q#Fk|Z1*YL_zys@zYTz7ieab^d;ZB~_D$Mv z{Q2*H5tpz%0L^d|k_kKU*RTF!IRGX4^`Dpd@1jImzh}&cZ?-MUFUFtvlYx|7HtlH0 zvI}3C|0UdOJC|Ay%;ipi>(n&bIE(y9a|)L-3?mEk0A^vqcvqN|X{g$ln_JiwRc)3x z>vdZ~J;@;pt^iK2nMhxv-Ir|uzrPO^Id;W;r0uDA-;m=Up&i2E{@rCk;g>h7ji3&fId zvdZ~}nJ%$lr*pIsOtNm9FEhW?X9Q{G-XMr&_rDiI7+9z^V*Q8EIRz_!SKZsA-~EeI zR7d&z*tfL^R8mR|3!U#{>k{e4vB=&k{UO_m)E*Awc3^C#N1 zoc0!08}-Kw8076m@2y+<0vcitZ_dSF`a$YZ-Bik&hz&psOo{Dmst`ezg8M63_yR;gL9qT@) zl9B(QK5N{T81))FKLG{IZjKnV9m)aVi zZ)>`l2mN~^LLlt!WE~Y4z&dUE^wf7M?ix6_i6`k3>%#@4dRuWv&Skb%kaK`YIqXr4 z-la!gg;#VqbETShcqX~M;_~TfUE9=uvnc#f4xsK`6(S2`jrYASKBgk*uPJAxBOY=&b#aeIU5K9&Xyc$BbeC1gZ?{XEKV_S9X z?~wm$fZf}adNoT0`g;WP?|%3c`Mb2D ze*T&J52d8jVp~3Zbp!slrqYXp+J0VDvaYHg%OF9+j2h=F@;>2k5JwOY0PT!yAiOgm ztQ0WQTc50r+l}<(aTS8&c49QIqBT2tSHDAff_9?xTH=u{@9utBZ8f-9G*8H_q`UTh4T z{r&cbE?2h;4kib5-hRjOrCuJfL{!|Y`tMfS)|hbh$9Gq`q{ZIp?TGf9L1i8AJX}P# zz|)qiJEhUI7>&CSp|1`E<&hnxN9}%yL_QWmIEMgK;dcL;f?bI`_Ceb|;uTxLsFUdS zL9%&Cf6Ez8zMh|y@&ZMT`L=GRIo*owNoW;!SR|hY%3!V*YZ?1b5Qn}HWZbz!-bPlk zW_fJdF^~Gy=Mx?2g->+R-Fm8_iVYUhzipklEtD5u@IvtTj#{v`8rW~SxN@}nQ%;E0 zgZ%+_cWNtJ!C^!Xfu zSzGUE{?@W!)w_eK$ozI`diN7Wh(bJk>0R&WCFtU>f(8Fkzd-7l@zx06J2MNq(~z`t z-s}Nck9Ya=F8C_vmpc_=nR`z-BPXhf05nIIm9Nh2;Mv>p--xa)=3qmJ)H@&$TQP#u z=hO%Swvh&D@piq=fj;>)O!Dei5n;tX$+nW*$vQZrv4HLI_-M_VZh>Xzfz=a#Sgqbh zq94a|%cWX=>zRABYI+Hhdn>eu$_+hogfAn=JfeM&MTpopCkxbsENxePpgvcxo#eeMcGlK|EKR5WAfa8LLGwTwx z014W`rPKyE!{M}yi1yqENvzb#M}LBW*k8j=%GWd89Tw0ST0CpmyN!*+t>)E-EdNSo z4MYa9ns8;jV@^F9l$C~{dyL-%!F~7fe2&xP<4D?+lU|Qmjf>U9LWp3_f!Af`v<-AC zm$O#b&8Gv!)R*LOyF4-vajQ+L+5nRuxB5yl^Bh6j5h5nn`H02ruJcy}I-B|Uck|>kpTq>-Cl% ztK*johJF2YJn4TFp;mZnE|aU;ON6?na$PSA4&X}C3G z%WlMiUdLMD{f@e8dVwL;>4GMSQT+KWc_{Pf50n<^N)>-16Pv$+9Ycj*u0s6@AA21 zc=MEdLxBs2?C+68>fdun1C4iuu$Jmizb+h#C1*}qnN%|63Y5&WaPS-2L=rtAD&mgq zgfiI+4gWDk}L$dC*rNpUp zRBXC;XC9`WM}ARBP7xb-OvGL0Hb+bJ_4a16>HUz#Cp$E`-%yqOpu`{gZ)f>76)=8U zz4vWN!Pe-o`OsUJDRXi#b4wS`9Cws*v`Ox}{HX1t zY73E1lIvHqo1;ob4JOWb!MA(6mgJV@|&EWP~n-okmWhGuntQ+5Q$$^6usKt3sX?~_yCDB7B~uRZ{^7N9y2 z^xmI2az;_nijb~jx1a8K0RB3CrlHQ=&IqlGu+=?YB(l?MbKt2~f2cxxoj#$@!jF1x zc+tM$PTf-OFnys&f$y_KeqcQcRh}(!eW{J}t<<6LQ{;TCKh73c1*ki@!|%XCATSx1 zLv1HcIz8t#lsU_||An0a;W##r5@NTC0=bNegeo3fLGqUR_iT5P?I<{*6&DvFq+zqK z@+De2>uLWYJHcvs+BUW}|KCF<91`>`lgc5ziRF^cjcXN`Ag!l*${enY z?2I(|EeYD2Y;69?rgku~#Yf0o%OFk3be?{4uh6amO`s}bhl_UnIX0gSLbB-8x5?9I zEG)*6hLv)bN?AAk#*g-?aNg&NzlKjc^>~-clt-EC6q?GG+UOvRpdIS7FZ(N=H@5;+ zfV7kxOCyQO`%hgg+D-)f#CXH2Cno;K4{{@V)Wfyx_jWlA8Cj3#B#A(zKSV?ucKv z%sz6=4Co`lboElV>{ptOKfN)gu&;`c97>M=m^P!~(|#*WszPfnwndZeE%44eP4ak} z?WkgDARwL4EYEFl47{Bz)xU`hryKX-5!WlDrZ8e_b5D2{(Y+d!19BEIMBn~#5OM{0 zcNW9u_3gAbR<;(Y5}9V{n9Gq9xs2?V?R7p2F8$&2qEi*mET$35{i?5T4%PW9xfX&t zTX|BsO#5>tcLJi*1LAioodDkHcV6?cKpD9J z?tU{>p8I~AKSVJx+wj;716&St9JoZ9#7fTvMwbko@y196eLT~8rEa-7A!cp8xTb$SL*1fqrw=lf~#mseO#}wSBX5)o_vo$}vHorEL#$t2lWQ5Qu zs%M75A@YYceO5$NSI@!6?*fml=QAhKIgSwN{9FUu=E9_6r*TTm5KZ>V%j%=AEnKa@PgNg*5T<~ZNKhtQzpO>Sv@LZ}+VYon~etI{m zY*K`4wqfS<^C8c2@f$iv+1h#el?&2(-{Q&BP4UIk2jQ)+ZG~4mGmQm0qqP?FbeGX$ zwwS2f%>d=w-HQjgIh%83rlyJ%AW32ja}(>vsG7m{+7%ljIF$N#Yy{gSryuz8rUD9sXF-USqJfEe$I`qhq?Fq zGw0uf?-gI+r0QH$?Gb|u?E%6<3t&AQkeujI$U19dG9Se^=kPzIN2_41?P%I6&?|K|;Exa$=F&>U zFV)Xd76(+#E!HgK6tE?xRv}q8>2+9WT~V3`v{h_&V+{HYr1{qS(Pv5Gs%5s`>Gp3! z_Ctr61qbq}jSgIkXKxx@EMnE{oMC4J;)>157`mB6|U9LdXUHX{PdU>mP;$_FZ9~Q2*TBt%S2nzs*btzn$F|^%G=-XoG^yC>>ZlD7Z=kzM@4Rz?U)=oXF$&aHN3kp+QB2x z6t>l$nbkY|)RF(qZ*fj?ZE)c$@55ZxvcE!ZkGUDS5a}9%=AXSi*V)*IsYS0AjxRxz zmWm{LlB!2zX1D6--A>d6`6G?67L$qdzP2jN+#>#;ZC*jjYY6tDOG82B?2*U*wv;$C z0(pkK9IxW{ZTBdBo4}{n>SDM@<&vy!Hu6_p&|1T&=Hler@~P^zmmyqp0CJ`U2})JP z;S(~kHuIDto{v^pOQf5v)6~%SID{+n%O7vj z{iM9i-mAaQvR9xOAs)fq@t(O`k#D8<64{p4Ruk%xeb)nPb>p=B_gAx%8`wb&naDcH z=iF~YBR07bXz6!deSSv_;XAg&@!xv!iqUPp@Y?~Ol#Ynnu7T=b@n1|;>#5~pD4wHrIl}7dI4;;Gjw0eed}&mLQT3>?d&QOZ>FS*q zB_%owmk}PayaHtwy!f?8pv}9=SHdk>j}w#;tHWC5wqny0GU7g@CJ?(LBvPgLjMY?N zcTC*tv!)@TSh;+?hP)^#!MySg{`4*jsp|#UoC$F2%eUoo{cCTZ8F0)TT>ZusRn>nt zJ-__+{d`3#^KlK4IMzM#eB3`-I->GZ!@Z?jeIZzG&GcYY{3_p}+tuv)?6TrGZ>TM| zFM3op^-FyGTF7!ef@2YQ`7%R&d#i4lt5I)k{2T4mV_I;gN@!|n;OatrN^gqVX3eBI zWB9d5mU_N`6c2&3ljc-HFD>_Riuwhu+gF0%BJeS%yfx9n8o?*s)>Vbz^Lxr4bVS{< z`K@(vQdx6Gq9>D@GyS|RZ;_MZXxqzF{{39W#b1O-FFzELJl&r>`Ab07O4CP91O@c1 z)h5{)+1Pl`_x5ZrmBNcCM~rFsX6@Hp%9HtaRMk}~g|O{1e`+Za?h^fE^$|O__oD%c z+-ecjbDzB<&s$vwsmUlEdK;*)w(Ick79+AwD+RndG8oo9H1U;p{rZT~l+=#8g$sQI zkg{`e{gmg5LcnYKVoRxdSV=`gFLqRd&OP{8e^cs2lg9UkEtK z!d9v*T}O>lxgGuXn{92>aJ3sRT520fK!so;0zw>47C?h!8VYqd+?w0uY`C|5a+y2$ z(z{8@Z5n&eE z`*87Cwkz)bnAieR6xVj}>|*5WO`#5rb_{2$$;mmhX!2?f=+nl2uHR%Oto!kJqc5Yf z(LbgUnKAW+<=bT_-DTdx@>XrvHS^snFTB--UjBAAS~Iwf{TV_U;k~9L=WPo;ROkWNRC<4X&00=4lKvu++1I-< z|Di&EPK)3R7H+3GhBA_OVxs|g#7d+)caD(n0Czog_*|Q6$gt562yc|@G5Pk! znqOq$>h$XZh7(_^B+1H$v?$@#w2&*C17@;LPJK4x2w#`%H#yM|$hMc~6Vle!_cs z&!=U(TO18N$f&9JscV?J$=u;R6L_rS7N=E7$y}OGQx@wF#kazWwFc@AyuM*w4=vZK z5Y-}Orwb8g7n!q?wr;PODg_-z5rJI^-6P*1zJ&|9kdN{+OiV>#2f zGlM4GwT)6|j*G3^6S9m?G|~$kJvUVL^`-rdRei+r?#3;DNHN+8iCV^1y@&QJ&N(dh zKzu-$OFsJEszvu((vnvNdQ%@l1Fy%;(~fCq)&dk%>p9RNX}s@$rxq!2{rrF0Aqj?B zrhF24?>=00!^FhIwjrcW&3zyPWu_ZggNfMoGssUK5{mvVD?H7Of1qBW6r0pSxdgGV zx#j^eV>2c6E~eE#xdOjZUwd=^c=eW7-iAw+7j2ef79}9+ZUg}@Tsc?8mO7+5ssOXB zJHyLU9!eIZnTxJgS7#eP%}d}leM~D^%ojfwcE{SFz3{!#TX&oDP9czBSz^4F`uI4W z(?*g$OzKTcDXlwg-X9OW3986XW2=3aMLzZL1i-ri>xGOQg4&J?c!@^7&0!%IH`dH; z`ciTB%-Q{nwBNt}yN!}L$4;KxWbkJnB}1p?KkAb+?_7rdkE@fIx-%TCc9qjMT+L_z z#wIykdqyumor)!;(*-7?nj^@&+59`)!kXU-htHqHyUL#iIVl|Bev(`~zWd*b#f!t8 zx#uer_rsqZIw#9pGGpgt=DsN@8LZ8J5C(fw+Xv>13z8ue-%OhssjIy z(--wgw1j-Ttl>stJJ*Z}MbCn{ztfze+A#NM~Em z;mi%!+DM}x5_kQT{eIL&u)l>}^mQ(okc+Lnq>il`U z!r_8dV^)*5O>j;5tWAcPZNQN=B1~C|uvi9y;IgIv1F!&f7NCS$W~*o;;x}=PT1@p6 z=2BA&x+@LB7@e<#hlnNtq0*Er{9bxXASG}-&MG9u=kBGyDkgv08xYm!K1C{8n{Z6S z`kL7pbcKS*DN=Vg4@dQA8z|uU`Gt)P3Uf^ORSI^{qfJlwg4yS?h0B9l%-WvVR}sSN z2YfA2-`vf-L7nY-=UJ$*fjxbH>3z%*tszB?3XT~|24M&Fw+v&Xj7*0=j+s9Yu7*BD zVZBoBXw&A;l!j_kqs}n`xZ?>{pEvmY^v9o|>y(4gnt<+vIiMT7ENQ08{U+Ub=G?Qm zlXU&cy+~A`r)v)9#}er$QFL#o86;;9{WTnBkDB0!(ixWk?cuK|34EMv-86Q$m(595 zclLWtC7-&-^|LPIlU=)mPpUr#LC?=Hs`)yiqzTrslrO3z+I+ns7&Le%&=6cbpRUcQ z-vxVClYSC}p0ZPsec*4&!k1Zm4vH~sJ9n`1yc8X*;jxKkH~KVkL4`a~+$hY?nBZ0E znb^i~3#wJf%{dp=J&Wn4e0}zWs;wM#NRC*CZmF77ttmLx!45QO2H}E~Ws)RtC@d1S z$lL|D6ZWwSwtpd7>PC5BCm3p-w;r;Wip-)V5o`|`^~mFh_mpa`=6_kLzc72fbanlv zBl0tSd+BN^qn@6fpxdf9m8wT5ynuSTls9kOKPAO8xyTzxTe5-c(7d-28WwxQ7a5Rz`Bu58tFBS-)(=RwsA$8x+If z%5b<;_{38|wq0cZOOE?ewIuBXqByM=#W!uPY-PBG!6FCZ-<&H5O;#LN6vYRUo{(JwOxk&MW(!|?qJH+MR;hkv^zlfd3fe<1i7w%D*b}G%Xj@+8 z1RR;V9H1=4N4P6~f=XJ(!p}9RvTWM>lVankx;^0kn9^T1VK;8b%c=eD<8?6Qy2QqX zo`qMdnQ25EaIyGG+lK6>S~LerKIYM+A!Wu2%)eApy4}a_x>D1-@e|%;)8^rd9o6Y4 zp(82{+?j9tpWC&k=#?-9_RuFi!`U5KN{Z2H7AiK`pWMWR7X^u9O(s2_15xl0T}N>pU`w@-ko z-m_5K(0Fa)I8`l%hBfBRY_equ4Z164pn?>Xo|6)ZV{e9e1J)td!^s%DFKV7gRg$~1 zu4kaLZ(Bf}3xAGx+9Vm%c?UEw>RE}Z2V$XTIv=I{8%4RE4s>xBsxqlT9Z3Q6hnT3X zm5H-Wc6)?RH|?8LGqm>+6>k&Fj^jN09hyOj1mwjKB#CElc1_DO{5flY={;ag#3fJp z1*DUT%d{bC7}dx?Z#3!Ze=J>K%V3(mJuP!;!bqxuR`t$w+K|K-TYwK|MKgnvjhpc? zDeU42<@dBdo+X@D-#zq6?o4H(5cx{Q$FWb|k%O9(b4`PZ^8*Ys)P#}N%wz6(QRJ)& zIhdF~Y1#voLngr@R1?%9_zowj_kjJZfst10?~Z=h_j}8PE~=%`3-8LdI^?YtAMV4X z%?D_6su%4ObVF}8&zB`~{oG_L^v&#Ip(kw~HrK~Y#zXMV`J`n=WERjv+E6Nqv#+dT ztAp{8FeX)i@}!`~ZA$0!lPI9Vn#HP?s=$z$OF#Qqf2_IAxNQN^`+%2}J0h#g4)xb9 zDU*D(#u7=M`SNJaj>fVmDo6$au1-gt1UbR6(UO&!lDMJX$}D}eo$irWWv_xl^Tno_ z8Yc^qjHkgBfaed28wXv0`6-Lq%X<>M(MV_on>}yOnZHZ&HfhFtxvgJyd&8>kEg-51 znbM-(I;ZQlRvzk?mw8`9;M-|0RnN|=fW50IY!$%iOG8HPk`ZZVfaQ)9yC;kkwu!7( zBqAOu=uDKzHy}MU{BG9X_9L%ImCo1h@(1ezUZ^Y4n2bz{ZAm1Zq)t$Ay6d5YLz2FI z4p(=W=4+iSi5vSg~t#ibQG^vKa3L-#^(B zJ~)PI%H`h$3*-pu61Z2`LBBt=DeSMUToc>Us;03Ch@t32JV*&yQ&yWvff9=AiFdV# ztQ(H&Ql1Y~$PqKBaF+~aad-1;6-t4=C~A-#D#%@I8oihT#nYa#FDem~!15h!@CquO zxNoxJLxH?@9ma^13?!QAdt#b1zYhk2)=FJb#F^L&+Gah0SmMzfTzF?+V!}{>?|)h z-APLRAi7H|t9Ul(=1_nkdXQ+GE$KHFEEQQZi{1Z?*v`Z+2+pE8W++A^%bug8o%5nB zczrHm7=O5b76R@L4zZ3A`&tb1v5NC z+XAxTEc?or>s?KA1Ey^Z0c2chQGXqipdGH=W8u!W?Fb@{&piZ-xZbu>3=4z~;z#|$ zk$Yvbxnlt-yb94t-75J{kvD+uHASKq3d^Zfk{9K0>^V4%iPF7aL0C!3WhBn8?nPN~ z0j8;EXJpNHch&vp*bf)>xuc7rLCLcrdDQ&C3*`94M0PYgK(3$kN)3%O;oQ@Oc($|8 zPiRzsn{4tvYK80TIMb=6St>sw>`bRi_>=F32m2|%qMWM?X35ws6jnDO9MO%!ft3zH zFJXwWLy@FA@H z7QdgPgeKK1pbANejxTB`G&L>aqru#>93^|(VcYHwc;LSaJ^wOBCwrT9^gi`UP*N{m z!ND?_`eKr#v}Yj_Jp&4XNcksmCY(S{snQ%)FtUbxy5t;y^={j@i`w2+R;;Kq zn*oqf%eOn7)O(jJxbBjkVeUbPN2#0$Jto&P7$rWS`a4LvmfsNCjn`)ZseKPec z4eL7PGG%DMdjO2vywUhO0P>IIV<{PSECVu`iI8JJ%C-9oI+x~$nXD!yLUAGv@f)W} zV8WRQyobZ${^5_x8!$EQBE^gfK5&8qfTQfKaoA{Z1po#$o)F^VLDb_clx`^sk27v{ zcC@SDKxJjb=vTu=7`JcyA%EB=6^rQ{lcU^~LbAxN83=n;5;VNr&;tff=Q>pCidHVc z&bND17@`S2oGq$30A^CHbU*_0qD_GwUx)GvO+U~}1Ed*|E;rRl2+PK5Nh1zvV8UN3 zz7Rg_4x}I@R0Wdmi2~~);d1D5ZXX}I=^$P02|pimf&FP{ zh1gCDgr$^CRL6txtacAU-Kk3JlIxZB1Rysd6vzoD@iaWYCi?MTWxzkZ1Aqc1qRd9b zze0pG0O!Mf4*8>v+RUc!)hM3sY4%4GsNE}t>Uo>PX`(4k4>FuHN&zOlTz^~k7Id$I z4q-NPaYs!QPHEEdHV{p0UZfD3RT}nxO_%PCR}C1>R8{Q7n`!|7r!dEm704>A<1#Ase%r;=r~vETv9t{ICD|guwjr?05uAO2`g0m z!XD0yQlI$6-I#DLz|mgIzO=`OE`|#}#A&LSg4Mvfy`V5iF#uf|-a7GyI(Rkdb=3g? z&wo`#F^^O@Apd0mwJRlVOI3!EfdX0uuFfer3gH<_hsehl(vEShz5{Oas~p|%vCRU~ z%?#Y5A)(r*z72|Jrp%hj71~UY?xV@g-udttZm>JD#<&IotJaO6pM>tLM7XnTyh&q3 zZ34i6V0dp9F}{NT53dy8fO#2A4tM9IM6s*TeI7(@TqgIf)_eFcKiLP(K|+CxIOX#P zCcMd6-2A|`+HwuWgGLj9Ara01D4ox&C~|&ze?H)&s6WX!A)bzX&QA@4?$$9zaKpA_E}}4pQLx8VbO?cj>7Vns*X#DOD#FUBk6h&&1!UtK{wGpstNFd!WZG7qeBj zd{t_T6n+5AqQH>cTr4!a;&{78vC8&#kerEaI=5iZ`nw%e4)VYA+UoyeDIABKCz;K7 z>zH5((`pb{iaUMIo}SQd!~=i?HDsbvly9uqJM?`2bkChoR{$1VSNt7B7Te3W2i6#` zI^A2PVc&w?fPT=tJ9a#ImioB@2~wUCDNH4R48i^*bxlgFq*-|)KA}u!J&^?ty_nN% z=Zsevu~HZYw&}RLQv&1c{e#7n$XRE{Zc_<8zzBFmAuSaj((e!?T?!&oC10i8$Pi2J z6s0xgD)zc_WDw!ahAas;#cp|6Mf7YpZSE8K?))&=?`q3?#TJ7t(-SxT=8m4F(kX!hudKImuB-3@&WA4JS7x|;ZFr)0Ic3OlI zfF9;&Jvm9USPsM}C=^B1R`)1hyU&G|FOAtPl9PoHkr(#tw0kV}(U5#oUX+h_#UqN_ z4TeJ47s(~|94=M*DeDah1hdoYrzUa>0_8lQBPq0?7#!#z?Q_UKuxH%EKKGXR#p>Da z1rxKOVhYGJ9RPGo&p=Jn^9Kr37VlB5S@R2)baoA<=lja4n5;TrG6WW0I)ozTH_!pF z-JOg?)xu#^Zn8EGp__msdr)N%BkCHJv?jz*t6_7|da!v}$dfFJ(Obe{X;lR0BV;dB zaT#zlFEbS$TI#^DPbk(y$Ay+xm1nlY)%n!aGT?MvxylOt%FVdV_wDxLs_Sns>BR#~ zAV~!lOAf zRTL(?8%PQ|@li5@;@Rq!i#qZ$OHJNbSa%AN-YuAIiJ71^myUTs>})BSiWR7!?h4;T z0uZ+cr6T$JT3Lq@JSi$p+A&Ra)f9KY2Tcz}A|}`4y<=fz7kd#3P)e~BiJ?9f(NqBF zBgX}v?W`WJp*EK8$$}j@P1}WaJ z{8AI5vIZHE03)u9d>M~&pFh-cKy6(TWf*5%zaY+cP&^)uY#OVgd&&WzLOZ?;B><6p zQN-Y=+VVgha0P*?gaTv$$Lo|KgJI#FiCYIg4V|&?8LK}?r(t(Ukc!iznktY?$b%^= z7TUd&z^RIMk?)9{wKfBM;~yc)$5#mKYgU_co)L62d)&^{*0GzZq4oqfZ1n{u+YdTf zz&#KMX|qB@+OEn3)XohLz243?p8NN;=pgMcI%*q#L#kKoen^uOL_-rYw2}>#~}#gG6mbk1U$CTmgo2VLfbtZF*IHv$o;xT~E_| z(s^Keh@Da!JD6ZJG*KDf?(3v5u(F2}Eg)THw*OJmX$@Y@RK&+1z(L)#3Zsqpg51Aj zLu7S3oP{xTg8L1N{p>vbAyj?XlB8c!1SP0h0(*9p3?c)y2XkKNb+C$GQTMgZ2PXhS zGonl2o)b}*UArWOkBf>c;?axyOF*GF0sne6$nc&r@+D>QflTHy6UllNl!}hpuXV*% zFL>RgU5nFyeYCHi8Dk|JemW+m! z9$hRRkfx7RTBFBv#(C$PK2wKx(zSBmxqGBnjDZ0$8mha53)*9Br$m6t<`g7y4Aw1} z<->d3@&=%?6YWE-&j4*2VUo@52_DHQuzo#X>yh(C(#B2noRytM_}wO+@OabV$d+fU zp2dy>$DsEA@Y!g6_+_XPUH)E_vuXEIpyqUCNH}jfv1#Hn*Di}$VNq96Z(a;^rw4}L z4PSv4_(6CB0d^!zLHVwAuG5*eb)Wfi_N;n=Q!;@MLnb1jB?a@;S}4)q^0iHS?d-Ta zWO%e()znUjiCjmWehC}fR%S6*u|Lw)O%YQNU{@T|zMJOvy#AbUXG}ZU5eD~+uy9YO z)eH}XjI37&zCJR|82EI)<^4d&?0C5tComGvS}{dsT@Ubc-qAE6nnF9QmCO{Z4{?V( zH8yG59uP}$a!;RqbF4b8l-T%Vu6x$b9b0|w6_dJ( zHD6I)4$lVJUMnA%nZ#-)qaR}n1SsmAb$REZK5nZ!3Q_{s>R=0)}qA7TM?(jMg0Q)_o zO_U0zeSKuv8Pd6w;@2_%Pd~<5>>d#f*iNjNEQ|S=t12S5P7ZF`wfteraWUg=dNl7An;{35$OU72n0}+P9XG-QbGy6 zcaRRDhMGXg%k`Y+J?FXa`}_kZpZ#IKyE`+xvoo`^Gn+_lEj5a}Om|60NGM*tRDMH3 za$|#pf~oNY)*jL1riz4i3WCxE_*jJ6xQ zyStxv6J2RPe*c*KQ1P+yH9A4P=LruI9+2OszDA~VlkD0b5?VUtj^~P74PgiJ81udJ z1u3UFPnd63(8-)PX#3jx^yg7Ah_aCy+tiHA7TU^`acpr4FjD*za+SB{o2{~L8 z+uOx|h!;WZH||$?Dknd|z{&}yl&V0Yy*SBd*4Z5ly(VSYfj5b^*GgAD1IeJdsJS$N zCoyW_Pb&9agZn6q9!qVkSlP+@j#K)FQ)?a&(d#5R*GMQrl%ww6eBaNId#^p=#yc6W zV0U)iT6S(0wqz13bI5(NTn~vhdGUAesP!(GP!F1ki8N;-8l1Sxudg_7U3RoMhp4rR zNaiZ5eM~%Y1zx};-+f1xwN%4G-PP|ziTl3a6D(VZdcj$opGvY^^tV~g}%Q;nnk_=VwK-ohmJ zlh(gAP-S<&4K2su-;n#S6fYmb_Q{C$G-|1onD=)mvr|oeGsMv?K`cKf3BIP&)6E@sPrbI~p+h`!=tusMi!9wElQkDa6jN%;VA$ zeIvK1{ie*fHZMQH;pu^e$(yMCzy#OJzx^%8inmoz3~yGoaWq^?HHXaOH2$r?(U{lt zp&tHT8OvEn1u-2hJ=txk6(iB^#I(|~{;u#Y2Dw5|KE9a5>Fv#!5Ry;fBH;ViLa*7p zMLJLipnHu)h~C!^~rK1@2b`5*=FR4jOB zayNE{jr#ihZ?`*29^bumrum#|$09_yzubO(I3Rru4-Bx>5JfR~#_+3RscwCKSEBS+ z;I8)Ny}rAnH$qOzj!fk~QnJPdpVO0hyj6b~I65h?}IP(cVihdI!B=&ssPjx}-C~!WuIh=bSvX1IR4MPf&w(yY9#g>xs_UG75ac(km1&zE}k`$oVSdhsUoIm64}lKj^s zvMG*2$jWY3|0b&qn=6|Ld(KeoE&uX*!6TZL_ww#cwC^poZwrT+4Bkn5F0etOu*l&@ zp81|%T59cgVA;*)Fo`>%W%sEqKT2~1zobq4_~jABOM3B;Vh++SR&2=i@9umLUxyBV zXW6)^NAmT#zV;2Tdt2}E*&G}E&u;W7fwQyjJPv*TR&k%R{XX^%#fNLHcmIBv48Nni zt@xNz{C?+sJL+GbnA$a!NtZa5$Sj!-*pM+dUz$8-esiBcdh|IHhf4xm7l}131Srn@ zEnZM%oWpd{DE?7iLUflt?d^`32hY1(aSm}1)dZbD?Q+!^=#&w60GyBs565{jO>L=y;Z2#xu}}xy4Z25 zPw)LTI$Or*4v6J1q^yytAQ^L5UwdZT_}e|p2&CsE9g_)DFcUYEE)!Lnd)l)!Xqvz? zmuGFy+#JT;$)$BN9`-+6kC=%@cYIwoaSyANnPtkP7KxkqBlkD#px~_HOcpA2$#^Ls zBvf+0gr`J7DEx$E$9TG^^3=i3-jGb=CVwdPpXmGQ(yIB7D`_$xG(U(`7W)Ah3S&@` zS1sn8=W2e07!(`n9x@)p4gDIV8=m{goO}K_qTH_T=3OJIgE!p5>BdishIEBpnR7>z?NR(VcDGc?sGdzLdCBz8wS=DP9P>axUg?21BXV7zZad@LwSBz2D# z%)B5EeA*%m3@6eO%`b0VGq`pmL_4J7y7_hWQ2EfcP-G}~=#5aP7fUbtUpTxtelZY! z(8|<$tu^n1TzHD|nToS=qH>`MkBY|+rXPww?34plV4otR>!Mg6I6VmD?dNdi$mFvS zy3`2e`_7A0`=Mv4_kgQ_gOStU0*j=5@ml}M%R+5OZ6=Nq9?b-Rh{EHr9Hbw=w!xw?uc)7y5+L&p_b?ftG^o!uy5V2+?0%KkW-rb0>eCsl0j} zS*(};tWZNwQeR8Q)!?$2vt+3hqGf-|v@U^$2 zKf+PevFB;_no51NeFlB{LJ}nmC8{N$>GElgX~W8eo%;)aTXLYC0<0XYiCU5Um5BF< z_+6Db2L(liM2mv?;^wmEtV?b}&F#e7e{M_MW~ZPd&mnhXxD*{0e=X@KrOK4fXva*I zD*8=2oi?>mlun%DS&5{%xUR&E-MDLXKx;ic>)@5RFHjhh*F{OI#({KVY>FGcu(_6e5C{Mk@SNh)E=OKMBzH;m7eP4*s0;IVr0m_yct1k4{I9UA!ynJ!#3$Mah92R7v!iQa)yTXR9xZrA;y zf3)1Vfctw7PrnuAAMF_hv-oM2!6znAXZ*w<4g8TmS|Ct&_szWNcO4z?NX(o5U$H&= zJ)3CCl!n65qC&*JS$B!5UZ=spgx{DvLIUA}NGcI1uqzs9S0jQDi* zF3-HVYrHdf`_8uC*yfy!_rT7)eM->8^BVnA0^3MtVAJg;-#yH%+s?>V!}0TF#Sx{d z2&r@Zb2s@nL0(qRT^84}@ox{U`Z|S_DTTE3V_TY|jZ;ZYXUiT0c_YPb zT1|dohSCqeKZ9l&Bn+Gm3=^9ct$N$xLkWoiZ{KKlJ#3CC8Jb=CfX z@hMq;Uf2-_JhbC!za33K9i10#vjV6w+hp5YtZC)-ruJ0)-Awh{UoTUr^v#=xO$qh3 zMR7S6e+!Mn46R11iZS%AW=&JmUZr*G<4wskHBP4Yc7IkU>}}Ol)QEV^ZfP{xjfZ0b z2H{gygB9a}gy5H1+6yaT%Ty)jWCW#nEUEl$+-*Dd6slX~>f{6QW{ecUoX0B!qhr6W z554UXdB;hCxq7(aobS^wge$%)V7&Nm2G*fGnQu;~SJVkGHMhp=WaNI9s*Pi5yE`#zZXwGO@3R zO+Ng3kV5w5Zt|V4cT=MEq6{L(qNHA$r8p;Secny>eD^2(|q!f@@`y)*5{GN!!oqX#N8URQ)Ma`$h4HTq22dspwc{hQ0~j1X3dqmElHP* z*9t-$6gR&dEmnR$8mufk+CTbK*;T3FpyM#)AXb1RU7~Eyd;D{IE-STBgrA?^f8nzI zOB3mX#qBQ5(k^=i=``G>sCcx1DSzk1r-7j}D>VrE&?F8BI9OZ3JD{KkPi5_P25!+l zrehO%0$hN+D?1zOVE^?&{12q+E?T`oAZL-BzwwRXoX%5|^XlDo{TDaDITsRP7)q%w zsho82G|*h(d%H5kc!FH%^ut|%Yw1qcE_tKfEcam5yuy%Y$?0U!>#U(0^P~&xlqZIO z*NpCrUxH!+Og=<<=KYScp`y5(ybCL00fi>7Y9wA)w9AJ25%WF(%b>HlUm<_-1_=)?Rko& zo*30%)}7m$99r~Ju|VY_<@3bPO0$Y)KjlDszn`NA`}|_M(~T2ub%^(v{7L@p+V*Dv z*^7H$^g%A9GubedYAJoCh3B&XgP`>jLBU3{y_~cTY=_n3?Z@1YCw_hWDPVolGu=hj z6O_ECEsfa7d7M{SI7>D_73av}sJqp{^jMRlkFmsZXHKrxHwKe2S~bSCMS-Db_)K9# z-oi8|*)NKev=M`g3xuuT~Al-u0$9UIvhQKsWYW>+P$foNK_UD2(vGIM`^P)!%cZF7!_Lo0n zzT@5<{B@-^NtjJw4zy79R!-@#M!i{bUZSI!d@z2}&jXm8NYzV)y2GraCay*6RXzLb zl}paIeE3J|n`gut6|5JZNKjZ`Pw2DyWTwHkD}8XY?Nzewc3vG8*MBFIPKMXwkv^a4 zPd|&*&;aC#%G`snH$~zQe@B&rU)4R`XaU2?LVd?=r?#zfj6kW6goYFXY}fJ~I#SJF zi(4#7Odg-HD#@C;yl0HecUr)T@@G(ZsfFtWWgLZDd^_fQX&8)W2#fk|QRa%-4 z1t@51p=A{Z~Ewb8U0 z!KlHwD_STfDcUVICWd|rwi>eDw63y*zrAN|`fgzP^U}uJ(Q4?Tpu|e%a`&44BI~mH z-;_mm>PZnkp)EQ#HPP`$8oWLgpNp!s%^4Tl`OKOu`=16lPrFq0+h2Cf$N4TqziLeRQUg4CZ&|Z{4y4$W7B6W`W24ju{bXo)B4}ZLEMV1b`3!?9ezC>_*Q>jY8U} zhM%AZnvah(!*+02*C)zK_YOspt{XR`@$VMzo2hJ&RNQAmP)Pswe*eUtB}9?0)$W`{ z`JN?pcf8k&wMPw~)A@w-vhVw+*-p-4ai!kj7 zT$*v{aPV}1JqW!+qpWVp3}-S|Xd(}i2oqG|%6Vu-pQJEC?)mH&3XY};{RoB6BSid_$@8BKI3C*?)!)Fk`t7!tik3w3dTX+H>{L+b4Zqz zlgvisD|FZFgoKR@OeBnIrjGWYuIE#1JHMCncdhqWHWSyUHze1Z|MKtJvzbLOg$f6Z1~eL26KU6 zb8HMC*w8Es9nLWN*3US|!Rv*O8n>+i?Xi6JqW*kVk5^v})(+_|Sua#f(hG{H+bP@L z16!?MpRa7<>X#GF2nOz7JJ#7r+8a(7Fl>nlCe-&HnV&>V-F5I@7@%>|$M{O2Btirq{N+SU+;izDQAD?uKk@TGUPoPWziom#VVyO zLRH>qzc!wJ{gE@B+fV>lScZ6<>s(q6t}@vvxE-5YpWJe?$vP*C4zR?#-j?~eqq?AJ ztr6CXNQIT@Rlly@o#}qZ>^!mddx>Ff6vMfcwkEm!Zp9dRuSTvlFy<%5bhUFWi)~+` zD{GjUlOc^ok`BpL{`f94EA>-4JL-jULrJ7yq?`;e9QV8X+4xat*o$ux5n~kr5VJTz z7P-%z{*4De{}jyN3hkH%_-V}q-pDPOIC~xF6sS!>RV=v=AX9WZTgd5+fZU=9Ci7C4 z`PxzIiN>KfGxZIzo33EB=_Xe5g#gFh2!J*wf{lVall?~wU%YVqnC5nxvL>AZeV`7Y z4?qK)A*NaN?`q;_qI`YV*@elQTUz|qmAX57L9Xf#)E59!P%`2Z{P96e5SkP1+uQ&J z;}hybO=X&Us!Yz3HtkNBDyw~6T<2^FsM~v}Ke0H?u)ff~Qd3FOl>CEuN^OH52sRQ< zhSt?idQ($y2g&bDlDEre<)pxAq^bQ#$qi!jNJ99TuqY?s?xRPKM8wzESJ?Ndup7iqL`+6TMnqIxL|k0xYJ`xdzpK|fKOt97uK$JP z|Hq?j<7owPaQAX>bA9v=-glO6-d+lvoc|E|-@pGgPa8jn|3k^u^S_sMwLp=7dPKy8 zMMeHM?kiCFe_Ca=9sF#Zjg%c+Y+OCBXaK}SMMdTR3*bMx{twCj0c!kzK&2%A1@eFN z{5Pb$$UiIiA1nG_?E0_PD}Dj)%8UGO-UIGJWP7hRZ|lT>{_fNH)9r1~U4?E1#>EEgl<{6bUe!s(#PoS_V1J!d zYsXEE-~Ue*6S5e4m-?}g_r~7ut&+%6-~UVTdQ?^y)|V@EGt)l3pLZG;@FV?S2c-Vq z)mkL0sDm}S7_qypS2Tz`j$in9Mg(XsdQ^lgz@;kX9Jl2Pvn)U+>r&TfME_?Afor~1 zbIJA91Ge>e`p!TpC;TD=2FfhRd7u8zVgFU%;1xAX-~QV(0xYv2YiLvTHtnn}@ldjv z!(1q_MiBmeC*B%z4PrD8s@E3AYgt=;4d<+VIJ;wv^#zJC3(HdEi2M_lo2y}qIiTK@UL zSi`0gk0@W4>pXA&rGUJy&(z~s9cSMLhfN(GZ=$3+T~Zn=Br}yZeMPf&s_vQP$-|xh zFMvFvUf1E0EpX?$h(8zA6I$S~)vjg6hyH4A&m*S6ye&S}J^@$Rw~5V_g@XC;;UUH3B{lUMAbOuEDH18Zm z^_>W!waoKUw2rk272t+hYynzm#%FBKS+Zd#Uw7B1S~f^Oui7!TXj(PP+t@&3%#Lq| zW#*OZ1UQdj=0Bv`_fc6%`skGixFEZNjS&?&N%31g!Bd8S!Xo6bZ&WwH9Njdn?9(&T zZ!lKlR5OBEsZc;qddi{o(;+U=fKC(1e^aawUd|Ao^qKEl72xD|-)4*v(~!XY;P#a_ zvCM@+=M=^W*EbO?lYfx9{jxLKURWK?Q{(=f?;Aw`ndT=W2mx=It&8ISkQQF15G`g? zWEa4A*SZv6P!eEjNvCAqH=>o7Dhr#cM+73fol`2bfT>zOV;OIg#}Fgk$xhaM(96`4$ar+4Vq5lb~2tlc){fypDF|SfT?oC6T2)yL&9h-;t=z50I8Hb z<02oCk-ao242CxsI!*NB3o~ZO!>hdtC6uf5)j>k=tzvYrzRbCyi&CEsy&e2tY~PhB z+%CL1?P4G?|7ZKmhP}J+EAP3!i=(c(fth~FPNM!gL-(kDpqx3cLN%i2EpxAHVij6H zuXJl`+SriS(A%-dcDt)ih*d&Ga!k|ZkXYzi<`Y-t{4aWZrPd(p>thtVZ(WSK)x5_e zxvOOZd4uBmApK#%Wj=6FKd*l}W-Hgzl%@);``@^~&Q$#>9na9cl9hg`8t$TG5MpL( zdegJm=HIQ7pFDNGtFyQ3&e5ceF5G*}50~HHJiKaBeo?rS0i0P82{dnjBa#5?7`{{Q zO3D^weMp~6cj0E!lOi}qkcyk-$J_@?#ZT0fSl5sxQ>s(UPdv@!Bmuis>Omc2A6+*~-Y zBU=a8!kcleODZKI;A~m^tEtr;Db<#b*O|V(=}YmZh*-(-LC|G+|6dlwqg^95hSPJY z6EgOePz~4;6nV1KSuJholK%@WrZj|(NP81IP+w>^p{k~nXcpiY-^qa35i6RI!NFy%ha$(dr7H9@!p)a2v<-oIqWXHhL_Ai|2-l$zLR&)1-$gQ$|CBAMxqwTrHm zt!lm+OHb2s$jG=~k?izT=3!NI!I~kZBV9+KYUylO&rp9H$h;sgRB>(Cv`LfqFd6BT6PkbqPBHwxuha8VarPQ@5f9s5bL&N-Y*%ybYn%zd4_T|(=A8y zbqGP(DHJW+r7SWv&_Vvvi!aek@S`ZhShz?2Lh&AL7`U3N2(yA2-M!zr<#1-4~k-9H>zflxou(Hhfdb}1#6l-|;i!5;EfD2;P~#rnP1Ovf2NTS3$rtWHuQD?PW`!{Qvk^xw84{O8b4Di9^pr;-^CxCWU(Exm9cY6WG zc^NSUOf$WrpRRn;nwN|DUR<0Wf)ABWX{Ni2pF0wrWN%A{;> zd|_Uw{{euIm!s4howr10x+AHq7Imrp1<$%w;rD`9k}R6fx{|%OXyhQm*rd!1_NZ-)tCkS3o5`9$6dpF#m7LO?a5RzE7Yv`mq7G)`m<3SU!SU}$n$c|a_H7{xsGmm z{4_<|!}OKH5(GSGrl+JKw%XN{acimY>Xx2#vu+x(waV2pi%6ndaIFaL3Ob!TyCgskL8*-pu|%` zR^5-p^{6!}&+v0lrra6ExX8${Pth_`M{dSTq;2iIWsjJ~6|nQOdd)Huyiw-l+M^cG z_o}LZ+i9L!{ktYdmi6@6a!zpB)e*g1lo2%|v)HC6j8ho0AeNZbiCL9=g&{#$+(sWTPHXTEgoL{P@_j3*I3Z|&iXoCY)!Oq za@s0vW=mribC%EY08%I4s@=FEAP$0pUQK1gGD%boQT?P1D8&-9F2Sf-RUS17dRf*Z zxTpGf>?@i4uMtMau@+*JjA51)usAiI%&^Jrh`0-E!O58DPhy!sv#Z9KUsb_a#PF(_ zab6>nh-9p3FjLs{O5s`#+_PW@vyg#VQq&Cj>a}E2f{bpOT*{F#jx|hAOSFO@J|Zu8 zVYtMUCCozO>8D#Pb(9vw*v+9-sVmnHuI?B}2qc{2fM=MNwH`+TuoMXuIVH6C!}_7h zlyJ&j13xcA#Zp zI#cLTH}@)8Q{r2K_n>R@3)aEzaYX-R*OfXuR45YTc!Y1eKA_cf<-sSA>Vd>)T&z*Xo6^C>KCKooW?Rm{$7_@ zT?-sPr9wFhA$`=f1v-fBN5h6kRM+;8-4Gn9pIFU4{}o=d{HQgdL|*V~q-y_|-6(p= zWc3xZ&l5}DHT{MQNDMSzX1atJ0rN42E{&{rHkn@D>E#f)IP@c?uc6w7P6zc5VGqD( z7cHHq!Si0JkS?UhG&7OU!cXroqDramvSf%B);uuYiFv*rAxB~3S6jvqdTEcUFI;#=F* z;hp9Gbs~R_Nw!bMtCzl{Mn0tshg&eqsgJ;20^LRWJp~4M`?%MrRJO0UEY)NJ?(B+4 z%b6p9CzKA8n+G=~zTi9iL*P)Rdp<{7YA(2(ybYF7_5dhf;#XaB?#-db|y@2&$2< z;eI;#hZ5hGl^B$smMc6{7%;VUq@NCz9WTi;+})UVW0_1iDylYqWZ2JZjMtJVeJDGH z{QDHr_K_m?)laftkp|AOWVO(D{&skEa8#!&&baYVMI?p*c7Jo!2Op_rVMa6w2NU3X zD3}EfUW+sy4r)4Ix~Q z`-BMYogjXwW(mC>tl%o_`EH3Vu=8iZ2op{Gt(;qV$TuP;<)Y0#eZLN(J6b+TWgZmz z4Q(e`WxY}pyYTbt5ISh70W*T}trJ#<66u!l{DB?w-0b1dm{;NwE^LtUge=eJpb`}U zR-fjUQe=0$$CHQOGH9)#{F4cE(ZWRG_k|$Wbt7xaC~T7FT)j6z>MjcHZ7}5jW;xw+ zu_$rJSVRtqO^9J%&VJcH!QNz<&+qTt)h_Um-JE9AySZ@FbX^h9z4C}%BHTLf(8h+Q6=ced;A5sJ>bOo43dgw}?oY-BNU>;N90#Ylg#{r(I+aG|Dg z);)Fb9n|zxQVSrb71;9yB?~*ki?lBgI%}-@8QEij^81Dd$-czE#vHd7(A~ZPM{}aO zTO{n_tiuB=FEj2uJm&h3r|X?+fO7D~IWL%+CWvTf z@=V&ZX;I~X(1L3`I&6`lKg!6njobJp*fid>b$Q&f>aD$Op>4ej48Ejtw->C&W~IJ~ z{%X{!!97!9;ZySNpc8xwg+4w%Uf6HE1Xt!?xZ{ezv2B!c#W3P;@8$Oj7^R8p7p9cL z;gX|W8)M1Eu~H{K;S3ghQc}#CjNqz-n!J@+rJj+WvX3W%^VS3im$ph>vfYIq&kf`z z^o@hV#-0xVt{iK3i}F45%id|`*9{&Cb<|s-Im?y{J{@!i+A>;lSbmRvT^N_DusKt@ z>xF}JJhpPw zE8fEF5WUwqn;*YTDUycsjpVP`a&* z^ks?_*h7-CX6yOC)6-kS?|EQVlBvXRnIgXG29G|8QEUFjIH7zCcFfCY+OJeB87nIl z8&T$QcxemGMPKK<%A^_AWG2v)+`kN-erp4 zSQ2mNm0|Bo4d~?d7}|Nd)Zs}LHN<7mUIPg9nI$m8Sc&-DJk(^9U8g#w#r+iMW}5W& z7y9-}fH|>qDLo4cx>!imu{kXBy;yUD-`7RQAAV!cIaQFg2p&!lbC^w?JbVR;w$nOu zfS#+JwhwU!?>#6zPhQit3jIoi650LYkf=hHQ{UREhRgO z=S<(NY)lfM#4n>&?$}{CGwkNuB(#@pDAyN;F25+(68F|mMkKK@)4yoGidL^YeFEI~ zd%wwn3ZtAUpK*=M;nny=@UuHyuj%~6AK2dIvCOo*P?_tDK=TB(#4;~Qp+KKhq!w7x z!IxN77No{QEONh~yKZ};rekRJ*5sGSWy@uPubM{M1FlZz?wVj1XR`yNwvPiM?6pnRYO&8u3~YAKiUw%&n)11&XY1CWd&Mz@sfy zgfi1oM1vZOiRJk{xROul2%IsC-}dEduR(h`^(yIOo898`v15<1LY;#Zsb+;t0K`Mk zzgT9^ATHp|#ipEA95e8E?rc-Uu)UWTv;poEwWKVJ!?sP%5cd!axStcKpEl7}9jHDe zq?h^t#Ty%QSXw7wK^(Q~2$c&qL%AC@F6Ixd)|K&C9_LGMr}B1oi4dgOj67ekR9^n6 zy-Y~(Xl15~u|XSjpfm-3xQ!!iHh?h=>mu=aERx^FI@g2NflC6u@i9*?|CTOC?BOhn zMZkH}W#9dY3ylHB@{MnRi^g?&-)IKf8E(TX_wc$Mncw zC0WC7Q({2E?NZ>!b}mojkFei!vr}fZ^}M`&a1;e>aTIuM*d zTNKFYn;YSRLD}GPN5x)g$AI=TEOM9HDW_2~${Um3^86hEb*y3tNhT13D^4Oiqy+8T z?d6?Wu$oE)`|`uN`%T&RsZie;a2V*SdV1naSKXiin`0E>7NIaUt2rlBkg|OcsB++_ zD-eO_uEC?3d5$C5NdYW4=+LqnOF3RKPxIEYHf3zVF5W8Vev0P<^{i1@HS4 zcvn~+yDSi|{E4^?+^62g#pj)W*I}M>k?L#Ty>Ci2 zJxrj4(RB>>2UJ~!M&HG*Rbh^a?SX{TNvrsd(E{D`^&5w(r$jpijIjq=sHXkrtE8n{ zB7TZiv6)`OuMT3}pgCf0sd8V0BmxyAH~Fr0hX|UF)Yb78Cq+BKIOqX=k<+E~jyHtw z1>d-*IQiJwJ!m^Yzi{mz>RZ?)n-|ole4S@l&Iq!O*l^|)SBu$MzS&QxIA+$I z(+q~3T&=hvU7pS^q|NE9A@-}dqmqAKkh6?xa{S%hx+f?=efcUX+0C!sKb93Jo107~ zR}kYgTzeqqI+_ZJacM7^(*uM`*Eb57YuRkAR3R-dtYSSMCnMc6>pix3rWm@CiC(&{ zF(vztvQtFS5lDNOpA$TKPXr}3$B8&`kEbL#(YSnM^LHqxtd7nv4Tnb2J#1K7Z`*MTy; zht`+!J&D`^*cjabsK$c2vl(0LM~JNGQJd|228o^RQakB69UH+f=+(1>@y2hUl5Q4s zhr2=f^6Y7+BWty2-`P{?N4A;*@t7Z3$j(%wmhjH51gE70|5f<8v6Ypx3@G?`wku)T zYWwB5{b6J0*Opx|$5Hdu<(+h^-Y;u=a>k~TBflor2+qq}sD_L9k|zVz6!(_U?De9m zfc1gpCSJ`?;rGxJ)jhYH+{m5$7fC|}9?lxiPu0q?!E16?*r-qEe0*`}$E43J7{9g? zoS*JB&RZt)OOc9m@sjn zNF^^^zwyJb&&h&DH&-L_*D9vnxf#dp?zz?*S?9Tq8d|0IIP5jAb~avyd(P{?y45X) z&sIH_?k&IZ`yTf@+TCR^dh&C+NN;+&t5@ZW%Wxz&RfP3b2C!Es9pX%jd!tLM`%lDc zI@wif!-a-B>^5AFg1U*4R=-Y*H_PmXiHrH8>0wOkQ4NkcK=6LkeX9fajg%No`Lji% zYI_god8>E^EFRM7&D_KnC4Fiw8Ghe52q5v>CdCmO{>lC7rnJD9Bu<=!^LKd%`W zk6Bji=#QUPoq z>-`69uTiUUkY+sNaXx#HLtjDBiDj4B%sD_O_E3XdnEcZC4fb4m^k7uutrim@E2>$D_xYqE%SwR>2^+yFZVAmdnBe?1Xkmyp?S zi%?ot%)@Xr^y@Z$vTMX;lxxY*r34{$unc4DcxYy2_uqf~YqNd)9)#d##6|^pCaHB1 zcG}_AytMAo9|2?nUf}JPqrUCMC-o>~1)`$9I#~w!A7(gZy-W_@_)z~;Sm_MvIqrx}FX_iK{vpM4EE6BejT4I&0qWOZv?`Xpt z>7(f{74+he)VZaZP(8emZ)CV@$~^vTL4w^}a%MJ(3DY7ktCoC8m04vc*l^z9p;WgQ z+q{{0hVeC{b&7V`+~7Wl^lg;W@<)R^3nH{!+qYq@?CrqI@yT+3Fgp;}dcLGf4}Frm zNBr(*fMaXKLPY*rD6_NFXL*2@fLW7@I52gf#7xf&dg5(}z^5ItCG=oH3rlj&Y^I68dddae>1l*X2wk&1ZM zi42&oOf9qXKdyw~E*C8VQ-;3mtI?;xv8hILQnG9o)to%+{(8i=H#FfaJ>jlPM{Uaq ztH3ykRh6Ms?5=MNoHo64cW9qcXWoS`OINTu0htXQ^%w@IJ+t^Miu1eHHhE+j9^URp zHH#UnJsmQ&oXu@cH3Fak-bY_jjVNC)1O#iB2^*yNDvk@A(2(v0c`e_1AnE$1{V5Oj zwVEb7t#f1caIWAvr<2aJ3^dGJP z-RI0D7*c8SALZ+V29#Sv6|$g zrTx)qFDSb!Bl`|kj*i{RLvJ}l38a9{wv2a|_AUh!yxk_c|72-)I&;!K~~Efyd9(>wU6-C`>he>4#znM zzgy{$dm%PYgVxKjanQry8vjEp#}(YQew--LR2 z9vso}_g87we2wT@Z|YF3q`*3nGuYg#6O>N7j3U@J9+!Xwz#Md1`Gbi(1RJw zGIwc^lDxMn4*n!u%BHUzdvIdYn?>!hQIW~YRT}V!&A3`}d}r$7uU&I9C?C^ev+JPX zPVsnM=AN24>}+xU9CqkmZ(0fi6H(ApyxT5W<7G|oC5W$OFWPsYv=Q5rD&uK8xP}W} z3?V&PAU&3SF+e%bvfNApno~nt-RZiL^JT??|Bt=z{%dks9##>t0U{ush^VLth)6FH zIf8|vBE1AD(tED~QIIYoMOqM~214(Ufb`x8gc2b176>&VByYUmd+zag?&tIV1NXNi z^E|V=GqY26XC^A1u&|ohA)l8+yG;-Y7ahSK#g}sBxdS7yi&S3L(gZhQS5GU~p2}y| zD&@~;&tQ!1yWw-}dQn-1`}$p~5r*Za<+)HNgQT4#H^NFbq?J}d2$kb$vHD0YK~8|% zV9PJ&`tTDj36@3!m7BcucCFbgkCypV5-x{zLhSU4(}^WD_cf>G0U}mDCtr(&X7s(vUY!V7SjS&V9NNp#FRZUCJN=kl_en~THe!rf&j zzg_lh?HoYMyqJ`L@h)emdVynquUvbL+t_?zV(}}}A=ev`>H3`kbRaxWSFnDW2rp{6 z<$Z+heY0{<$obQmyj4{v`y#fM*z>D-d^3Ga25fwIrvwR?zZlUv1s6oqV zOSrL8+vJzRIh>D%U5|PJ^E#(2FZv-jO}ZMi<%qGRK+D z<**kq8(Os=Z*8*5jp^e19@iX&aMYQ^HJCWgbs2IG-P{DUslxeS{NRe6edZWU5zcO} z-IX8HYb_Lcq3|n!H`3QRnH1i!FkU1T7aMgXOIXf`aKC(*fSzj!Gx5Y9;@fd|Q3q-D zbG2Gv5pFJLBN?;B_f9DojC8A zpmCEZzAS9Dxu+J`R$s$UK{0^jD= z7rt$HIJngE9hayyIeSaB-BV7MyAZK>&RO31kDWV((K3Dg)~O*3#l|00WZq;+7E1Vp zbjf56NtD4JTtSn)OaB$!s`Z^JBrmQ}NjTUK*3-%zpuu31K)4ouw@5CeQ+BnPQO%|< zX!JN*n|aQe?66 zM@ZbawbG_%uF7^g%6KSe!Eq*r&c^5NX$bWV^f5j04na&clGmIY;H`EC`{RpqCnNjU z^k4~$dSl#1I8`knb1_+BH~oZ8b-O~{;oub$Q-M1R53}WB74TnZDw-{~=Yna0BXb`j z9cOuggolMJHz|295wy~of^l-UI#t3^!(-A0;E3o%M=P7v=TZylfE0QuqFEr&7Ix0} z6)S!916WWAF`IMlVN&{zyjpT0c#RMomSAQ`xkCoJo}wxKlwW<_u={b3h-r`PNN5ZY z-Wi}m44E=Ol0QQXFOx|HwMSU>*RZ!zJXt>WG4*{K(j}2B z8CHlOc!D?`q=dgWmMq?khOZXk6fh$ZdkAEAJ*JJkA5gX4JEA9(%8otE28MP}qGB+% zZjXehA&yso2?vmtBs4yFL9IwsJSNN@ezg}rN)@5qss=&q@qronHY;z&!8klx=eGW+ zAtehTGdjNOIFt0%dR(nmw&I1PjBlsFfNrB7v1Ap)dqd95c3a=m4t6MhQ4ftQWY=a; z7n&p;Ixu-n(K(d87DO(GH{OJAF__HP9Fa$~KvQFBO)S!V5^%7pp3MOLK7owy1Cy|^ zf_^XQuE^3b8Y+!$7Ymi&!0@G*j&y41v*U9XlGgGJZLQA3?`)WkTdyrLej9xa?IyW8 zjf#TIzIZRhDG#U6;ngupDP}EJfr8%q(W>yWs6)~L0{kYIBSxMj^+U&dBd4M_vnE%3 z%`o-Ec=whP@6CuEUX-U+6%CB+DJWWcE#F}dk zHSh~E2)|vpc8v{#p|ES`u0$G9cIxYb$zFz1ctk9}*5zs3CnT-XlBz0xnOMBt#mY)W z;?#YJTOjVfd+D~iI(MDs@rB3=A0|F&xzA=@+hYq&tQudEglE+RKrLO0Xr{TA69A$s zX$*Qi(=_?Qumvh){E5wdkAUIl*sh6dUY3d0*c$LmYf=(!pUltcc-|PK9ByYCuHi<= zy3{1!coT#=qzsHFh%cqH5(wgev_$?yAx}e*%7m~)lXi!Ud)Wg+arCykCFVL6oKXgH zHG-inkm8yD3@@oobvRRqFHFW|v}Pym0X^=&|>oJ~Nh6^g+yb~8(Udz_#3rc^qGAGUBF@aP$$&{jkJ$^LvBQlG zJe~)z$awIYd6y#dJ0LxjO+S>w8a4P-Q;);Nkm)%eU?nsc9R!;S0EnHJeEnTL0-lne zfU3bi%l$u*FL4>xxv8_$F_dps2;EkIthqU5_tZFT(Gl=o4)@tSs#az@E?nidQ9kci zPM+_GF(DpqM+9@ngt>PiVFnXq4(fsEuyc`!olFud8{k12y)+HW<+@irZ}_v^dv#;RMZlqgdPX2l`vn+rfm9jGE9GR+pr zU%$>3TT?12hxW2T!q>z}QffBrm4v3z6#}EeOLDWL9Fwu+moWIP-2ger1>1R_16N;xW7$Z}A@a0!eNkp!`fxl6P!JaJVkC)hIa& zhx{Y7-bHV7g$g7;c%0Vwb?FH{<bkOOzGM~PHzaY#G281``N!TUCV$$Mi+fnhyYg2vXMQad(WDVyYE`4&qEgRWiXl96LF3Koo++c7!n5KTT>UH8cge)8;0a~VADQfE>3)TgtiiO_e{s| zhN)pydyu}nQFe|Ue2i_YCC;_%_MpZ>Q1KMFXUbIC8Y*{0chdwZf2Cu?%C^BQTfhL8 zsTF}R9a+Y;9)HAjorUN8REqcS-@aAH`n{(|MO@{LSQsUNBr98X#@!+$6xavbLJ9 zuxJ4*8`UMO^Ky}NvZgja0hVq+RtmkR)(l^wBEou`7r#9$p!vg(;a%#}#-MNojz@FM zO)4?xGK0fxsz*;Z1#@!R;9}L1tc@Sgt**ZRgHKv4LQPUsbC+{BEP~av^_iYYYj6eS zuAFkmDz?JP;mEnH{c!#U&4)zNKu44qb;@qpc1!R(dOTAYT@tQK$;&1@>XGVL#!z~% z_#K>a25`kvz33Ysi)gjW=KQ1VN6K>j{AZ3|6e67ZS(c#t6^7u5C?8HH7@G-ZhSSo7 z@dAonM6OKIo|052+(47$GC%4uutNA0_^81877)c0K`%=U>NxzQRy#)j z8RmSmX+cVhF|d9lynH)fXOWA>v8^b ztvQYv*)#lX0Nm{>(qaqQ1u~sUI>L6l!-ixysisdvDM727b}?zU?DkkcMybH{v7U9t ztTt(=PMwl;w!FosT3y@`oCv=ank}?ghJus5k43<@5`z%BiVb*-Rw(;a7a%NDYAIqZ!42YO40IncIU zB!aXjWPVv4_u(zWvatwH(STyFsH0bOpmL+uh2X@HKa#Ipkt?fqgwnetI&>=OiMy}Q zKURv6JBp^M5A_*-%gJ6#$S2G^H{5&f=QFUNIz z?dO4_&l^=?^ILh*#g?S5U|p#~a3Oe;A5NyECObdw(~@;h;P8rb0uIRj(I*Nd9}~@0 zx02teUR}dKN-}3~4)tsnQQA54m9^*gzo*nA-t&CqkTD8!9?nix?zF5E4A}0r-3y=t z(k&>oDt)M1&KWQV;?lIDqfk;H;;{J$4G5P9!ePQxY2?OkkGpGmAG;!jKMQ|@xPYG_K^e`mW#$cI*#LG9WX zg5{2`E>R(}_5JV*OZR5AWt1f-9>aU(7w2Pir0^EoqMQ|nMhXy?_jAQ7S0Qs9&OH&JI?| z{%8d;S>=uo`F>j(cj46*7puWUNpqXznARK#12t6hET5opws3kp!ll`gGyDQ&H=0Id ze!EMa<#k&XYJ=Ka`7o;qmirzj?Yx|9rX-WQZVH<}2+sWqq}=TtNGwK-ij<6IjHZn> zMpM-jGkH~aF2R>?A6~OQ7NcZJ`xJfC0!teDs$17Xh01^C7Jt1U(^a*^BNCkF996M+ zPt?zlY)GCA!>w_eKJ&9}_L<$Hq*B%x3plC;oh#q$aOgU~uCnhQgOxEc&G+7MiP9Wg z3zbMW;qGkJZ>I5UulG16zFmUy zR^C{ui3u;LC<@@R>VRuVR*qj)|K4S5x;w(V7VSC{EHie-fRwkOMqIb|UDG2s&KsA! zz!_&2bbNmr$8y{4ywrHdV`anj#P^4*JfYpq-wTp*TIB`pn3z_!-h`J`7r#>PkAXZ2 zx4U=ugsGKJbjVV_t4WSe)Cz<@ zGbXh{Ihz-XyP}epL$o+e9%a2vCh)u?WC1(oweH^cA}A{H06jl`Q)`*cc&F+%*L(tC zQ#Si#dv7UqWb$6)MZsZ*z<3z{edG)3$g&oYADm=J?!Hj)W-$UuxqDt#12-K<>o^~U zhYCp?C+ROS+c9yq8GXsYrEZ={fg;M9u^8IpW` zq>f&W6+ck0W1V--5_4X@Ielw#i&10fP)6`Fwzs7He^G)wPt$s_nSs{SS zh==h!RCmW#Vic%whbHNPZ77YHP3D>@Qc*{0u0%gEBE9}P?f4CG&Wc&!UODZz9!5^9 zt6aHHV9-)J9b4OE>X)|bpPeHnH;qKi^z3+{p!w`c=RR5Uc+@{j+l}}xDXRb0dl zY^L#VILgIv7iqu#xCsHAb=cA>>V%j`XXM~NN@GK<8DZ}2<7MX6un3sfCmI9T4Hnn1 zhzdexR*pb5tXFEj;)9A^_nXkIiJY2_s-kyw(^~cUvll>`D^-jjQiD>{0d$c< z8C=|z`Vy$k@_QlUy@08h6W-GMo_D-hIJipIKbNz7hsAX8`o<017WaCV0<&amC3{nh z=hM8=bC%14T5IdUD#5!}OA@AnA6-5#Bne&h{BKibIMo#|6!kdlN=u1Z7d!7AfLYYa zP<9(7PJ;60PpoYhyKS5+7v@tvlHXv=R2Kl*Hg9)Hm6w9!O1X}UzRXUhEiv{5b`kEH z15C$PDej$}`|>-DH7k&GeE}%3XozO+=mB~)TLEzo%I`%^sA7)=S%gg3<+u3$LDdqo4G%3<$wy(`sv-svYDgV15mEP zr9N6EsY=CO{MM_sje`<~#}_7Gi$4~^w0f!)P>Dt^L6fi)+i~;Yi-Re@F?qe>blKO; zUZ=hHMV5s;h`c_#{!x;e{SPYz0om-UUlY?S=7a7Z^VK?y*jB}c6-m_&<Sm5fyt5 zHvJPf`uJWh#cXKk*sl6~Qlb?5W>Vt%am&n_liBr_Ls=x1+AHU&x}6NXvs2zn|5hPB zwsJZzX>C$*RM=^rQgZGczFs?|W;$`^MJ2LJ%C@9K&^IO774-rG;=||(j8|Gs!1gIu z>TEMFo&{Y8w`fwTU8`X?)l#@qrXOp8r1M9wrN!l|NQ%-tcG@4h%A8&Cj{@TyyRx0E z_dc z&!niby(k}E*|N-zFR;=|s=4zVnR^pU$Dc?X{#?$Gtyt-ZX(hQ2L@z+Sf@w#&eyiJG zk2_r^tR-tToYUG`Tk~{jo|`XeSJhi?T*AQgU)rFke8PuO&m200Tp3o=+HK5N_d9YtyT-e_@~vHr2-QeotxzH6j9ER`O`^*dw5 zPVa73iY*S?^p2aqa4y0wuUSuR%F>Cgr0n~YtfV-g?r40iAa3QN1{Qie_xrqvLFHwJ z#f|Dq0rZ>ovV#(b1GW0?onQg`GN`SOyY>J&YOO-yjO>?dz>y8()%6XRW!Fz?POy;eo-SH z!;~R4u}bL*Qr>L0cBcNuQb>F|HdnJ;$ysn+;dO z>TMSsTd0cMuykF~iCe?E7j;<)w@2v!U!))Wy+?${k4ydVz~i&`vFX;}Q>W*r%I51+ z=WEo*HItlNL*vy9R3^pZH>T3;o!f3W+~T_nNcnEHm$ac1mr}zXW7{NXovy_Pv=z8$ z^$!!g%p6d6kTa3gU$`$=&#pj^(>!qVq)%9wEH_4tmkLoj77(}wr@wNzq!^vK>QS2;rJLW{|iv!Q`sG-d=zAq}m z(#%5?<>dNm!uh^r+l$%uLS6eli68;Zuz>fU%bx-&RFm6(?=*7fG)@UM9{8e+FSly5 zw`W!E#YSUq?y8cvcbCKb$Db(^rmJT(WIge8nVR{yJnp+IXV-{g4e}hiF(_vggm>XiTbg+eXAK)BWR87nPOh5 zJ;r>l@$94fzwu@6lE=FZNgVg<5-Y)){fG&vldE&iB-@@^4SgZ8<<;+-*Ne)B_+b-L zON`&GrfhpR_WLe8x?euOvDt9$H(RQlMio#;Za?~T*ZnM-S!KjG6~)fKF?)9dS3YF# zj;^0f6RA?AYSS!7u2dXdcYphC$^(a0w%MHc``U~W&Ux6EF!$FSo2LlUXdV=eS4=q- ziZ3g$8FBg7#K3kqp7|a9zB&BQ$q1h-n^4j`NZug2-6}VD69{93oq7bKdFq!}3Q9rX zu$M)FZ7^$?`)ULiP}+=e>0+NzL-l1NY(oHZX}e+Ooj8`bd4(0YlNt^YTiveziEs0OKme^B24oQpyO$F7~lIP$;X^!kGW z`HL@4^_LX>7o58vDKObnYi0k9*#BeT{}}kYT=f5M2mWsd{;eV1|5WJz zROtU~;BP$(DNc;GUDHa`_g z;7|klC=Me|7%-5JipfHP;9JJTI66%sLoT7i32-QqQ!>{!p8goUgaHYHueL_;3tEph zpAZC^^)s&G@p${$oELvxYy6KdBQnzqgGFtym+|!fu-e! zmI2$&iR@R0T<$-hm;*3bW4o>?`}u@!r7T30x}$2$ee~OQnPgvxNtERE)h8AAy1!oN z6a6p%5AKOHms=X&4(By@yFMzW*88-T%8(uFv6H+gGuB&N`Q5GQG&jv@+|;6@gOnWg zFWyK%$|Iwdp6yL72)EW^j~QD#<UlUTJp8TGhZjvpq6b=9+Hil1g(($^>6 z2}I1+0a~o(oSYs@_v<_C9L#k>HWMVaB{2MnZ0ee+PoF)yQ?BxN^t(blicUI-m>^-Jr@_^gxcBRUp3P+Vs_H>|{Ci$MjLXy=}RqeKTk;_7I;~ zqpUKuEX=4EUQlpl=wU|PW6|q~%ffDDKPt$avAGCAEH{|wu4;#n#Jby@e_nW8>9H_b zI=9Ho-1Mo>+%S0uhHRxlh#+prQ>H5{9yfgnaI~Y<+5z@zSG z?fK2E!>cp21Pn%saQCm^#VDIW+wmxK+kT`H-c?(WT~DIjE>VxPhO5_@xqu0+7;CvX zcv&6P&n#&heFH_SbbBC$~$jz)yw%wJ9dpK9k1NZVIOs%ryo7=7-!1Km8WMhL*vq z3{d)nMCFIzfwf|AM}y`tdYGSF?e5W*tK*v^h1xp~O zd$edmT($UCe8lcXtiiVeJSN7G?b@r4i)|FFG@HlGeRz7kcV6n1am-DVkFbYbK(c8U zA7`B3MvCJ&2bif)qUzW!T~sYIrlMzq!+WAkETO^fk^nMj7JVJwQj5EY0=LgG<&7Ska_MBx=i2;4Fn#I$F(^f;`t3`Z&k+mH zX-fACj(j%-R0|?3`@#^uJL{TLb-~hh{aT9C%auWHR&_g@K}JY zkcb7iOn$16aG$E;?UktZSt3+*ZfCO`98}9LRqZ)wHme}ZNp$$}KCoh!`<|W&VeWRH zmnJmN#M!@!()z@9qd2@^0kguO)b?fw1%6O(usYr+ojdXX8|>&F47uDb zU0LT0GSfw|OiuIM@)Fq}4X8CcRV(^DQ`6Sa(lBc#ZY?Gx>pFBg|M)z zJDOaxLfh7Fgm&r8y+zEwZ&fVE&X{1##+@jo?U?lZv6HwT3j^dj&n>>?dmAd44n} z6w!Ld9e{)CUG;l3?OP3zF|Id@_%Cmr@ztuk!~IJ$}K5wZL$$YcivRfn!_gwr>$G z(VQYnjZFJlZ33k`HSOz%=y{YnrQYcpN>%-VBguykV4$x;d~PUa#^D zs$vw+TSN=oK`rz)QvG`FGe&(@8Mr9wI31M=KpHr`Ar1jkweZ@mY%}G6Fp8dSKb+)9 zOxj|uBwy7<13~OdJ_L*-2dI);T5MAm2o{%}OC*!*?g}JzjoO>g_k}>V7E6s)@fODo zadkEy4`lqHAS>jC0M7MgK7fjcyEvJPzp?(u~uI7jx? zI;Py4qIA{218%&&7_qprAU@j%C;#v;C+%2)j*N-(8_siICk`1SY3%FD%b_}b5q$1r z)_tU7U^vpYsdlfP)qAaB>VAMhygzUbzGlb~)%};2a|4|sdKQY;iNk$2zZNXr!!A}q z&C%{RqeZfWVLsqDhmvG~Bv) zj%jaEzv8jMBm_t7hM{tbuHU)w;asY={8gbFVO5ytTCEBxmm_~f}}2yO5;5g3zts3TbL@)A%WeVxa+NZY)91g zA+7}yJ8I51F(3E>d%adx%)HUMy7ApG$TIvMu>Y81GLG&6)850BUlky7?d%Z~epEsB%BkdYhU9w#silYRCKq zP02^8Rdecv9}5hV1qpu1#B_H$p99a8$J3pdOCu+;+tHRX;C z=Map2(si3VQ{v5%WKa18>E#7vNi6m`fSjk*`cnNU{m9uyw%!VAznHsi?u_OD)QIUU zrm$r99Q!4c-0h>x0m1^BvM$a(+q{r)j<}nEW}WOdET_6VH#1^zJqaHwanuvv%(fE0 zZ9pPbwsklQMM&Dd`Z$~5+J%Xq*;GMNdYoR+yHDRd8r7E-lSkbn} z))B8!P}>`SyuZE2QDNEE4DD(w>wD_m{G4=F$e-|f%?pPPc70~5wGNy}qPu>hAkk=_m%GhUWV z432e2^V7vHijWVf99Jnm)QMQn?Ft~`>c1S~q6t*8Iod6ZUB!DheM>k9$dbvP_0 z9|>VEMb+Jqa^26}hJmYZ*&PmBvttvd-PISwKlk}*$ds$xy%_013cG`?Fa%TrhlwTo z%fgu8a7&aS!H=+TcbnxWKMGGD}7!u+mf2*t7Mv>G_g&2n1t;_Tg$sClb`2swgJT46W@x-Oz%EAK@womXSNg&C4V-5%!7OylF9wuoyROY z>${t!>FpHb%x?yF*qAMI&9bKWhXd-L&G4IgSsC(7$aZVKChh9blQ4|SxaZ< zShiHUwhJusi5cNs+1)MDC(|ba86Q3F+3B`WUU65?o_NI3jwcf zGL3WGHc%Zgf({Q2%@_7!OkiH-G6=DUo!IFJjoZJ@=s*< z9xi$R=ehq(dgdf$Y}uLXMd$vB?98$c{qKuAK`ryp|6~nK@@cVve-NeNL<*Kd&fiAy zr#XA?;emfl!_D6zC#Wx8|NBG}Rnz90UotWIQ90w;$}DChMzuhJFoT+gVH7k-QDes9 z^AHh8M(vopeS>$dENQj3%7$Tf$0kkaeI@^3rU zC{sbnZ~*^sjX27EBV4RkA;-UZ9&t#~6JH>57g+1wSfSabGJO@1oBHVwOBs#)r`J_) z(|`PHc{78TB*K2IBY4(~bRv30UCt}cZ%rhh-084CFd3o&V1lix>96q}L6%**u#{Gq z5xvBbl4?F}dXwYMD2Ha*jUlz7sim9YY|@u*%GQ6|Sy||;n_EUi!+F}GjUSFRG4`vU z+QmHfF5YfZGji-&)3phf#sUJ83UmF~8e@Z>GgY}zOEe!3%DUS~{v`eb9)k$!&!T(sd+1@U!7-dg}Dl4u$MK<+gf-88+TW2P9I3s-sYTtbv z9#RZ)i7}RNr?DCyws4`2JP;pQ9@LwymA?4a2AxB*WHoim_i#V=$1C0E4zLQc|Hhn= z>-<9asU8*olU~l!88X~|F*@KT%OSjbK-|`XKOmb8{U!I);28ye+4;ECIbO8ngrx=g z!of5cs7U0w=sr29aHjBRreYc?SpiVkUldCAG^-Q0tHIcXdu~Nlfyc1@O>N~J`x}U& z%0D_OLH6=e<&s&LLBr{X`I2(AJ>;tNyB{P*ssi0hVV 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 P+;F~8aQWefDgou$cw)9 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 Sv34H#*}FUTgQoY7eD*)4=ZZ7{ literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/help/print-style-standard.png b/xhiveframework/public/images/help/print-style-standard.png new file mode 100644 index 0000000000000000000000000000000000000000..db1c37044e2a07e7e86f8a18e06c4b9722a22ee9 GIT binary patch literal 100605 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 RP2ex|lcR^){(j=C{{^=d#6$o9 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/leaflet/layers-2x.png b/xhiveframework/public/images/leaflet/layers-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..200c333dca9652ac4cba004d609e5af4eee168c1 GIT binary patch literal 1259 zcmVFhCYNy;#0irRPomHqW|G1C*;4?@4#E?jH>?v@U%cy?3dQAc-DchXVErpOh~ z-jbon+tNbnl6hoEb;)TVk+%hTDDi_G%i3*RZ&15!$Fjr^f;Ke&A@|?=`2&+{zr+3a z{D*=t(`AXyS%X7N z%a#RZw6vD^t_rnM`L4E>m=U&R!A-&}nZIi$BOPvkhrCuUe@BN~-lRD)f44;J%TwgE zcze8u!PQ_NR7?o(NylLXVTfDO zxs5=@|GsYEsNo4M#nT%N!UE(?dnS)t2+{ELYAFp*3=iF=|EQnTp`#vlSXuGVraYo? z+RCzXo6h3qA8{KG?S4nE(lM+;Eb4nT3XV;7gcAxUi5m)`k5tv}cPy()8ZR3TLW3I- zAS^}cq-IJvL7a4RgR!yk@~RT%$lA7{L5ES*hyx)M4(yxI$Ub(4f)K|^v1>zvwQY!_ zIrWw8q9GS^!Dp~}+?mbnB6jDF8mVlbQ!jFKDY;w=7;XO{9bq7>LXGK24WA`;rL)_Z z)&j}pbV(;6gY;VMhbxgvn`X;6x}VUEE-7 z%)7j-%t8S=ZL3yc)HbXDAqJZvBTPoiW_A-+a8m3_Z?v{DN7Tnr#O_VUMT0UBt$;p` zDh6JbGHN8JJ*JN%y2%msb97@_S>9!%Egwk;?PEkU9ntz&3uR}%Fj5d$JHQbQb3}a{ zSzFT^#n=VInPpcAS}CNxj?_ zVscANk5Cfz(51EI1pz};AWWb|kgbYNb4wCEGUn3+eMUMV?1-{=I4TlmLJMot@rd07 zZuo2hk1ccu{YmGkcYdWAVdk{Z4Nm?^cTD&}jGm+Q1SYIXMwmG*oO*83&#>l%nbR`G zhh=lZ%xIb7kU3#;TBbfECrnC9P=-XpL|TG2BoZdj61*XiFbW8?1Z_wp%#;>${SUIy V$8qr;L*)Pf002ovPDHLkV1hYLS~36t literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/leaflet/layers.png b/xhiveframework/public/images/leaflet/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..1a72e5784b2b456eac5d7670738db80697af3377 GIT binary patch literal 696 zcmV;p0!RIcP)*@&l2<6p=!C&s@#ZL+%BQvF&b?w6S%wp=I>1QHj7AP5C)IWy#b znXXB;g;j=$a-tW89K%FbDceHVq&unY*Wx3L#=EGWH=rjqnp|4c_Ulec!ql3#G-5ZF zVlbBA@XP=)C8U&+Lrc)S4O5%1$&{(;7R^K(CSnvSr$v;+B$8q&7Bf|h$#PARo1^%M zf1H^nG-EiXVXr07OH(*8R)xa|FD;lXUlg_-%)~ZGsL2cX0NXaAzN2q%jqLRR6ruVk8`Jb7n#{`T;o@`F= z#3YcynIR^s83UNF3D!f5m#Mg)NJ24&Qfrqb&_z=yF;=B)#9Iq7u-@^O!(mW{D;qvr zPc)gVb%aowtS8m@ElL4A9G>w#ffQ~q{i&_i)*6f^)Sz|C?C>zb4Uo?H<-&Hz@a?J; z$ml@zGygWofb9$ZBj6aLjpLhsT2AzjOu=-*u_gSCUP001cn1^@s6z>|W`000GnNklGNuHDcIX17Zdjl&3`L?0sTjIws<{((Dh&g-s0<@jYQyl?D*X^?%13;ml^gy> ziMrY_^1WI=(g@LMizu=zCoA>C`6|QEq1eV92k*7m>G65*&@&6)aC&e}G zI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc-P`S@q)rdTo84@QI@;0yXrG%9uhI>A zG5QHb6s4=<6xy{1 z@NMxEkryp{LS44%z$3lP^cX!9+2-;CTt3wM4(k*#C{aiIiLuB>jJj;KPhPzIC00bL zU3a#;aJld94lCW=`4&aAy8M7PY=HQ>O%$YEP4c4UY#CRxfgbE~(|uiI=YS8q;O9y6 zmIkXzR`}p7ti|PrM3a}WMnR=3NVnWdAAR>b9X@)DKL6=YsvmH%?I24wdq?Gh54_;# z$?_LvgjEdspdQlft#4CQ z`2Zyvy?*)N1Ftw|{_hakhG9WjS?Az@I@+IZ8JbWewR!XUK4&6346+d#~gsE0SY(LX8&JfY>Aj)RxGy96nwhs2rv zzW6pTnMpFkDSkT*a*6Dx|u@ds6ISVn0@^RmIsKZ5Y;bazbc;tTSq(kg(=481ODrPyNB6n z-$+U}(w$m6U6H$w17Bw+wDaFIe~GvNMYvnw31MpY0eQKT9l>SU``8k7w4)z!GZKMI z#_cEKq7k~i%nlK@6c-K?+R;B#5$?T#YpKD`t_4bAs^#E+@5QW$@OX3*`;(#{U^d-vY)&xEE>n5lYl&T?Amke9$Lam@{1K@O ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<* zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_ z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W zi$giRj1{ zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=% z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$ Ef<_J(9smFU literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/leaflet/lego.png b/xhiveframework/public/images/leaflet/lego.png new file mode 100644 index 0000000000000000000000000000000000000000..f173107335ab28024011d0c45bef7f7a98339306 GIT binary patch literal 226340 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

      bMH=; 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 UQO-*oc+)|Q_08}VdXD6O0C*xx%>V!Z literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/leaflet/marker-icon.png b/xhiveframework/public/images/leaflet/marker-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..950edf24677ded147df13b26f91baa2b0fa70513 GIT binary patch literal 1466 zcmV;r1x5OaP)P001cn1^@s6z>|W`000GnNklGNuHDcIX17Zdjl&3`L?0sTjIws<{((Dh&g-s0<@jYQyl?D*X^?%13;ml^gy> ziMrY_^1WI=(g@LMizu=zCoA>C`6|QEq1eV92k*7m>G65*&@&6)aC&e}G zI)pf-Za|N`DT&Cn1J|o`19mumxW~hiKiKyc-P`S@q)rdTo84@QI@;0yXrG%9uhI>A zG5QHb6s4=<6xy{1 z@NMxEkryp{LS44%z$3lP^cX!9+2-;CTt3wM4(k*#C{aiIiLuB>jJj;KPhPzIC00bL zU3a#;aJld94lCW=`4&aAy8M7PY=HQ>O%$YEP4c4UY#CRxfgbE~(|uiI=YS8q;O9y6 zmIkXzR`}p7ti|PrM3a}WMnR=3NVnWdAAR>b9X@)DKL6=YsvmH%?I24wdq?Gh54_;# z$?_LvgjEdspdQlft#4CQ z`2Zyvy?*)N1Ftw|{_hakhG9WjS?Az@I@+IZ8JbWewR!XUK4&6346+d#~gsE0SY(LX8&JfY>Aj)RxGy96nwhs2rv zzW6pTnMpFkDSkT*a*6Dx|u@ds6ISVn0@^RmIsKZ5Y;bazbc;tTSq(kg(=481ODrPyNB6n z-$+U}(w$m6U6H$w17Bw+wDaFIe~GvNMYvnw31MpY0eQKT9l>SU``8k7w4)z!GZKMI z#_cEKq7k~i%nlK@6c-K?+R;B#5$?T#YpKD`t_4bAs^#E+@5QW$@OX3*`;(#{U^d-vY)&xEE>n5lYl&T?Amke9$Lam@{1K@O ze*LXqlKQHiv=gx+V^Cbb2?z@ISBQ*3amF;9UJ3SBg(N|710TLamQmYZ&Qjn2LuO<* zCZlB4n%@pc&7NNnY1}x+NWpHlq`OJEo|`aYN9<`RBUB+79g;>dgb6YlfN#kGL?lO_ z!6~M^7sOnbsUkKk<@Ysie&`G>ruxH&Mgy&8;i=A zB9OO!xR{AyODw>DS-q5YM{0ExFEAzt zm>RdS+ssW(-8|?xr0(?$vBVB*%(xDLtq3Hf0I5yFm<_g=W2`QWAax{1rWVH=I!VrP zs(rTFX@W#t$hXNvbgX`gK&^w_YD;CQ!B@e0QbLIWaKAXQe2-kkloo;{iF#6}z!4=W zi$giRj1{ zt;2w`VSCF#WE&*ev7jpsC=6175@(~nTE2;7M-L((0bH@yG}-TB$R~WXd?tA$s3|%y zA`9$sA(>F%J3ioz<-LJl*^o1|w84l>HBR`>3l9c8$5Xr@xCiIQ7{x$fMCzOk_-M=% z+{a_Q#;42`#KfUte@$NT77uaTz?b-fBe)1s5XE$yA79fm?KqM^VgLXD07*qoM6N<$ Ef<_J(9smFU literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/leaflet/spritesheet-2x.png b/xhiveframework/public/images/leaflet/spritesheet-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c45231aff86b1344333414cdfa7a2a74d38d5304 GIT binary patch literal 3581 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 i{eP_FzxHA`84t@4c6%tZ%} + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xhiveframework/public/images/signature-placeholder.png b/xhiveframework/public/images/signature-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..e3ffdb80611e9f4edba7f34019d79d7f13576bda GIT binary patch literal 3124 zcmeAS@N?(olHy`uVBq!ia0y~yU|b2rIvi|3k;*_H0|o}}-<~dxAr*0NZ(PhwjudbW z)Lb@?L9OweyQ0SGGA7~4>g`Oj`7fUSe;&O?>&AlThvgZWSU3baxNH^|OLjAWI5sY) z1O*fv8XUqLmTX#WC;<}dm?8}nVQ5^_;E@`hYyi?CplJ>iVPuMAoOo*8u>_!qfnP(o;5&0Sej*K{u~T^ngrpImJ8bu+b13O=F|EasT)zyEHne)qn{bBW!a@7G^{egFCApU)4||5P)v)Hyu( z{rmTPpo;taHh-_%Xa>p0ZS|;?xUpaTLo<`o*UFlSzxI4Qe7=@B#b4kC_m7?V33@>w zHn4GXz}I0(Ufw<>P-CgF!Gm{#ts|(qGH0B)Yv<1{P$PzoNy*gV8_*Ff9Cuh;UOjuJ z4QfqEaCBT@xG4;31QiGiteic2J*e@M0Bll?8ax^l#HES@|95Ig>n82=3Y?-<$ueFq__Ur5?hP2)-u4|KCu;7$_AA6X=u1f7yc^CHuoW4sd}L+)D3Fv2!}JN_%Oy@uRZeOG|s0Q}!}8+#B(YEnMk? k?PVCBs3Y#)y*~j409Ham7!1h@H2?qr07*qoM6N<$f@a8}jsO4v literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/ui-states/404.png b/xhiveframework/public/images/ui-states/404.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbf7eeee0a3035756fd1e9bc6bb06276abfdfb4 GIT binary patch literal 96089 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

    • ?vT*$dgdj$@AN@R0$t+V^;`HXE7XIblX~D8emMR}; zXTK+2Z|9N6ak;z+=?j`@3<4tZuqmDm7`T1#nXms?|H)uR$G%=__4aG>Jpu%5?aKmm zK1!V50l-M^R)$zpcX|&QIUiLI0Ox^TzP9`g2s?u3onaUBy-sXe|HsfTTx_~1_i-C^ z;x*x|iWCE#cERp`3~HO-lJUa%2>^U@*B38A=p(%%OmXh~Q+`H3ssC`j7Tc11HQyrw z8V7_mv*x{p03aeBxwUw{BAu|{(6t&l4ibX(&CAt$Kr>FkD%(8DyMiML7DajFdhr|9D*Q`xRy~y|J4#wet|f(-tRVd0DSa(~Oif!IW8_4AOHjNS}?dD1Ejd@QzdP zZ`f*9`;UX?62hA3G3DhQsSU>yk4z{3$?X1WzA0xN#KXE%6ksBF4DnYfTqEZX-J+#! z^W-vWTM#5QNc9YQUS?rGH|CQmuU&d7*7-al^n`(qg7jM;Wvs1WABPhdArYgsbPO%_7N#c>>tuW=q5takyUM?; zT~(ONnxRX~&)JJ-LbpV7MmKhK9%ZW6rhUCF7l(iJcwn_!M%5^M|FHkaU?|92l?EP` zYz&`4lrsqQNsm*1BL&*;AOy)eTA;{EO#kfCy@0k_X9|fxx^@~`HBnN6umhh%mscqY zAbdR;`Qai}DZ3#*aDXpvXQ&+M#A3gXqG@rN{qFv8O#--O#EI#gQmfwH11Px@q3(|u z0PSEt7%CF`XQTH7{U^cZ+E_(l6~g=e3|-w~n`Bfd(Rg76C8$3^pc4vI2q)}X>v{&8 z;UX2{>qcre4_isJ%XyWsY*7)398QvFQ{+Kx;~_66g_lzRP^Rwr4dw>^de-)-DImI5 zNr~$gE*?AE|C@w{5MEo9%MBMj*N|&eB8|n-%;jhc{!>K z^wn`dzm_p4Ps0m#U(}~pa--zkBtXb35HG%PH^{w`F2Pc7;ZF8XKd_7r{;X%5t`(EH zWKW}`T|Yi4BS_@7q)DD&7Y$9~IMWzbA^0pN}@=*8@p z&L@F!8uq3_xA(sRqt1BCF=TkqKt>)NSIXO4BWzSJ4(wROyJ10x`zG1({L}JC6)Sw$jr+SJ3I*2!g*&2Smeseyj4==k7u#cwc?g?`5fB8oh{#AsDV6Ag| z)iOe7%tR#MF!u#;yZ3U-2)hZeO2{>15?qH1a{r6e`03^b3uV;Ou!H4k}fz)Du z2aYcQ;Q8I-GP;ihVN?p8QxD8{t~_2KwZUr;Fphsa5+&!Nwxb82PLTSyg8akXqfBl) zQ4jF9=-$gB+x~xv2-2~?K)6Y44s+fimSS3!BG8&sYGGLVhbKE(08 zn`^r1ihz?+I9+Ko+5GhhVU3J$c;=gv7P6uk4Qs)t^}&A$^=~l&80IU~svkZ5cwC=< z922&G9$&yu_gf};Y5$cNbYuS)@&SEt^bQ38g9HPE1p|i$fBx_Y67<0l6f}T>`V#FG z5j~$AgC06D2`L#fITHrsJ6j5V(3eQCpbwP5pulfAI`{RMWJDZy&m%6*jnp$05t@)<=B}&$GTdYN3jOY+W)d!;_ zDv@%-hPrcXKta1*Ho=R`wgg8|60q5 z+-)wai0S?u6fDNfwT}Za9CdmD`4Wu{tPZ`jTHseZbE}MXCIv1le$8fv&2)hZ~w~Q}g;wm0#{nL>kG#fZ_MT z*;)RtlTh>VvfVlXf!)asB={zU^SV)ku;oLqwZ&rz?or%?53X zuyeMhf*G0m>P7CCW=T`)d?*GV3>UiB?%iuU0+=$~UZl$Z2DUR}tOjSV+zlGZWQSRb za}>U0RNm|VJVRV4jIhe00$ZNDcTux(QzlN%J~30zb-Pf6iQ6_1Wyo}FItb5S&*2MD z!#BUdlE6dE7axE(V(zkd*?AS~0VBQPY$Uy^UHSS9Sd2>YSF!9fP*MD)rqX2qe}ohdbUiakIKWkNFRO!KGiq0RxICK>!Vex#jz1o5|FA``Jjk5 z`f&9_+q=6VG1+zQ4FJ2L^_%T|Pl0nIhPquOF}@q5F9yk%sF$6A*`?uiaR~bAO!T(U zBc}lYbC#yCh?;0C$9CB!V@_*+1e(7AX|Vf9gSi+HwL_FPaL25dhygHPxeEo&S2ljp z&CV(*x4~v0WUZKS5WvLnbydo@Zms=H&fY^Tjp?B9U}Bd`#9ZtpU_+f-hr}(ubB#I2 zxBc?2uX9Q}+1z`kn1j~Z^F7elM`}*Gii~y(a%3he)uzmi<+)UG9VPBKQkAZ@X z>76ijG8D&GvNEP56r6bGV&_mz%8}4x7SLXmPQe!^YMN*!j@q3hm^oK%_4t%;d*D4Be+PKsWw7(WlsjA=1phKwD4xa9slnkIQ2n);BSus-f zj{U^8RkmWg7(O5JFM27$@rF6PbGz6}5=hp=8B@!xo$`Wcb0amZnqQMxR#}SHI3Drc z_MS*k;YTm$Cw4FHTc?xj!a8xUk7H2dlG2d*TqeT0PP0Q)DQp(ktGwK$muZUUiGG}_ zr(DI%b|$J@J%ohzzPf;<>Vh5PH&AmxOI#jlm-t#mNluHv|^|lXBTt`h=cd&#y!;H#K;@TW+=ChTMDoeyyh2JXPNq0rucP z&zEG4P1monZ^BA>QCgjTT}X-0a9QVkp`<8fM2#rS)a#NRXQ6>U^;5O{BBQriOniS? z&?!J&Qd~fjx-$dk?k#OadOc4bA2*;a8wSzLnY_R)jQ|hDJuX{aRh?u$sAZ1XqkOFT zDvbRzfi%AWCTh-oq>I3y+=BZ?2DrG~{`EL_dN#qI^2<5JB zF9f%#he0}R7G7|youtY#V9?V-b?NsP>zM+ z`6iA8J?D+o3;zC3nEV7g!UYjN6wmJ$uJ z?20z!`SB<`#ivNHbEx4#^9*^yTO=v+zI{Wc+qy_C`{lD3u{nck9!-Oy-9-)7BATkN z_z@E2W6Y51Vu*5IUiVUw#`NKac=@no6@LpM@d}jQaZ?N)XCb<0o?{eB~<0Rq?0qX6;{E%iOj+Q3e~RO zT4!oNlrZ;j(y<8rLtJ$qq)LMVL7m#KXw;%jqVYfh#^~fCM$Q+rIrZ%Ny|DIuVPVUR z5p5}YBz?ISif!W}2Rx1J%oIEj7Zh|abLW0U?}eB810QipHwShI-y?b=*|~h2RqoQpjLHdmk-vfL-luM4dR1ic zbxK|ojr#)xUOrkFv(tl23SV^suiPjq!YP%FWD3%&;>Lt_vDaZqUV{GbI|XUZe?>`k z5?#NO&=cUtn`MdW{QBeE(vqYCKaBR*7ZhS}LTvI*JW<1m!5sM{kANZ+?qpd^p0c~a z3ds8Rf<^Z4HL8u!Y@CHWqOJGwD|#X(G&53+1w5Rf5oJt zdcxj6Gq*k*DL9L7L+K&7Mu)yCEmBnBDq8iJhPEu{}G* zHj=j5>IMq#Y7nQa&iJse_?-G~C@@h3E0jHqQRKmSLpr~?Z*S7v&hKsOp|fD?v2nZ_ z|7M*w9qs_`zLPDqOp3G8CpKdy6)I*Zncq|{9nkYFByA=5 zDnG*6H81GRyip{LnWhIDEmomaXrGJZ@U(J`UwWiv*!dx__a%?pVxLFk`*c-x3Fw~< zV+kUHsD`6%2HLv#wky<@emUDX_B6S^u0f$z@mkI_DaD=b8V<(vR`u)%w$v-)ry^*I zGSQU-b;`H}$+(U`jpEhNmh%H&nh6Cu{=ggN=Ovi;S9Y`?A**Wwm-!9g2TH-zePf9l zY-zMa>+_QRT1;((p<7WDqk6uv)_0+I=PlqPhQtWNmMOTM$YNbZkx^8)c4ZVF)o0?w z98l*-gU_YPGJ(oPT-PkLltPOhjmuTFSCT3rH8LU0)EtIyC%}{8+?>3eOJz)>$YMy| zQXAUF!o$)ZD^%G*$SjWB$AlV&EBPS6{Ry+TcB2l_P~T^~L|7 z@AtzWp(Ey^{|rrVDyEl{r1gtzYP{BQMv#s+7xM1noi^?o1v^VgbhBMFs|@>4`Ag$T zzFA>{Mng=omIQE{%7nI*87|3#rINM9dVC zEr<;qq{C+|F28E2;o$gY$&=kR*1xYJ{-z`fxPwH+s+1(gMvTht#(L1o^NAzz!BzM& zub3MhA^LJzWUEELd_8WaL44}fwi%f=OOoS+3T%p=L2p}-O0T5aWhm>c5NCvYV>+U4 z#%@8W5qXPWJUU(m?%+s3tNTz9;Y1B8VnsGxcI{PbT)S5wN;%-ME_2ybOuICUe0Owrzxt0RlNYTkz#FhVq;nf{)Puf5 zHq1^}D>k-O=J#tOoucQ}z>OwH%+12;Cz^m)664DTR@-W*fnSsf(106(JZH4o@BIzv>T25?OfVD`MB_4*Ck=q3XY=Qbm_}DK%BCK(9wWmc z@$^XVT-l~jBuDTUWHfsekS<2f_F=80&drmushiVS$Qt>2!!Q$c!NfFoHg>ZS3J;4` z3wdd`+Ow#&lPoxKd}kPwyBlnYQRO+kC=+?tT5W)!akAc2dv5QuFY+7ERK)ltkvG`9 zN>ltJt-ok47%6StfY|SsQB`zzPYTOUAHuRX1wpzkmDy~xnT_M#Kpl5LcTI?y!TVo) z^kz|hUYtORZG6vwtnn=A;`e@w8$Y|XW`{PKLLKa?Y`5u5WMf%z%}DqujqB5Q&3X)3 zZs4x8VNU73`jy09c%OOGUf(P@S~|zDu)Z(3rPmPSg7XR}bDtla337q?LBT05A=3eo zDlS~{Wo<$MIXe)NvfKBizUdgN(NFI*w>u}Ed|~Zxk=WHgZT#f%7QxcB4YV|6;Kv&= zOGz3X%*SgTZc11R;4QzacbYN0qLf8oOBfTHYOs{1O0?iM z^@8OotAuq`+gX0o4pT)I7Q9|w^(rWiE+;;+G0ZZ;NwzbYR|@nmJmt|$;&iDRf>(If zcX5Y&N_^+OH|D{c(eQp%EZR5F<>YuN&fKK&YvR}3IUTt?Q_8`<2l}4cYBwo*hOvL6kyaH7OBp#ry^KhNGsj6wbd&bfBR{bYZ73>LhzYhw{(CEJ6iQwbq$-yA? zJqjG?-$VwR$f7hut>;K!lAmGx8DVfBs37B6;xaphSvzWM!*hkfm}morK@-buz?zT%Y@Bc zfz9aPt=8Bs!sP8~{S@Nk|oK;G8j1`+ALku+AmpE4$B<&aYT<^C>Hy zCuD@*aqU_vSigKgCQgl)4iRq6WZ@GfDZnF)Rb7ooyP@vg0e)E*DIYq-Yq!A_L%B#7 z2^*G-(Ov}_dZ71DxeZIdsV+}3EUX-ieP_9PC0B8sY494;gW;!pNG4R}{oAZ}7ewxm zBC&{)B#}4T5*VI*SqA0k z*PW6i(&XPY5NRga=xNWxsjJ`ZUEG2}n&9G3{n7F2+tT2w(E1MTLaX!@O}-8_641A531;4(+CD;}W!;ew(YcOr6EX4e&A6J(?JGon|N*=@q={{(9sa(hg1I}b zI7|V5%w>IKORaZAOJ^W}=<|#~{@lVa+Mem;H70{y2>fqAHZ0Kr7Qs2>bzM}~aBx<(>w~{QZduaUXXxGPuJQpfRl8}1>_x{7JQ%BDr7q%iR zc6dif=2*Q6K4=4nYvb4Mf=ElbdP-m!z2qD&gW;k!Fj(4?e8Zi+JX&SoN;*D``l`B9 z+Ou=1Zb8_vbh}rWA|Jc%t-QQN0i77C&6Hy|ubdJ;#;3?&BA!~tV;s2yN=w%QxIJ3!5)XeDR5MF5#^A&v3!(a; z)EwxNSTC0R4({^=SClc%ChHD5Uit_$gSzZ}MrmVUgXALC3|F?$lsbL+6pJ5fuJ+EJ zVcGW@gT|brfzOW-ae?(wExy2BL+(5s#0cLZkmR_Ah^Z>>k&2xbC&AH|Ur)-sZwP{323}SqeV@(Sc(Vo4n zo-(4mz~#k}yC`0aw4niYPnq8}{8eXNK+p8-Mbf3$u*-(1Y4-4rA?RFu+(&%X;$DXN zzAxM5(gz~JCojZDbFbS9lfo2=YGun`q}o+Si&sAEgedd<048qM)#rI$O#Y zD(Cp=xV#F&r#~W;e=yAz|2$n*K~TIG*H>5AyE{h^8WO2@sDB{X&q<)Vo`*P=^1PYbHJ7yQFt4RA zuwt(FF=Jyv{r6{XuWozGh)_S`c>pNIAk*M>!v@YF*0TggQE z;cP<$m)9OtMZAg$BGbji+3T8 zbrMB{L!$E|F)%tog_B{6q3m0f#HHTNRxb{SjWbF6ydd{I>ZXMSw5^6;nOm!=^!mh{ zMRf8@>tz*x*euCAMOmy9Wak%Tx>EVtYTa|@ntd$cfgh&yKRxgcdp^Kv$)}E95f;O> zanGjQfZjz=Qx!J1mAe`Tn$G6NXAc#OH%N+!bU;(<`p1{`st#yJ7dN5ft>XXidh5go z`W{46tw}{rggeQeM*jtz9oo;8)LR?D88#OdLL^~>CMrR3-9;DphRK!CNfy(UArF}O zbS`Abkfdey2J5A_luNQLsUCA~*0}iean+|A$UjQDlW%z7rdv}BSHFc0af!2CIF6RR zQ|KPZsq0&z{ZJL99HKHzFw2bhaoSoU)y1DwE7ByCgf7F2vIy3@tPxI&bN$mu^r?A9 zo%{-|d0*_W(U;PYi~~B3jP#EH+o^lOhcy# zY$r>xzG|Ruy&0bX4h`3+u;$oyBPy%N^-FOv5l2#o62wb&cyKa54CH)oD2h#0{Mo{N z@m<)=2+rT;g`SvKU^3IJy^zS_Zmx=i)}s(>EE3syY9BC3u!sU zBO6vq#Hx(SlF%>uHIk>kb`UD;e^pfH<$x?J4Ux{1w%H(7csN9)@`B1n9cA5clKrCL zB<^rO!5d(>k~g}N`am}=&C1eI`!%Xcm_%Z-lLPJNK2cIY$jE{u%*@Pjg>Q3G{+Kde zvevqVCJ9>&lABG0ICj=HA|1cM2gHhkj9nS8DVmM1$LdU4gJCkVqg_5WT#!;$Y5JiY zVM50urXz-a7{BPNQl^6U_;2)s83zZ6ckU|eI^|c(`wP3|Lh_8#ZHo6O z`&I3}+L8<`WDpu(7njOKcF-{`yE{4XO>>zfauCNFOB4l_gI;HE{LrJ!*S^CK)4Ex* zwpQ-=NK)*EgE4=yJAzdwC5y*u``|cv5^91>wxf9!sIBhBw61{+Z%M4Q#rp%%YD~%m4x-?m;Ls1^u2YjT zIiawn^WGe`YiM?m>Xnc~@B`n*;$xyJ5mCM!I7FAx{#g70>>)ZPfSGgJ=cv;RB@1NH z^o0t*4lo6?buQqWnQbpg_ZRjOuhm}h7W;g5%uI2$EbQYI0hjiSlKQc=g{)6mLrJkH z5F08XbAYYXiMPONaAa6MYFJL&{rbi2|nJp14=DOgu z8T#h)mnNi+b-D>dj-YjMrwi$ZGuv=fZDwv1>$P82$zP3Mo`4$}=BLp|1+NT=HPv+{ z(_4!~Id&?GBD)te7o`1MFYHSm5)zV@z?#e@LE^CNA6TFMnd9vpq09=)ma7?fC6$1r zT&8p{to!MtS_L>Npw=@do z*rj9DTnbb7+AA12qxwlz8jNO zSB^yeWSlm}YaGp=t9uS~DE4sjewHpW$RWiVqUPteFzk{0In;6#j23=2M7>;0ZnIm6 zgqBd{>CM%NsTm)9y{SI_wZHI6s+@rMME%PDw3&rhrU+~y6&GAvHiUL(R~A+7bS`)j z$u+5FRuY`)6imgOq#qk^XD~`{;%%h6NY5Hg60sQ3+%Fc9SgAgwiw)K20)F6?Y+c>qnWALXcRr^<> z+|zoe*o#c4BV8prv?W$)968a$M?Md>6)$v_FMpxORO1*}8)gbj4e(?kfz=j=o8__n z`s2=21V)m$6XNN-qb&Zk}1@%DUzh)cBXa3{p>=CQn_y=hHeaR(tx zLN1fr6}C*uMpxB-&=M>uVYgVlki;2YD6N)TxeII+k7|%#2{{nbJNp&ign+n5q%(cy zf;ZwuiRb}kJrq-?fJw!R*yLGgSN!0(0{>#!#F~kcekDRhWV=fK3jE5l&>Dha!tFxG zgoM3iS=zOaMZ_7j?>s(oxG(Nl}ah7;cB7!~@KOQ1||EVlt@QW!9!t&&cx{0nTc)Nww+9D+sVBCe((M9Zg*8z zb=AFf?z!izv(H{@uPc4_V~s^wF+{bEV@&$H$>m%5n<(#~FDT8E&KF_y!=3Dv34T8b zzeasm=|Hv#T#*m_rSb@txgxD!A*yE@6E|sI|SJ?e?XG>T5u+Xq~f@;n^~h zny|c95!8fTjUVFDYVxJkvbzFUMFHfN2FyROi=4#%IKYgO2%|btYiq}*E$Db}4Udi$ zq^53~;3$7Sfz|iQkY{)HPFOp=%I1{dB@#J*eKz`a&}HxjdD%~IS2|RAzA#_#iL#b| z@e0zLyzy;%DV8tnRYNH=R*Z{_-fyqC=k0swm33mf%NX(8kD{_nj_uv)&y+j2fxW-N z2f6^i@X2RA)HS+CQ(5a8y0x_-nf+D(wPJHS5A}&JV$k|8?7Kaba70KkZ z7plF&P>;;YKXG=Bq5ulSQQBxYxxgnbnuy5j?FKLP>s{gQ{-N)(XQ!7k1YSeIeTmwx zhH=Yo!9Hq}b0b9D8@iwX|3f{-uC9xstR)OEgZ#Ts%c@pEkg|}i-pdc$P457wOzgKy z0c|$E%WEP8TTEcDE_-aPcd`pqZAkT!x(%Uy`FP~T0ghEjaG#?|5WJLL3HIqB)s1*G z>;UnwIVoK+lP3XD=%b;Qkg)ZF!ftbZ%t0cVZ6wk%uiE*xi3=hjqf4g70I$b>M~tIi z@hx;d)NkJJtqGvhr-~9HLUfUliW!m)Du+`vV(jd0Pz6kSj}yFYXh7t5;d3+3kzZ=_ zK<`}mOFyYK`S7WYy-7lY#EBWEK%^C8dubrf2uTp5@#}=>$rpaYdQ6Qlzeq>jM$9KqgY7vf7Zl+4;9JTOV+L zf!|xwt*!ijZ`VZdA7cndpz|o~GwH!BO8CmBQpLSuDo_rnu~I4+RrakZYD4+5u&oQU zlXq@Q=nH`HBb9OQHaTZs-yA;&|AEO&+u{nz4w6_3Neg&GjeM_rbF=m5rLa?(p=f@} zkDZfvIo-8J3Ahw&L}&T=2>}tYn*v+d0J9p0Kd6&}ubluKD`v7o4tTx6^6_X%@;XXt~Who}*7{l{A`Q8hXg=#)RNNYq7W1;NbZs;_3D^;=$pFj~@JNJTcqM;Fvf zow*yX5HfgcsK$y~$fI6h;86%#pD(8_AH`A1Rq!gx!>L4GTzw2WE7A_DJ3uSW2;gs= zP~HNPVW0aj`?R4eGI&@&?KUX&gmS#^5f)dKt%u{VX(Go!W98wYF3eEGMDfeL!s+-0 zN@pe2FZa=lmyHrQ2l(1ztw3v`U)tde56`5HmcMz|Z5a3kfOsEV>sTSm%AR-yQGp*r zAZ5xJqcS^1Ypa9io=y@rYr77;^y8eKRMqm70lhtaRhnLhrR~kRx7#{AuSR;AT(OBv zXz zv%%BtsT~ASgdi18I3;$1c~nZnhndZao^#zS0BlhCP zVJ`B z;ea4ax}dRXW)}rOvZXl*+u!E^hM71P^&Q=~nrvr`= zwxSh2i-;`LYac63X%d6?LF}kYk$T;j^TfE#srxPr&bHyy7ph6I``059sMFJ!x)tec zwCWk_=g~{R3L+##P5f3>SE+An5qTTS($=&FsmwYS8WYJ;T;s?#+kL_`#h)+|nzb&^ za&!eL_b~pXH8a(~YRr%60%e%;!dUm}itq*`_Ky+DM>w@H5@}j@8&2p*`VA@id&Mwg z;^?Mi%*gk;>rsuzb3JmKV1Q)#Z?Opph0jelHhwkw_Ctw4U=mrM7y_PTf!h4|kA0%b z$7-(8W35a`g7TS{9YQ6$`(X|&fSOK{oct{LJDQcY0APs?pd{lHQoO-8TDezUh=a5u zIbvO9>oI0Rpc7OItHe+%!EZ4FmO?#56#e4f%O|%Xx=c|0L;5agGp4&bAj60tQwHZ6 zq9ioaKV79V1JRe0N(1S&{+E7r`^gm7uhA;es2_jDl@Z_~BC`GBGs3>b&W6;t)_-t_ zx`>l!<7x{CgKBvqlYS^NsZmsoP@i=~QjwC>HDoVF*#LfR*A)Vjq?{GsPOTm1PIvuc zBEt?5Oj1l#%KMHIz-f2!q6fD{CvE3tbIliSa|uiK>P}zE@I1IpmSm%?9(Jm=epeiw zhds+-o|TAt(g%KPN|GN&bm7JbC>WBGl8DnBDGkO$)}*q;6jV!SUZW-0(1u7QrFEFa zVZA$K+UPY-faD!XaL^Z|2eVv5HZ?b&!VlK*`>6$rxm)*fz`}LVjn3XwD>(?k#7?qA zep<6p3@IlqF8awIim)3d{aP6zH;?PS^8Q|4$PwDSG^uwMq2I^LPD@z}b6mt#8DBCi z67SVFRh~_EXZ7Eq(Yne^rt0f({F2=25cJj<&Z+W%512;xN zEY4nw?SYxRhn&HhBohSLG9YJQc)@=f@Y%Q0lV)Cj7FXlXNVd|`=NMn;Q(Z~6>>})jke$MD!#COCK#Rax*1hI%FG2nprlItylm|ka~(F>o?{Avjp6O8jDG2|-F59_GfbA1k$mY!*i34)KQiD;Bh5#n`)-rp5# zfhO^eIX|Z7Br`P=6^#C1dOAO$tcV-h2*w4-P%~vhlS@<6{*2HVD`Lc#_fM@yYq!aR zR-NGS%8Wjygc+AJ|$LE_Je=UWOYZ7`ZhbP{BNr zSr`jQXlhGtQeGXNV_H8#9CS*1n%L6++|0Fh(EhEoLI%8=sGGTIx)QH1d=XQd(n0p* z5n*nIXtU&PQW0?71`;{cCE;o4+qn|Thtu94f#E0V`EwsZ69BQaC2uZh3A($h81%f2i-v_oNXYey_CfK z#aEz+CwC6TapBWYL9Qk{N?Ko^Iv7zAoegu~ri#ogpo@}agjqzkEUJn|Y%?Hv3QY@Tx$5%joZ?t*xZm>`PwUp(CzN4DY zoBoU>9YPQ=iL1=Lo{nu|Kp@oy(}9gf!JDA`)cPG*(?hNQw3_4(YaR=8!REF|j&CR7 zA;6R4OsLKnY)1nJFJcA-om&mbKa+r0xVqPOD^n6+m%wAb^{OwYm9lrykYdp|JbbWZ z)=V>M)q8H|VJ^IG68Oq{X&E5(IZgGq|J5udzV;$7X1;9s06c~@GnVVeOdC=$$^kbq zivpC7kq$BmLY2Rt!&VgunW<2xK@4JQ>%|OHJ}~7om8Bh?^o|>-P2l=l!`zoXWGyA; zif)6~mLk!lDi+v@>X8m}`~%1Nn{dpp9n`QxzD@VWoxLfCyf7Z}_uTaaq`B5HmNSUB ziga%aTlmhhVSV~Y`^bGNK080V7-5`rKGPAjfsMynAl^Bo@LYYh#q#PFV^6O!p z@3h~BcH>k)lBs92y?!w{z$;180TWmOD{PK(eVhUQrgkfZnREMjX$1J|0 z-doqMYVr8#wQ&fsVd#WdNjYwVq}*d%!iHSMg9~b=mLt`PgA(Rb$?!y~rtU;qnHI|E zjT3Ljr;I@!@w=MRKj^EuYAhZU80w0X^A_Yx@ZHYKpH5#Xece7P-4-*Jt^zpn)P*|42~17&9u^ zH*kn=P!Qj~gY2mPJHWqzL7<|MvZ9c%h$^F#sX($}u#1Qp8I!9z=l-u&3{+Mo4E_&n zxaj{KJ;6GPxm6O zB;6%H759v`S=3OrL55ya4{T3a!#LS40=)#e;^@|aj~O|U9{jM8{268_EGYVM&FX-R z*ttjS6V$_?V^)t)(bPsJNFDiqU~U-7@|ycV_m);E?uC4*WQbg&b(8=LVk$NH*eo%z z=!~-t|AJUdP2FL?({#Qc-lTJ&vvrR!YCC1ivF5hf8y|`5C(A#S?v?J)?MiYLkJ$E> zzu-&=NuA*mnM{7mwm?!Zu~qDG#7N_bR>|*{7Ib=yRr6VnBK^569O96Z!{AAePMJ5? zaBCbdF^1xzy6&AtOsEFpe%m2-pR)^e|&UC+o*eKfy&+#F0L0WoU*aU13 zA3-WZOBj40u^K zrDnxfGNpoT#i;N(pKrlL<+1e*`CG5p@J$#w`B6CF{`9>+uO*#RxSmnNv7Hms`o;Mk zD=JcjNi^AAR{>b`(R&)CYt<;c4yhtc+f$18!uEa3_cU-cmO#&`a<#IswURj9SVAx_ zmtPXf^E3yGPyO_;EJ!yJJTwM!-j=XUXgt$1h0d6Zy9^xKDv&#x#@H05cl9AInh=GJ zE6W+sMsicy1}kyJNcbx{y-j^_JSdubP|5d@f%;JRk-K6{g^iGWZ$vU^`6FDmQ(GIK z@+a=pmK`}k#4z;>$V??KG)C5#>#(x<87XoH<&RV(Ap}h{GkKhoz}SVKxv?{9J@zr# z-fWCQ+mf6;^ukAaqq=O9*0}QOy|V7_b~`pK_E}+8b(-44 zay#$?a&!lAnuS5V8#jw_Ki>EB`VDZU{9e)YH~aD#j$WzNt>j!qzB7ZKuo&rY;HCs zgrE2)=C_+lhB=iStK@#?GG&sO!sZu*h+aL)lJ~dddw5qS{-U4rMS2grqD+o> zna#JQs88bn#H(FnL{7ong4<4A)z4wcDfV;%CK#}bA=EEAbr#M>KYCasqSDRLgmJ=Ia8whY{mEkIfiNnM^h5Ti} zKA@_R;QMy&TkI@ahteR?o*=-HO}MOP1_Jw%81(P_-Iv1$_re3@&(8AK%j4Og6&4oz z_R3vPpy8)6#q|e_hB5S_U!R8jD3M-JSb0Q1!5&^^V91u4g%qfkjTi)stp5SyH%KT* zFi7bC4;Uf;Lq>EG7G*LvQI-E-5ekEx1B8qw|AWSF{}&p8a+3=jQ|$5!96cv?^Pe4) z+qrU1FDDZWREc|fau7>d(@BG|T;B|V#lJnzI=_EeWfzpdPecY_pKQ7ZAHnvBg!Cc& z19MrB@vdy7V7k@Awc-bRHE~?-xaolxS4PY$9kvj#=00SxUV|P-U)6uv;x|WuKV_rY zyqye}lhCVyy@WW3yr_Yn7%rCS)R?$uW8NIkaeZ^0vt4%;Sru`Ge;Ua|nUEVOB@YIl zNM2!GM%y0tO1^lh`3-)+Ub994d=`>ASVhBId7TR@!N(=HpPoI4TzZdC;J0oM6 zDJ0w51f#Z|AmbERj~ruB(+IzA6>#+wO@WHA@(jgn`ZXZhzbW~5ZeO?atwiGR<-=2R z3Jn8`Ek&|SJ~guZ5k|?)YhY`*sZ94uEx=w_UUJ;dyk>a5-!(~?!SN`|;WFln#6O?B z9=}pvReioVJ`b-F6GzcGtN;gSY#sBk)Eea)H2-&QZ=?At8Duib^CgPcCAm3)pg{uN zDh8KDgi+DOm=7q?RfsXC?}4**W#!~efkk~BpnZ!r$iFZ#P4^8wAxH|O4;&2ffj$u{l|r7_7b zx3^J$Jt20Js4#=q_$$BlbvLYH+;`HR0hsPLt_}p$rXbQ0rC_tes>skjvpP=52OmKj z=d!<-8@{2hh1I_;YsRf5h&4f-1Lik zN`CScJDIz42Lona1%yhYYQd%n$RW72?SW(#?DKzW#%ya zR!3PD=&z6ez;FR&UtlIkOB3uRcY~*BZ z?7b=+N4C%tY6uSP7Py*2ds8_PcZ;ECAnl6b35^MU40ijJ6q1s(tau`<*#xu7Bk=;;;3ELLKJ{i-sn%$6=7%6a@YM-61Oo`?+(txrfGq9>IyxK zCZxVg2`DfwuigjJNt9~&W$0rP(>*(97wIJ6rBHSssaHR8?*`i~Rz1SPc3LtHp>t@! zH_{>9fN|X{hef>nPJ*IDE&cpak`@Z6ZA1dVRf+O!Rk=nx*BbH+8^fHEu2BLqyC z9sOxszarokaIJgc(=T786ewTsFx7(tQtXP=sW)piSQAqWox&meLYvc%J0OV(FYu4l zI40NYbcKveG-LLc6vl#kkIV8IS>eQ38z!`*4R zl&q3(gqblhq0%0ZZf}ya_PD^l)bQao0!WJooabrVRwep1Knxl2fr+s-^~gyg`0u7S z&^ML%#t{UyQYimH?Kdb`C}@yt0XQTm!2AXQ28ux;$yh~IP|(R)*hEx~ok_$@e&%7Y zE4w7r^%>2HtGWJ4%;zwrfC{d^`Qenb`~Q3lz6*c*itP(PdVSe4<+4&$* zUZXwAhO5kHS5X*S>xTK1IKrPsEJ4vZH^B1qro-JqtogITcYLJ4l}m=O(YDO~!a@j> z+sCS+U4GR>tEsur5ehzG@Q6Ffaz*S`^25vK$O0~1!tl#cFl?%*+B!_HTsr|pu_^%x zfmbwjE;DXSg0Z@x&*uLwzk0>(?Q-vYAb7MZgLWZmJT_I_9PfdIIb|KIPjGPj_p38n z5}KPQu7omF968jR^2Obo^#voxHa+5DK5)_HvsoqMz2{o*b#8X7`6E+=4CJbPIDVVs z{8^B1bvaM8fw)Qqj69g9+!K3eB+sIY&P?=tj-&Dx%V_(ep^FBOCuo-;K5V(15I!QBS=$xl zN9@LHJ3y_LWw+R&H4b$uJ@78%Qpe_45FT|Rh{iCvSd6E-EQ$PTPj!Sl)I3OZZI-Nk zAV?C1q)z6JjC@l2p_X;dT|%Og!AU@3Y1>j$SICUB>d&j+I(8v2;xI&@z~0uu+9nW< zAa=1x7U$Txsl>cyDu`hz%sw$K&1cQ^K_33N=@fU1_YbUu0o4&cr-mx&6`3jSG_awr)o%dfLd#<0_0d$yU!R$rpErkzO=UFt@b?b>cg^zN{k1#ts2~31S`kRTr#!lTySq!5P|^9+oys zbF+TunpsK`)!81-1D?#hB&S6i2ZG7cMerfpu-jT~Wl&uY71BS>Z|y6jy~AFb0Tf09 z5nna!S;3vYkPAH#+ey+%vo1J0jSc=-G4*wp{IA%{RTpAHLJ|(&9|Mh&YSkMYOw&rp zPod4knbj^(Jn+1_rpo=gx}8TPkSr%32`R|2BV9QCu|!F3nk9P%f3`YshRetP1H%in z8GJPaat-*DAJ(R$>XLZv5 z8Wi6nnzZkGH40!Yd-FvII-e@%RZfAANIJjRNunZ5#h0+LMol<2^v7OqYD|8GW1;B0 zA}ro`#YE9hUsV<3x-=bp)S{4Z-;pqx%gS)Tmv9ZmwZG2G&6K^L*xS}eD8LRm2rR2s z2*sFt^W48$M$-{*!#0OrAO^c(S|5Ov&7L5)z|oqEISS`+AYX;W!!~VISK_Rb@mJ|c zM4lGRsA_U=Q{_J&D3_v_Ave~T%w)3@O+;oAh4V@3RUPiY&@D}LZS;^rmq`B`YnjkE zf>)8~^Efo?%I2(}Q)D_23EiEHE=};}{9Jw`4SRD&PNZC^B6@=ITam z28m3Sj!RkP)59+%^1oZP0`qIj*}1`JxDsYdmTP-c_co@iwyMSozML=Wxy?g?G7y*I zqg2sHJWL#do)YX>m8anB=;~a^c zdE&8I`JC_YOBium4XaKuRa>ZwHcmKe;Ac10~oBT<{ z5Z@2Fq*ylUy8Jt)zNPN5an31``5YU*(uT+4E$XRyZ8&Ncv~=W9#e#LWUcK!@X8pF$ z9o{hnNaSHTXNSD(EuBx}2HgD?DQ^2unWeqF{%RQbEDemaF zD#s|%9#;VL$??tY!&kyFm7~O#6(g{w3~x3|u3@ar6ZvG?!E8|2<`|q2^Uk?F6_Mk( zR_5~`Si$8+`i0W9?mJ>8A}g}7 zpEK=yo6X)TGJAoLbiHhUau=-eNQQ`(l@MXvqB{jVtDu3ww5w7N#$os%+t4o#g{iJr zL|5fFwazav4nY*R66+?@1-D9wf#$Yk3;To2_;K(UrlvE0Dy`%^E(!i}SPlc=h5-3< zk>>>9s1Zn6K+sFAxt!_+>Z?^xbu^hQ%$nL0?h#*hrfu0w>xWXb%xs0 zZ>T?hf^2-WE-(uNA89ukq}}E4Q2X#_{#3Ia9Q|BQ-#ApUTBoqkw$J$M^X~Z%Om*_T z)JBirP&HBeuiEbw?kZ(9i8ybNg3X>cMi*{J)}Hg;a!DsW$IBQK^QpNBB(GPRyVtNK z*5RY@BBgPX^}=oFAMhge`$Vz?E6IXx~r;5YqL z6w3)09S_lMlCT7wQPX*h+)JM~*KN00H>n9>(2FG1yoLl9t6j3>ju--WVgh!%t&Kl# zWn~|v`&u&B7?CPSf)6d+LIupD+P)SuaLtKCKB3i=gvV?i>Ce+2Iz!V}WeH}N!w~t5 zCC_p$?saNglnt9U@ux7`k_>6r+kD*}uB1KSM=@wx=EZ)fkb@sNVy}*q*1h2k00+8L zUOgZ%?*f)owaB;jACHB%?Ds6w%hOBIU{ ztF0Mmu13?A+19705P!^@W*-j}6&WhXuidtLBo_Xhu_%UI#Ft7E_q+CMswC~=F+3}`Ze-pv{hk-xl|+>kketJB9g+3ek^R=}m6+_rS3e_*6N=UUkxvwYXXsXQxi)gqq0nFGX1 z)KF`0%LH2L?~z)CC)j%%n&!tj_FI)u;X5Gfh~C>uWoZ)uB$GPraA=+8goT>rcIpN% zUGlBh(V@oBwOenEbRvf*x1v<8K_JL8PQ*E-gI@CB$dP=00Hwahlb%?C8oXAM`%moX ze4VxZR86SIQeKSVpvM=YQ|H93&QSPoiRbxWG7D12Esxcaj~~#EK78^J#rRTDZDmR# zBf?=nzwdkrSR@_rWbRXhA3!m%#!Y%xEH#HYO>j?6Y!JE z&hX|m2gaTmXu=eD7Z8Plit<@jc%D)J)N{Rc+Irsp=?Pnn4wh8F&QBU>UFm}VM1*D0QY_F!%ee9L_27%3J_xJ4cjnt(k?Y6Q8rXRw^&*YY5pXLp91z`&`3M89CT##&Syq{D%Sfh0)hQExRXZ5 zS5BKk+q=r5Pqg|()031InNP&Pk4)lM-sQjK4^`HbL}i9vB}=wCH0$m9glUub%MNp2 z#`*-eEd`$J6kt-qv&&j}@y^?##05Fw#EMs@ygXCLe=K0?2FPxUQrX>&onHpz-!=nyQun=l*?omL{J zSHPnuTGNLm-Kn_*k@kuVL)=46>(H64rc_K|b3MeyvV<_*@?60uJEWYX?65Ap>=2l%NFM*wIFHHoBJjZ$1UH-_M!1XnOP{8@V#a<)4@>q+OS z?VK^6WRxU7bI4!1rnH4l*+fUC!$=o|>_0m)QcaBqfG|PhZ*@^<6E)obz~b~{wK1_H zIW(oj>79Au`tZ3AV^pj+G9M>rk6C5?&gOoUo$%+#e9I9`nU$W)5je-t%G!%r#;u~k zPuE(;7MSqU71n8w7v199CnzOU+?TXsn)L^y5|X3^TV~F>8I60O{%so#%}cWEw?zmq zj%zwTerZ`F=ezUh4E3*9Dg6`2zIi+9`mE`li)0ygfN8!Qe;Lq%aTj{Gt;NC7FQCxe zHWmpnL|Wv}W;C)ls*x|D@gn6tz#+S+2;}U>s;UltR1jzg8Ix0lopNPIU+e+FEL==J^rL*%(!Sp-!xz31=xQ74~gGw|V zJklX>y6&O|kLsC!`VjLMY4+2W&$Ns8H{`9hE?6gI)D69-%1Fq8oSf(kfH4j>A>7?H>#Le%;DDpW!Ky|uu2LT$6btn&^VGugb;Y8=h@Ok8 z+3;9D>H-e*S#Q#P%sr9iTNVh3PC_al)Kjr1&QA(E+e+`YSwzzn=SCckYFghe5Pwd(D@WbU=6w8;(C?$osrkOl3$+)g-T9uRKs)MhyYc{P zYk1lI(w3SBKZ-qvX$+BLKNqqNG7H+oaji>@(4fhhvF0z4E!|hR+Ez|hr4LVBZ0`v-`YXFjI^zZoA=FYdTj&|Iz|CM9!W~)$|Bt@Bkw=Ww%*c z<}{y}ZR1>o*^S^B*8dlm6v8CE# z@Zo)rs-M_ftZwekViH+2@MUW1PmF&N)UQ8K&AT7oTp3iJwDaq**hxQ^UuRUdK`_k$ z25lYZJ2Yx@bXJ5(byJ(LA1mWd=(Pg!>o@vCCf0R&7}GUc95_r8>SVBq2#sUakxGEL z4=(cJ!kl^EGuN(?-XA^4c2@XNPKdu6Ie0gOo&J33&+XX1BSYxCf;mak?Hhma9YxCb z@sMN0GhKFf4W20f@wm(;Qs-#W+L#Q`PE)PIg;hV>uca&(mg81_4 z+&qWqYr)qdhd)JXwDZlZIX85m`kZuYO7k+B%Q_Gc*d0f8e}5HSL^3X)wcHlQcLVB8 z{!FzRE<-wd+OSG(WP?2||8uinv1x*9m^C?_J_QUVi~~UP3Y1S26qrUoZa&52nd55?=?y-!Yz-1ti0=Ofr%N0P>+2;yE1t|^ zR-?2FKnUR?vUM{tI?J3A3ytZVE*#d3u;h+b5MmFZyVAuq@KF1-feTp)Fv zF}hBzh70F7za#Me+-Sb!|^HS2Y6HXYNP#5nK%nq1p5km zq+OJB_{`SI|CIQ;QzRMDy6NLzB_AFii}5_YPmR>X-KoCwW&w(uZ1K<(U2c{!lncs- z_H<|W>w{TN_NO|Mteu#8x(fpTFohR#m0fTnGnM^sI%rjeNynOiQlm?u?-}l6=F6BE z-A}`hA9ONtt<2K+0t&$Qh$wT&YLQ`qky?D{$F3BGGj(P z?7CPThD8R0%d~j?DHuAOlXqKEZ~|wWFBP$xi7Wlgn#0iXnAVG!do+CbWu>~K^I{oE z@ffwh?%O@zjQKo5bbE-uoKMzq^1Urt>vJe@0poX9 z=qEkpw+_vRN|?e|xPfb;xr5qi@mdwKE3eG=r6D;K?tux@`tAdb9UdhJEi+Y&%kR1F z+~1#y=BHhRW|&S?hL-Bdf+eM~hwxR$8KbudkX1Drpy|w3mH#SG3|Ldtv2=_(nT)A5}6>Z4HcBffBN5%J*YyhE-fCg_bzn!W(VW2So>T z`O8(!HQp8Yhlg5K83LiPeSZY15)FkfwUnVSe@6+q|2}FUrdN> z2$jyHcZKQptNy;pWwy0AY0GW-?&espU#Lhi+~=9z(`(3=o4ifja=XOIs`=4$(MY+W zf()4zQgZROV`~prPI6~W4$!GbRmA6d`*xhur7MVk1SpQ$VhyadN)0fp9mefPTX3Qs zD&`w57{l^j7Y9J4JFlgO*V&g*Y$Dwp97#;K={r6-jm!?j>sb;GlO2M-S-a1~X!Ft? z1co=>PDkLqO&H{V4Y~nc8BKsJdHWjB(36-J0eqP&oirK86HeA{pbp7*+8mQ=({h@u z0&3O;tX?hEtV))H_#ct!(XSE8wgx-ehP_*L=t?;U+yY-?xk0 zR1&>1;V^pQap9z8I_w2kOW%j46bSL;6Ak#|R7d;I`iG-{nvvDT1sNVZJuXrHlPsAq zip~VW!E)R`9aRjCcxIfU#o`2T{idYgE+kr1p4=~|-T_>M6m2=Af8qXwWwKup7tsN; ztYlaQr~b487;#o#tO|mU=tt+XzYn(^Uda4;!GmWva_(`i>52PN=Us{HZp6eVZDmA7 zu)OE=&+DLXm`QFip1w5%`5ov?o4%=79SlmIE6*8TZG(jL1a}^kGqdwD*Feq3Gt>Ez z1DhlX)752zLu#DIP#JJu=^m9AmI(Wla(|L9(SKmqrz@RRYoe~Ir-?|@Et8Lszk~9R zjxa%c0!I&hMrcdwgAw8oo%F2>UB2UZ+v;sry|q{60FLyFSMBnfZent649*SN<#fML z2$SRKO17uHfiKJZEh1)Lo3|BKDmkW_yfgD~UxY3jDtmo9#?1_*VXVTTG)~I6ojHje z-LEEW?3MFDa=VdJndl~}wlg?p8+)B&WCy!1{j~yTcVkl}c!yQ5C_aWPsY{9Z7pcxh zlQgZLz6(g-_v#-pW{G`SFJ;x5!n$Y319ZQ(hC9oJ+)P5W73XcJv+9NZ9B1o=v=1!j zXL;kkJu(}cC+VrH`t}+-8XO$h#Y2};?DhPb2;WKq`z%woQ*r3xaG#`w{X7A$b%+0U zj7K26Rmp{PovOwRTBR_%D8#iaPBu;@rKKGGrI-oI{OT*^S~pckJMdG-=7m#i2*^h@ zvgMZcr>lTf$A*4^vQq~(7hg>t%ei}Ls4H>m&*8Nz3dX8dmA*O?iii|e|Ub|YnHlsWxuk7aJn8*9Wf-tRvkiu4PH z0NY|*g81g9?U6TG3ygXmp~mDGmhL9UQpIn5Fuk3gnS-E+dF{fEsPC?f?6&4|%Ng1E z7FiadEjUMo1JF&$iOOqh>~P48v0P?+%lHorGA(+|#@mnHp=-FyQ((+6ki4}4z7Uj#Y8F8 zM?Lkd@EfH#EwiGk*3jErgeSGD^@fLw4S}hBWqTltS>Kco8$_$?8|2>OJ02UF4?1YQ zX%Ct%Ievovq{t86 zu@1e@qVS_r{RD+%Kp??p|x9P?__c=%orNMHs=3#fEIC9)qXzcGeJe`a>V|AAAz) zJS$xLXowp_Ccqj1FU#tZg2fe@Ti+^2{#&@(o#EWuZL>fF#o`^yU>5F2kl;-BKIL$ zBcJeZR_(>lmxSmh;kmgu?`!`>I>J9;if8Z>t8XZ)xQBfM(7bWot!Xm}PZ+v0pBlRS z1LIrnPw_lO&K^&|(AA{-?HpPIQ& z17u-DZn{!|PniQAz_2rTmR_%`ieTrycI0%96E#XvJi!xpO@lQ#;1R=6 zYQ+%gZAqxy=5P2-j&--74x5AM7)XYOM2T3@-4KE-Ur~@S`_)wXU-GHen*5HR{(EXG zGSEXAv}0;Nxjc}tx*X>GY>)J-w9(inzTZsR&3gTSV}2Bl`u!0&VwesB&Gv|J>?&dq zp(MEQ&8_}slOCRxo0n{vk+k-MtO~=}?=z-8!IuhHFLakV9KPuE)U-S@$hFK`h4UPC zhH4Ex*Y%rrRX^K}iF2EM4Bd$X`kQ|=AE-wSB}QLtp>6n&!b0je7oQafyUBG}U-*FJ zcME~Vfw2c$YJtK(#lK2!RY9I?A4r{Fo@X3^leSVH!EJx=e>hI=zf{`Ej_mDgnHpx_ zU+&8n6YC7JnUDREh2uZ`dAxVPL-afv=46^dOeqV5bjYa{1O+xBRb@MJAVFvL!S9i?>YMi22mpn z%F$;RNIr5Fe2H$hl};5UozPuB%e74ZDy=0{62v*x{94K}t~VA<-XylMjq?b)g|oDe zViuv62L<^{fGA7dX^5zl^o zN+x|}6n&JmDSg#}c&l&0_n_y%{MOTZJ}P`9tO8;ub%YNH2RqElnXI}uCC3HIO!PNC zYw9gyH#~g>_F`j|@@g7{mM-3*ND1~4-$PBJ4v^p&RqyLCl3=m+Wuoa1<-mBGEn*V~ zxW@eHAuA$nvyRXa5h!+f^KzB8m=u(bB;>b@+;NiK92>^lGWeYx_xO_yvl|0Job7xd zY2GWcOU|e1`xg}QTh5TY@KYdRz;%~HKGx1-!JF>+rgyyi(Zr#oX}$3FMy&g~)TIn_ zgyE{!^CeMZJ+rj}MRZ;0E520F*@eZv);3Mrxv07pk|;%>$F{gi*c31|mE)pXuSm+? zRi^{OVFE7F)M$dL>6E9rMR`tWKogh4QkYLB{;c+jCBRgN`{0ZtZbIQJ<8#C%X&Tfd z$Z$|Wmo>y5AtW7sNA?U8pz|TElQA zHJwU{`^RPaJQEW7D_EWPbAEQkP|HUB1Iw&tCus0KRL%JeoN*y?1&v}Ko+{cv=KGb@ znQM$kbZ@Os>y`e`ewi#2`1$H*8#pddq|^)#S_a53?(Nec`YVH^J8j;?jR~M&_OVn( zEhG>_j(r5SRDZ5`xt@u5Wi)V@(!8Y z0;eXo1~Q+_MmP%r54eHIs$IQMfqJ>h{3nd@*AQD~S|TdU!j^c#ab#Yp>lch}04LwA z+^5fnpE@1@f9SJ|G5|v7M?iS1(Kc<9iSTapBU)U@0&;M<)rKj&W|;I8Ma8v7?D*2N zB4uHU=i{M7UCeS@8Ji5JQo{LV+;96-*%ML;Vl4OMSmrjU@(Y$PQ_bWIE0mqyXW)SuRp9j9v_K2?n*2vp+FeNMA37RTIVZVF97>wmzsf$-5DD9G#JlG|3* zqc{Gs$)B>ycZ*So`@pI02@dGRJiFZW7O9tXV2-VHmGo+D9gaserj`36g-51DZU$7^ zGaQa<+yuxqWc}*SdRX#3VU||HqKs%t#7;;z&}|^w;T5YA^4!-m2sm$G2fbLD%{kiK zJh^cURLd$&9wDz5%+~g+tsXUMRS{rEom{Z)jKLD;KR5yO}?(fW~+}X z45r}bI;M9vRTn0&{ut~n2^@5%>FkJ?Y?b!e7c-K7enTX#5k~V|%yJw@Wyad(S7K@k z=ElXfM+oDPu4r)|Gg6*1e;s`Ob{|@m_(%Ny2NnIvf1bWu3zgQXqwux)8f6_6hTx6H zxt%88oBP%IyM9{e^nIvBs?{$JrhT|Al#cx zeU;=ahW7_N`sH5}){_2%@Hzhg)Wu5vA%40@iMBN>O(#4Z8EvxMg(OhQcN#M2*OD$9 z*08p;d^k{aH&g6cfFsU~ux#2^gmRm}f9pY%-A=>R^CPnkUp@8C`_kv#gT^{U`q39; zKc#CsH>shd%q?!f>d4&MzfWo7KJc{%9k|UTso7zEq2_JdJ!^}|ZcPM1kmWKpTscv} zYd6L|D(0ma816d_UN$lPXdZg^-9~l>%h|6*6uZmo8c2rWK}JfVjgM^oE1QS!Uw-<1 zF|T2g`Ssd7ex{3f+@HYlc+el>w08I{)#H!0=LkF~PtQope|s=#w1cW+9U$RN%}DAt zg;uqz?Al|Jta4`ePq6w_KqQH**7vBzxyQ70h==ABG+CRXZprZVQcQipL3g>ipsSD> z4)1v)Mm}P$uplGLtmj7Dnw7`AFYO7JWSf0y^kzAxYw7Kmb_%0uHDvph?2bNliN5jn z`xbQ!JnOQ*rCH)%(y>E#U#OuTkZ!&%6a;yw$!TNZwx#v825gm9gAv+m{HVbhyPS=; zQ#!5Q)(3@i^AaMyY<8Mh;}}&%fa{TbDOxzrF^%aEs;?y*R8+%m}K%mQnV`kiFX!=l4=d#0pBj}KxahtS8!A5U&a^%)vD6runH`84CqNF!q|;x zs)J(D2uQ`2Rfw-NoL+-V)41ca^zIW|Jn)J{9_Y8=c>e&)aDR%@BlJB`(R6mecC%AM zq}}3i?`QR;kbM0r+(p#ZeWW+59M#1twqajC)`XGjhRj=WrRMQ-KwDJpK6QDH0mFS~ zWI;t?=+o<0!tLhNtQ;nOFJP++gIQ-8*lPrfd|NAu@uksplY8Wi?IeB$sQ9*hRq9ej zu(n>E>G-C#JSX25_A?-Q)~f^Pc`=Pic=4dwt1i*RQp*{-w<|{vOE=I}*qI#e;X&jW zrTx9U$Q6;lE#z=0#~8wgZxdIFEngF4D*8;=z7(O&K(`ytXSkN(QG9aZ{(l6XJ}7NU zzvcqHP|~3onMIjvo6-g$$9SdEG<#rdjvh2&W14LAq?mqFMlJFF)Z80dfnL!U`O%78 zmALLTlXH#0R(EDQ1!9`c{5KD8m>s9v~_)+ROrifH){6?2Ij$$xm(K5AI}?OMb?sH+CUy6?Y{r7`-$b3% zY%Wgf=;Su=oW)_%W9I%PtHUb4&ydSAVOqwjLc8AFoZ|xCm6nhH0BZY%{cBrC{Ti%T z#(pZe_7X1{ri~7~&Ge(Bu&~&@>R&wS0(YRSZzvFrgMC^hx1Rq1W>LUinV@_ng^gcY zM?1|1mtFpz*(BHs!oPMl4m6rZmN7BRN5ZT!8qsAW5i7SWrTJ{{Hf4A$P6*GL-|#a33;-lt^;T!^40 z=-cU6M+`ilVZhbw(YW7M(nmVHchE7CVo|GH^q+deXAWHBU(&OJKls<(;6DkiS)5lk9h`h# z$2Yn$QO`SK8$x%#RTU4XvC_z^R)!+pXDZcYPeE87W+3jM^6Dql&`8$9sQWS;9Ub->49-$VI+&6npJ++?FlTFV>+O+gSb!4Biuj)Uf{mme>7y?PU$?S?IC;m8z{D zo-I?e~%Yo>LFoouSWNhz#aRDpgKrh)Zr#8xQt94iVrQNpg+oYp7JzivX! zuvSA^j#RO(?p!XRfviv*CxO{kU{Qq#^qp4maq8W?O-UH{MZ4PmT8roV@@qCH@QT8n z7~r*XxJMSQcS(G9-9See1Km}wa$Mx$!>KLr9G11O&5dU?6-9vd-d5Ox^o6s;_}lmH zZEx{pu%M%2i+&3_Z?+*pH%c(~0`bVo5&u zEy;6MUdXO`LDDzV+QTB^6^%8c{isDTEf(Y9tEsp>LtAaB3EbWAeoBqxV5%?s*Egfx z7mo8-<|P#Vrn)rGiK0$GPzVkkiYpuEFYVQ8W}{VUQr#nF=g+839Bq```cvsFGjoM| zl=5%GVM@njV;A7;2l!H4Qo8J6ao6*!+lN1uK8o8@S$#d1f&Jl66q}8WTykP#VOS6x zJUGyrah~Pj)R~|jt5_d6kJ63^E;y4}=;!`btQ|g8(af#v%ZFwoYKe~0z3p3y)OtGH zh1e?*)|Zbc(a`y|bIvu}XO~sXuW9!BQPSiZiIV#DCkMRP5~GE7Ah&XdM;`XEsV6_; zHHV=59z7roq!vD+l}Xl%SFa%KHM|6G)o41Ks|$nd^RW({Hhe>c7^S!#d#e^9ZZyXE zZmw_($-eu#v!q5fPiSziVtB#D*=`l8St{|!8_4Ba(KpLF)sXr&b%#h?2Z*dl;fIwx zhus&iHZ-UFhNeXsQTc~Ru zl1){n(p_?oCH3xZ7ciQFp@+h``u-0kyHV?kk^}xAxcPSk($ID_ThhMH$6hU-p3#;Y zB>wP*u!?ca3&$-A~Fb)N%q413Te+iKQ)*8z}8?!dA&v$Sr!s>D& zviA+w%u-Dmy12hk(7s#`16W&9NX=p$p|o|+Ck>QkKjG)|`~pvlI$wt_tL)ML0J#4E z753RiUf}+Eo@z7F%aHOEbjb#)#K)zW{~!f+|@4eNQ(T=}*$J z9HLXK+6$d6z zxxc)7qG*b9eP;BjG^QP`Rs1zH#TzwRn?z%*n#<|jdFaPYAJkXbCOsRSTl=S~GQ()+ z`ZZa@M&s*PW;h?{dW;MAt@N&O$3q#p^YW>{<9dy_sQdz9@E$-VLWgmnCf8m&>sCR2 z2P(OB1Jw)ZS_vA3QMht{uvA}wzpX*}2kk|eg;BJc z38TVhUOZ?#k#kIa+x3S-(c4@6Mjy27l)s8X&l*9T-DTPBUwiQQNo)LTk!XZ z`z|bN;)%aeNi=5a;^VlOWf>j(D}yiS!j@={sNvO_G;p%pY;F7+!#r|8!)?Xu94;Jt zQdr*z0-+xd;4XNYh{%1xWgAXa%|xdMglrzQi1d?qxO#P*#4Xde{cG(rspf|tc-4l^ z=rlOaZ=3p4>5jh&ej+r)8qwt5lyoT$+|&DaEC(qEVYpK(FO#ntS)w;n)$5EBZgE(F zAZ~ToXeN=-zl(KT8;@dV+jpc+1MmlPmJwI3e@bGAcN}oVlP_hzio(E;5*>zwkvFK`I^LQfPm6sP(O;ck z3CPinqp-ee!mS`NAJkRRgAXqAc_dZ!HE0tbnb}xSGcT4)ShUG89|~-&5XPv!U2%## zK9F~KXwNe+j{44b`clOj;Z^RXnAi--tx$0GV_I1Dxy{d?;;#&ymKE{Df8;4Ak7Ip1 zo)*c+Ww=l~5B*xa67NpC4AHBPF;~&$F?k%tV^v~2QRi4@Q76#Q$BHvrk}G;1=h8~6 z^RA5<{*+P>R{H`An#DIp4tH&}c`rMBTPP}kpl%&|L81`SZ+*hP(WH_hIr+T=L4`)a z-M+&S&uoYIueVA+(;v^`{N!V%k7tk<3vsfgl^u))z9b=M~aKt?89(qpf}L9=TSmsD5>{dwG#s)1dj+ z-s1EXsT()awGkKVR#7t#Az@uJtV8uRc}vWE@)c;JMD!iNyo?n_(rCe=U1K?7-qgBI zm^{`$?^YhqJBJ$+U@PqNMygw~SGX73BV>ZL$-~-_s3MCf$bK>QxQ9mPTgcbhCOsl{ z4-wr8I)+oU8s~I7x_CE=5b0NV##i37B#FbR-aCz$0zB(mMo*BnYBY)Uu8&InR<#~C z`P#CM{{WS$_$U2qv!wopyE;$&O?Gsj_!{i#KU({#kE>R)+w(PR%NqS_UrIkEtzhzc zn&|X+*X7*rD_yrB+Gs|KaPC2{de=E6q??ZqZcZt$P|e|Ce!ZoT*;g|{!?mi3!h~Zt zIkRy;jI?qLO2WiesW}``y$9JMVjZxtrjhLS% z+O5FR%FMYk@uiw0b#CCXB4d<`=de}{DlrxIFxgiI;PRN}K*RQ~cSgLikNFD0r3`8} z`@*rqAaVG0ov}&a8*7?HID85G(346$7v z4Sl9%=u>CJe;wzd9Xfy5RtYc6w`g$lpvrEiFnZGN!BlM~jCvB>{{WcOWR=UY-fLwn z6P!?S@YTn#SPp7a?=52(mxHd#5$S7y_}66yMhvHT{3{&t1s_`G^e|&yKJl(|OJf?J z`5NZnK%@8T3y#z})fB_sIr>$`!`0bOPW_Sm*0DGSay=|@t`%H>{CTv^9drKx5r3K* zE|A?3?6LQH!-=s;;2UeCYbN9yE2CQ`_d6&%-e(>ymCZ_m+lbvy17~fAh^10)(Q2P& z%q|UL(T_CTH&yJ&KUHeOrLQ)8LuF%)!>K7HOx8bV$0NZMpwM;Gv?S9{{{U=%G!Yakjev5Hzu~SL(njkq z^X-&%45x9T+B0)4;rDA2;z8nE)Ycs?EBs0Qs_@FK<@gcDCMF*0=4+eLQ<}dF+Jw48 zb78Z)=GApfa(LTsUf}zR0I9TsHLrwJbAha_hvi8;XIXx+A4mDQt0y^b2rMfWksy!iYdHDS#p=LVcR2KwA9t$W zlqR+%*u%<<#94MI;3^ncY)cVb?iU%~1@(QVmR7>6Kmc%F$bz})Mf8UM0I&2bM=FL! z+AAD)Fq5j>C}{diT3w3XFJV>KsZE4dD|3?g+u7DdKBBci9-O-ROhf(Ecx72V4_#?v zR~}-uP{e){T?F8c4OpY+GVnF((jSwhbh)~7pT;Y(HKY(Jf@Yp0!`^5s`e9dGBISPKr| zh8HP`>`3&aib+}HlPzO<#JR}%Edx0c$GlpWSk2Le!K+h8AiA)z6nTbMP1iOwucd&o zu6DlPY|ArYQ9(dBFR2)zG{Y8^y}es%>MRr2;k{<7QCawXLGZ@)oU7>FCv|q_Fi`)t*0{ zYT$21tx()DlB_xZ0F&4M0L%XXk*xmL`kMa$`CsxipFBbFNm|SLy=y#c^Q~O zyL_!*bYJr|bZ{d24(d;Zfvp&y7_M&lL@o2Ew1yoVyDCU$xj~wG@U13U4{d)=q{}l{ z53LN1wj^SNve~qQ{{Ua;l8&L2?FNkcI4uVc4G1*Gy8Exg)~+^tZ99wGcN*zmQ*sTJ zhz@Qq8&D-V*W&eOm2N%6%+#_duy!O0j^|l558+puRgcuwfRTqAQpl$2!sDow;%qIl z^$IlZ%EvHICXl}k=^KLsQcL4vYAqH)tAKos6lmF(a{-a5CzT2rG63-EG01MmvZHCB z4!a)8je@ZoJm+h0^!|&nrqVPqb@!v;=|UN<@O@T7sQx3EqvqgNjrsWsorfxt7TSi@3|w12 zs{-rJb}tWF-tOC2>{_zxNPADm`Z&#^fRgWB%|&ytI?=Ubg}qmJ_OUzw9xIi7(;rr? zO&TzLDn>g?8jFkT9!_zyW0J2LCTR`4JB4D7J12><6s;~2t(DZ&nkyWL#JzAc%CUox zy=$+lqj{_sb{-(PS2+ZM*OC+Q(s?PU zQ^eP;9nR+*qG$@NyAAi#O)*xGmiDTIFi0MZvalMRnU zY;HCYv8YESqMwH2>C`iYye@7J3S^LUN!V^l19oH8QD17DUNtsV;%gJIL|!x&Ke>-* zooM(r;^pb27`!8GSfr{r+PTPFI{9@CEUkrBjIG)CSAeJlY({zda`PfSb@%6MQcjy2 zcMiWvKl;d^IMOKGEy9}wEU zv?0?j>AClc4;uzku#amC0K8ysVo9%_Kn?VHtA<~7y|x}U3sJLnLjr2rJWV0u*$UQ}s`yhXjiJYY z9do+ej1<|WI0Z)n`{va&A#95ou%#ntG%8qpYij2cRz##%yW(t4O)T0v^r$UvB8F1I z5}kYHe=erXam%>{0B-DiD7ys-bel;p4a@y2V@7QcQNPRFeJFu(;*GXeslg`nKG!`U zEH7&c=OH^bgS1w4&1liaG=|<6vs~I{&6#8S(2702^Km?bM}*p024})KB#L|BY{3G) z&!d7PJ*GS9bcSAvEbqkoe`bjEvJCzaOB6#BH9R=6U^cxoc%(dup`qEV{{Y1YIZwSGOZz- zz(qkH7aXQkZfaAvfv0z1^0rqShf-O{RU6G1v@ibv)B9FD!e3fOtT3*7*zG^l7L_%( zINB?m7ih-G7j+2q-F5Nrp(IWnPVwv~`jwS^E94bjX zOie}RPw!7V`@07dO)Q4)=H`K6(o3v)46{BJ6{({$FA~6|Ql?F|e_!JBV_qqnwBE$Au-|ZaiddwCNq|@z+PWo3hwDY>8p_tcA8+ia zbt2p5YoCyGQ6JRP`f_OgpUo6g(M6E+^R9kT(MR<>jVy-BqUN!_NINUSjM2c%gMBQB z#aVL%R8siVH8c&8+7AOuA(%cRYg&>P>}_vK%SKb`Nvp#Iah@u^)M>d=rfv!R`z8`_ zovL^m!7N9}jfR!wUTPjEX{Cxa!m2C>RgQTA3Nh%t41OgSZ!!!YJNori8!!#-hM9gza|NC_LRX zzIzAt7fP0Y8ttrU(?`X2ioqvM9|qbg$tD-@w%)wN4@VAPI+IIb+0|Y{=}b5pQJWsf ztFY;$&Ahz1bca4NC_ed4D*GH~UHg7QmgA#~S-DgFG5pq%>L_AhFUQ+`QZgSpow%^; zi^$;=Z!}XwOlQX1E8m&^f`jRejQAI21dT$d+&`&q$%0QB?W|FbeK@Y)(y$ZGAopIZ z&CI_C{{3OL+B!R-X^GwMR8VhGpRXyGM6p>7%9CqXJ1wN2=)T10!rJ80=;;vW6j(iB zTgfY$5e(aF9w(L^APrk#H0>A-j97z9h?kL#fZM_i!R=?6!NMM zIxC^a{`daX@6O*rLG;JP@GS(10w~-+s(M)L{x!(aZ5;E2GoNP+clB8SyS+ zuMDdGhpG}37{)=mkm}ebUHohR0Ff%9{5hJm31W7!+I@o@iWyGOYZP-bCx+LqhCOc) z+uXC1J9tw%E2Du5@!D%12YdVPShT0-Tf#CF5;O>-aQ>~sG-~6{v1qd3J{HVYCXLqK z`fRI7Y1iW0R*;w*;@SGvDBWFngMY#LY1AGYO(B!Op*l z{{XD@p$Oh2%CDnJAIY*civ-bpOIDK%!_OmDm0BbEis+|}zJGvS*eLU?=L~ozb!{3C zo7Nt6&%+*7b00J~yM!H0jBl?Qe(xdsCR!05a^2&+%n$`E*;oySx&}iiOxvf_b4}ev|%{Q6ZT{EO3 z^)-1hOnmYchA5Q>kp8~3k*kl0UcTp>KLNFLI!DQcYO2US6In&1NBM77=ikxA>s=y$ zm8+!Df8y1wvM0*E{@$Xp(&GJVEhaDdipo^~0F`9J_4YQ_ZojQ(JY)UBv(lpf0F{5` zA@r=Bo6&1h2^-eBJsB6&-m-O%@~wU5ktgN7f8((8Y^$Y}6Vp2BWO+W7(dftz5-ZL+UMA7+d|-_aOQ>tm3!(g=Pr<0Cjeo^{(4~ zwcBsjvM2Sdke~NfP=DN4We2NPw6KrXwe-KyD_Ut!~iT1 z0RRC40|f~I0RaI3000000TBQpF+ovbaevBA;d@KEt!aR1r>2mt{A0Y4%C z0FHfIRfhK}cxh(*+UuAog%+u%h(>gJ(c^FW45-rrM9;zG6f{vHQH+1!Dg{^!+5kZu z&e5<)yvkhD2m4Xytyg~vjCiomXUdgXSzhT@NrD=DM#N~zS0Go+yCM=BQ5 z!`2!TD#2j!?1qxI(y-$2(`SG3c_Fv7MW*S2sW^)^Fm_uBb{lPNZxCM|lp;vT{{Y?k z5haITPQ*Kk!CL4;zdpT~)<~WQW*)a3!@Y!2x?`)qw`2S{8PfE^;ft+cQGVQmcWu%z zwUCwdc zvezJC*VC{GmjO_aGIeRkI`TE3@t9hW9p`Ru*v<w3(r2mAl9~Ux_2Dk%KLT# zfF)>;$y@X5-M>KsYDam=|pBF-1^(s}m{IS5T^4zjw> zzJr;Ja1Vb1c`Bfu{lYkUR6=!+@M*yPMl^~SFl^pq{{U3S7)b$53usvJqoJ@uDXWYV#Ln}*Z)eK`7k71Dg z&vnQq5y8Jn?HJi^Sv0Qx0yfUoObiGh9ZASTH;YIfgY13Vq2@&Fqag7Uq9z z;-DcUX;d3{-%HHAFUI`Jw}Y91$38Wys#f10^kqidgL_00y|upSgK4>Za;UAdLi^8v zbe9J;sMak<4=}Cvz%-(ESiTulQl}F8=D!nXXX)$RKIKKRp@JaqGI@408y^iSCI}ZX zYB{MHbgR@oqt)yM07~UpdB3vD!34F;KL@X2KQI&nL0x%nhOCL>YIxBV?ZlqLiGT8qXEe+2?TE+&8TWsuUO z1w|GL&PKFcslKz1wMV$JZMbebic zhZlP-)n%`N0py@^Ua=nihyxPzkX%8a$G`XxM3pqNp$X|lqrMZ$r256q4Lm&}BadX} z_*{Iv;uN#$)}V@L@555;z!J~1eFH%*CV%p{Rf`p$&S-|E`5h=k%&_#+$_8jFDPRq~ zmJXB~F!u7d!#C@ciD)9xJ zKM`8CMh}Rr8O9!2Y}~vjdQom(WLKipbq@f@{);$Fu`Rf(*C-bca*Rc+ZTidpM#vL| z(W%flephHl4e|H7zwwDoxH+vxf#{RECrUe0b-$U=i6zJWIgx{7VYgzAAQ?WzN61#4 z4G-WbB|@UhSZ0xkF0NGTJS;ikSBP@I)3KorB0%xRoZbhJP93ALNC`ADN&GWTFbNW* z!+JM0DTlzAzEfoa$COC_0LBR{m(zc4%9V7c<~~|42RFsl{sSK%#W%nTx0hwXz_mwE zx{K{h=m@}(PQxWlV5^X^{kDYCG6-HfTh;fi`Z+Q- zQzejdW72CM6pI(AjqFsJ{qX&%;^zF`Ci;0Emz~g!)aj3XbEFe9$CK1I}&WF%>8e zPENFp;QmlirWk^TC$QPUrN179AyW`Ln$eSAZn)4g0}D`zfSxd{auYohVrKnqiuYL4)8H(;HB!M(ds_2u#H9PVtf+Go&uL85a6?r0BY@o{7o-0G4Z49Gt+YJT zyPMGYqjCrs2b$XO;;Aij^$kf7$oiVjSrn%>jNB!cN+lh{nK%`N*e`Q-EF4mCa(!qI zB?QP7gvaL3K~vN=(6OiTzy;F7l>-QiLfbzoz`+H0`nc1dQko)>0G39zV(KR==~F|I zHFh40!h?W^hYmOtM{{RH^sB-JRo}rkS ztqrsZ1p_0?gCGWND6|>2J7}tGHNUp3`b@0&R2xBtR}3gngCqD3aU2P+&=R&$1X;Pa zGYV4IEb*)ynEAM7aNUG8Qu)-MuJVEqWIx~}q+9#W%208zzjXw?2Cmo3VN>or-eW*y z@CzSx8<`}ZtrRF^OXK`h5)wwF)R))cNuD+CMpO;2uG$f^n#lR{QI9)$erUoiizcl^XrAP^r?RcT?!5pj&5`1q!W6FPolaD&hwpTVsrdew1bB+{z?f6BSvKqEjvu< zC_u}t^-rC^hv}m_tgzqaM$s`3N4;WP!)Eq>bm@vlR6=~>WB#>*Cfw|7^0=ibBAd}@ zUEEP-a&eow)*d`KuaD-rHG()bFhsh)MNAuBPoh@APxp^73<9i^=6)2FyhkM% znc)7yU!#>33{4x=We*R3CwyC}N{0wPF|wIqj8{L*sL6uBc>9uO?o}_73;n6!E%U6= z94yiGS6GOt?g11wE^6&`4-_N_G%7o=IZ&`J(9tN##tB_bPkF8wFqPHII?*+?6Y$hi zVklIXZ4PA*LC$X=<6wOnLTZ_}pN6+ZFQ>|c0x~{YQp~Z>-g;JYTi#C}>w+K!)v2DX z`We(|mt8dSXd;#~Eecah(M_!t8z)WA8JL!eqDf)?%{#wnExv)A?>EgHfbok@2w@5L6S*nEa(107LO|j`7MQ ze5wfvBS?Xz; zzY2a5{Hh)@NS;>%(>#HxlMOAZ{{TcksHWJlt0JMYRIwe5Jd*j=v)wT>G$Hq_Y+n(k zK+ZdcbltCDrS3AQ0G-?T8BjtawtaSDdJXe`YJ4CoyTEKKV4)k6DZME%6P<%f;&inb z+5uTXxD>lJuO~{AAa+$?n$2A2QhsNIaPVve2LYs-^{>fO+X0Qf`=QjLGI*3Z6A4F( zWIxS@+P0-`zgeh;`5!7cc(ms5rcFR)qmT77JwqH$SaYl$-D{A0`6a?jW;&B9;Htqq z-Ys7ZF)tNhDXa-NO~!QXPVnQfQSd&~>D*~SCB2O!3#e{;v6LCCQ*xYyoa%E`K5KNY z3O3GFg0p?8-!Vpgl7PG3pjmmnoS_v6!rW>!&gd!lm6CMgk)Z0fGQXWtkC0QDjNpZS3REIco?foEZJH*$5w)m!O zio?jl#{kV}R3uA~s|Fa}yy81#urLY{&Xob5 zebnI{s1NgMZf{213Tt_0>o`^PhM2XCOHPpoaK3Rt!*D4p?*&4L+1osf)8eHcXh_z# zkfq`5+f39Oxm>N<)wXMT6#7xtjAIp|W#UMUrHFf}w!lA1kI2d_`9RMk+ou{LrRT>R~wkMwql^(6pn#v@zQE zFMM%dokAIKl#a5S2W?=M!9kt{pZixX)D#%4hEz5!juG5CNNuUYTMR1>2P)j*^J~;g zLPWP3R(vvPni->B)^J|!4d+m+b1M*S)5M1{;Qe)=j0VQ%??j+HQ*EK~CJ1qb11>>a ze-t1Qk@Asn@?1`M^QOBXbDl@~4~@}`b02EC&57sr@+u;*7_D3Nr{lV!RoBrxAolW$ z6)s2luqY)lg@2Lo*!0#!{wNL&kwE|hL$J!55wfg6jGEbLWS5m-Db^mBh_g^2T-i z9==6j9yr%=S6MOeynn^B9)(9FoG0!yG=?r0cK8rx9Nj8D2rrEm)`LFK{hf|Vj|j)w zxg_lS@==_X21zg3*0HNVuVRXV-@Of$Cjmk5DUO+wav78aFmG0>Wm<(7;6pCQ9WfcT zm}!q;tH4D}r4j_$SPGNG@t5H!Ap2BM)*0FHK(XZ#J`o@WnHDYO+0@-oyBwW7oGfM@ z14J@kV$bG?un7}C=H3G$Gr!Fr#lqEN0YVk7X0{zacVn>4r>>$NxP_k9bev?5j z9L3hJg9G{@nUzMY#J>~Um_^8a)D5p<4q#%~ltd|Gr7O9OVx?QotxR{6EM1Ezdo8*4 zdN{pc90HO==GsMo7+$TS)W=#L;?ZJ+Wm_Cl3sQcn5x(G{V%sH(#KSaWL{nPakPb4De1+h%Nu2Vdrp|U`xdX-=UJe*X?**X5(QiCMn zG5%H;>%8UGq->&W#jFTCdmepyz~2$rlgY=!BicR6?Ts z!^z$vT7C*K%)|93g`_P8CQSrlk~wJO zIgMcr-OFDe1P~BJ8=5r#04b37K1Ep5)N7*bKtc)F!0cNJKH+I~0}+?3(1UxLk7bl_ z=GL`PHUftVS%|eDfFfM`L4Z|}ug2A@yvZ7s&bI1QKAK#pUrybYV%|{Jl1610W4Lst zG-02?3TSdFRJ&K1H3q>zWMkxxXlQCsgo(RGT*_r+O|kw?9>G1<^{foTdCl>`5(4W$ z@0*jIN3p=r@DvysjMhaIn~m6L0Dyqr1KY+0;O+Gw`5-3NlUFFCi&zo6v1w zIso+vXxA>xIZ&CD-pwJnP)LyHO%^FLxe5oV1tVFZi1achqe8)WXaInMw|QvmGwYsK z?b{&7;Hez?E+&2m!vYb^ss8{6(<>Cw4uc;h6Y>zfVPlgnd;b6;W@2|Z9{j2XV9np_ z$55f1$N_~b+m+FEQf zL)7>TTm-9?W|kSiK4=+$fHLxa$g_h{wlP0B>En;Hghy;ZONk(b`qh4Hl)P>N}9e}01u~e zr)IJ;NBTS&p#W+$zAz)BSWTE|spOFJ7HvP;yF>VKJ$io_l43!>F0{~d$#?aSA&VF& z{aCCl5#itgwo|h`F~Z*22b45q;WkyR+*3TAD&^cuZ9K5TPzr1k%*gyva03gK43gtc zfFrF1zDBsJEfZ@QE(5&wO$Y61-JgVHB7MV1VmSBo$=#Y9%237EiX0 zE&@2w5P}kBRKV-IMS7(fh0d~igu$Ba%7Fk31H6S+0KH+=l98nxe7r8fKE@R4Yryb+ zJQ0H&Pb{5kVu20OvqL1FM)PcYnr^wk<{#rjy6yh};;%9!TyW_gJg&mKm-UKR$WCS* zJO@FQ*k@6vne=&S;V^8gsS0^TF4kjftA!;l^tf-Rmw;z>G(z@DDKBD)A#QWMJy1~< z8Mwu+;%02i7b-O?3{p_M$rCXr2Bt|ad4@TvGR@ZfXeFek>mLSl>JuKcQxm`oc_T;PGM8MMr+Ig490ksF0G)*5bD zPHLr0w92BS4VnNYcF~jOAk%bwdQrrNGhELHCGNXlbsk6uN88CxLlAGd=+y4rxgq>Q zQw~Z~zZ&5E_~^_bQy*@ltMT9(Sn=2nwFhSw^Im3F*0q=Bp_6u#e3DFB)s4CPDrvYg zR<}oKNL6s=9ZKAE+32OLd{^MCLD9{lBLgF~2NG-iYQwjK%}=;aUqAKc5J+w`$gLTx z`lcEf6EncXKtZ-GSH3tQx019;fbeh9YDRLV=kNHCVTB$o`VAX~5#s%L@y<9n#-~i7Ofw7+w-oM1# z2yh~sk2xQHI$oeldk3RmkVC+=y$AjMeC4cJa9Ek?SitG4C8qB2u2y~9z@mz zckw#>sM}$7nt9TXzckSZGrfO$5EA3yYbUUK)Q;Lv+Ib>kTZ-ZBSNc?);Scca#|MxK z$xfPh)>%T_ROA%8r~BX$O3Q$~L7Wxw^K76Dx!foWhyq+^vY?rdzOd+GYL%^CA#jsZ zT`TA+0}c;04`)6XJzBo@kj0(cB_yx>-}m2p_w3)5f-S_64}sj93b7Ja&{SJBZtRnrrBTw)D(Kv}UCX#T>vH=uXbWOSgf zPx%i7i_2Dlb+Yy~Ucn((Cnp2F6>2TT%d1pskdxS*VcK3+q8e_|s%4OOvsqgKCT8nL zxn{bEMn*QJJr}{#{G!+69Bx&Zh%XLw#=+Yc=kmyD<*~+E2!A-StC>s^ykSW(R}%(D zIGLApm>;jPC}BuFFmkO=i;*FSI{NUY`p3ZEN&SX?H~LnOVZDt#5YyK;Vpb#n8>Kk?cCrI~8+!u`*6^*w}Z(eQ2aqBMC zAhqa!O`XR<*L4AWE$QVhgP_0+1-~vXe$FeS5CP&Ii8@3j#>Xo8e z#EZ0uIRrI86>$A!u&V{9XYt80I{8tQ*5OsnZ3el8fp)zjP@f>l-;l~OJ zQPxvGa+wo689oehFo@@V)?-t5jtxnW;B{^+XES{}%(*MB6#?MLA)cT!Vd8pMXIC`M z=cGcxliLK)P-YYE`dJNc6}Nn;n3 z!LhQL$$sYbp`=|7eip}r34cHaVl$5Fb|=;Fo9)K|f1jK;cfI!DvgcVk+23U$S(M0C zUXa!=s99l6DGx&~r)F1O*5uo+hJqFl-I@&!EpLGWJ`U7fYOlR`NGMpD2kq7XT7z37 z-7_04mIOB2wk)zIPk+j)L7YlWKs(b(p{WS^=rSt_hazn9<+RL(W@-+Q)sGU-YUNtbZP92#c92umBQEUU{4 z)j}}#8HJPE6|I$1ri}T5Ay2S;KALEFjG{xbO~GQ6RB?9zLZ-n;sP`)(5jnsniw~!Z zX*>wadnUHM;OWV;WNVJC6AY*_3C`B|n#SiKNoH>CYs!*_2U=%SBk(9Decnn~Lx!_$ zrFD&it2Hc#SoY(%TEpmQC>zc;+4Oa;YWtE!W=#A!z8QrgMCFNvDQ(=LPO>t4vwTMZ z12=zroDp`<%q^jClaED4B`F{N&0LUMy+T6^Wpb2&`0R0w#Cyi5X>e6|CmuaAp)r}z zFQ%iEBSyg=M6&xWTh}$B%4W)beB7G zQtE&>8A!mG5=iMZ(ala4YNrm!+0o))DPfNsCKx3|+vrJ~kR5^8)RB>@!lxV8Qc0Yk zr3t1_qe)U4xgdbvl3zu`Emtx^M!w;4@RoDhN7JHy?#rvGIgpm+`H=IM~|;;5T#?e zo(!Oh%$C!a{QUVq&t8{7GGt)ha#Qxe-*^j}xGSO%gDX5)u=)gUmGzKw$|XK>#mv^5 zt|o9-Ik6N#OgujqAc;^-6P!H#g7}fFc9ijL<5YhM3NjW1t7{s5VU-b7V;&d+jFyMz za&uqV0vh2@Ku485QLffaV%SN0z37QR(dKCJXp5oDG5(@Y$;8FPwXVk8ie>@Q!j*J; zFSsve6t=u6hh^9*Jgw~9Xaw>Y`Xu}^&``}T8b=T^3>q(XeVsdKr&XDFjD;AlI;L#6 zLw_g9`oa|vLN<*cg#*@JkZW9-Ajmkt6jxkKZ>}Q4V=Au4IAn|I+|xK?J9;RRrGIeQcK#Y`ic#Cc*8*w;8}p9o%9F8 zuC3-wGL&PuUd-cpCDL+Lz*fD%eQQRu{V6kr%xEPoIQ%VGywI#XvWN~prVA9Q&UNg* z_Q>f)!8IpLeu7`Q&8E1tH7L$*iHNU7c@7P=1TVcrU<`@NXtOHI9iFRwi0S$|GYT7? zejawd%Q|Li(I;UjPm4n8LKgIVLS4RL2H+sbH2=g}nEQd9qsx zR;ARK)pP6~=9~6=9LS{tt=gSR2_f7oKJ<{bC)p-RtRMXq&l}k0P>@jzYFO1q`(N*l zx*V&D-CkkT7*J-lq^2L(L^Xt0E9}yP{?l61w>XlR3`Bjmg~C8AVwo?E09FZK1Uo@) zYPHGe?!nUE?wecZi~olhUJ8tN!onz=h8HI*i6^KIt}2`KBBq$hzNX>#~TwP1{GMvA~Y1vtFl z4B5N|20(|-#12SWtP)~^>G`L|%iy0MjB6nyrE62ib5S$G6L^tm#@hS@Py>CC`S*OW z5HDMfyuDh^fNVarTL>K3E6`4eQR|sILb7OJc;Im)JTcNM8q{fT$naZ~zB3tB%XHbR zgnA%DDi?;OD^J?e+hNlF(1h5D0V6NZX=oLdhA!`9!q*d2DK@FM5HKo- z*v{dY1~GG&IWd#F7t zuCRw~QR01M6{=2-|77h5Bj3%-e_+bGzenCYsaJJQ=bU2Vb z?F0K(f5>=D)r!)mh^XUwQ3Ck6AqQZkmj24ZCozazvFY|GksYxcMVj_dd`i4n^7i~j z9F;<57icYsVF&tY46*J}*CPc^JG)F{2z+gO`78dfYxy6WWQR;_9g~`h#w2jKnv-w_ zw%?K>$l*A?FHEiK&U0`LQBd{WKT)NoQ>4vzep#O}A#=%Gup=FXi`f@T7|u8tHA-Ql ztGfz%o+1xBu{^sMjj_*jSoII2#(?RFs7$304qV`g_n_!h2aM@+7Zy~C7;;lKq-02S zpDBHg5L+~zc^&d8f%bdvxAb*?)p$n7&L}+%lfw)dr7P*Sgd~5jj4?zEwQaGyS-)eo zOKj$yMktE7Q!W4dLrFJ1B>Hl3rPuD=EfPF}5rwuCA%y3|#8Gfd{uS{{UbLvt*j+<+ z^7KagQDrIAxFT8cQw6TdEX3IG0n@47%ObY^9ba8;DW}d9xmt(}t14F6+v4qtM~QY6 zi1ozrr(gjFPPo`?#(3y}&gxXO0TxsCfQs(RQ-$w){@b+Pj1+T3-rcc~2+`0vI|{P7 zU*wR-*k6n1A@8&f<*##?VIR%0$)&U?t;>oQlF=r8p5;I>XxIAqwEloyABFeaO~w68 zISvW3o!SORJ;m71+$rQxlr!%@b729!w^N~)xP+ab;S;#R(I)KdQNP1${S!dzZH?kQ=bxD3pGw=*e>UAD=$+g z*|{3H;%s?VgQ*8S%2{$a#qcOo!P3Qbko3{rsM*XF-&pEJ8w%oqOBj@xpP>QJow8?v z2eTC=5*kbh6r@1+d#2yv<(p!NzBL(H=VnfweN}D1k~cOnxozNqu5PgxRlYU}KOn5?db3kF8{%)5aWe3_|c7eMwuUVCo)a`TgX!3ww25A z7$QNbwF2}xS~!P*kqkwM2|hT936xgput9&_=%aM$TRrPkWxM#6>zCS_#4$@|OJ#7$ zyowK$%L4pyahpT(0xezov$Kt?aHtJD9MGp#Fs4GLWVVOKu$chq`FQ4_ra~a9Fu7eO zC!)h;ypwgqcWR#-X5&D)YEDlYn}s(~@n+^y!HEv|!859W9goGW;EDKo7a&Dur$0TbLv@}?Ccq+ zNL3FbGf<7^r-@J;=`Izo((n< zisjjg2yeuD9Z(=yJ~UjOjE;F(qHQD~TAj5oTC}@1cKP8kws!mXtx0IuOV2Ur6w2l5O`A)*BqV|{!c;#T3!6MHiUq>D9+J$&*vmGQ;KTr9 za}Y|^dSr`&>NWY!`JntXFnicKX0FdN<6$ULdi&>L$XdePhS6;W?Qgg~3y>&E!+S>& zc=+JaM+_j3Vt?&6u$W)dELbg5WHp6dsK~kQ+9Bnt%&!+7>byp=HX&rsdarQ;^po;e zB(KfJ5I}&TJTPMaq`0)>k(>s;K?6C;cjx9!4?!p%y-d@!u%Fte=jOs4DBI=wg(}(~ z@mNllDMU51GEWWHbR#TlcT%d$jeF>5o-@a=*wQ+6zZCSX{qovRtwd;& zWNCai5fRJ(Xj?eO(P$^*Najn|wM=BpP7PkHCtweEdTe3a z%)_b5O{}{pebXHS>>__GeqLX1zV>C7!z1+a>(oUW3w&s6EUMmMmSA;t z2{xT6JPm~X5);9-c*`x4=Id)|$Xe=-W*VC7OdlZuc}DYBR6FHu-QQCeznrb>jNfqE zWzBl;?|$EUO8^`yVbpWu2;ywikWI%6dS@&;qS&~rbTq+6P=sb}{zGVI=swPcv+!d8 zZrmVUOExbM(nnPss<%Ag~JGCv)bT_=kp%0|yCNWsa>2 z6V72FzE8BsyS8T_!XAUx>5O5$CNtBNIX;bM>T3a~g$EBN zGV;A+#-!sF+eTRDmD;KqBxHRe;Sx*1A{M?K9RPO_?H-C6eo(SxkewGr8>M_8D$O*! z<%B^;BeIlHQ}@g3$UYs`(+DSC4u^t)jfihqVcaNt!8WICGzyW`tk=c#ckNpxLU%`{ z@t1p$mIY)qM95R*uf-#1=ub4hq%^fP=r&|$YDf{_UBYv58!^T6$|@l z(5{@0g4*VhXbn#Q1&VE=!&Mub%Lt%Gp;e-jHqH-}k|3J2?mge#*mWD!BEYUWQU#Zwp{ea(z zLPg)NREY#oLky&E_VpF&Q#8sItGc(y9Rw8yHd&g! z&rWQM_w5ZT8Kpw83-!)56sZDD=6xyfe{h`CS~X-PvQ8EOf~>QmD>yP@Ng_THnA|Yw z-0Rp{`QCH<##;|cEFxo zG1f0Lp-4r!3W}}^(y93TS^~1T2AivG373r(cvq*U=Zk|9ld{!KSj4HuVr~+{5+)P- zL>y3Whfcxcw^1>LmmqR86*gSHXh(tppe39kbp_zED<3ZzT#WnF#KwnbZDo38)K;f6 z*wqlD4|xrkA3kJS4wfOfZlm~s*4F#bwB}Bg%{SD}!0DY*4q{TK;WP~GD;|Zjn+!kf ziZEfzcX;-Eniv0cRqD1X&k}pvQDNO)>3{e7E0rI=I$ZDitek0{v)4sIH-yt^!A<9@ z73)vU+42pV>4wU1I9ZFW{5SI-x%DT$Vj?e%=IU=s`~>vNL=|yLhqN9BV7%9r07FF_ zP5BJHrR6N}jvEXDxTq8p#pRc=ZS^B+S8y*&h(+Cvy2|EwCYH+RI2MfHL1Yb#HBc=K zS@kPHdfeSf1)G5vX%<_?z05-sBxWJ+TPNtO+g;O&Q^4D4c}#Oe95PP`0Qs9c^;f&~ z+fv*YM93U~?@mPfqL=2O&5!n}q$|bJ`#p=(@7Q>G z-uGwLl?d`fa|5YYhdB^wZoF1vo{oQyiNd;c^#wH? zL&C}Uhz|BE9S2xWKy)yoq=;dSH?;pZ|uX-NoIO)3;+El^Q3bKv#a%Vt}gFb zbV^%Y`8V?-+E<01g@9nE^v^3o<3)^@$qwho-zY%qi&@+UzCO~DQqqX|09e!;_*sNKMEFEWDmi=Ht)SY3PR~bk0^Z%rueBvYrRd+h%k}vaU5zt*j5@Cb ztu`N5#Sc?+p4PtUZ(JkRq?>I53P(yoi@szF5voU4uvZySae$VwS4G6S@A&f;$e1x! zf>hJKc3N`Hw0rG=eIfV))aA~gkB{Co;#ZavL%Q?Yi}*gHE*Y)2tU~tL#IZWFnOQ-9 zjv={uBoknnt~MzqF7?_bgG?^vlYHNqfLjnuI7a&INA{JPsLxD~CXxLpg-T;x8=Xf& zOS~Vq#?S-iw@IOA;A^ubRrLgA$adnewQE!l`C1gu(ZP|1ZK2QSq2R{Dosr@r3LTa;ByY^;q6vphX=>KXy29K82)(ohW50@c@%w%7#w zB%(dWD@mxOBs8}1=hE?C48;vYZt&>QIlbBKolDQ@Nb4fO<%Xaj8ZV3|>Y)0DA$7L} zA!e>hAoX_5oH$*Mv@W;RZiJ6#GS_i`JjUH8D5q-axA)CO%miW$eO47vXBO+lFu3^% z2z@01iXNj4)%9RHx&HOlD|{0;D9tx_hb{|<^H?lCY^|T;SeEn0lVl#I1fLU%l7}C8hzWj_oW&Alt!>XR4is60K+0 zkH&tULa4wn1ic(Nn%$-Wf9d6yZ;fVQyz;HX3b!zp(}8@xp!dLwF2LAuJy0c@Wo;lv z@%7qBIZ%_BA!wR#*|dYOTZ|b8;U49BljSw`wK}9ul69Wn2i#?)=45C5#CW;-pm>br zZu6Jbu9Hc6XWx*!w$s(V(WFr6^}LGmVeGUGZ zM@l`2FqPQRMA@BnH`?w*KovX-ZgkwO89j*ADJ^0va9@c*PP(bSId~Zx;Qe80UPSjM z>vDnZi$98sVQ_;C*i==Ms$Y_=y?WVwfCrq#-hmQ?U>5%Xf;r6T{2J?akWqhefQyBe zY)jN8W1phz3Oz?!ATy_yJQnj-mHp0?vZm~~*CM__eZA_uC){{e|4kVH9$BT(5vr(a zOu-iC?YIfJ6m-~Upsmr+NjE1wR}Nuy#zs~&5|M-vEKBxWhKf(Ij~cX_rs=-oz$2AhP05;cV=3$j4*8)CGgzMTp6!@->kas_~ zbD?Lpv?G?m1JVT>Gk4Rn33hXqM02JwYHUUlHZ1ZWE#%I4@fgxq>Ck#@L~aH8u_U}i z5s~oDx4meQ(b;AxmAe>W6LQeXN!tpbg|SAHYrIQn793noFNXLxAD#G5dT0$2cLqN0 z#D8mAm9**{C^)S!M>JpkW^++^nRklKDa$~@owI#|vnk#1z=D_Bv z5FGqMm_nCYDa|3ybO&yhu^P=dcZdK_MG2R@JJVz&)w7!e9nxf(`Fa&!y%cfps1R@(?AP&U^*;xpzm@ zJQ!S!f%s8T@)bfe{k}+-3Vm#MjZKm|O`CuY+KcgOYTy)jQv#J8Dz8qZtR8)bt6+x> z@HuqugCLy3kyAE(;|qP+P13bw#dukNH-*tcM}dhw1{5lgj=>xuXgAJYiCLmZy)h^F z(DbOQ2hM}cq3I)6X8La1hXDVggiyI^X+J#@txbW0QDE96ZsG2DISvD~wN_xa`^%Zs zCWY5D<@Cy=n~0S#r3}gj=|U}td|YoYqg>lEUd?f;aTq8!-j~t{@b=PvIhvSi)tGWu zvJ*N7Pk7TVgKv%6cStaQqy-YKoKSrfyiz&47=!F*@IX3Sq^xcpb-TB&o$6 zg{g8cou*P(w*01)Tcy{LigXCN6w$H6)Bq3r?Lyf1PppNA-GtHY@;?C|S=7)fB<0y8hn2;@$ zARMNtw|_h<=4`CdXfDQWukyHB2pLSmeo?As(X-qVZGj!mkK(1GbL4LJT5pIR(h2tD zo1~LEqof1>-fSY|nYJ~6gjG&`ZRIMa8%_oaes3Kn^GqhRj5pq3+nq-Y64iUGd=49B zm^h3pJV{Y6*OUi|z()J>WEV-lj!jM9-nF^K9r2}#v`gVkC5M01S~llYTDAtgY$wcX z-;Xy|ZN-mE!DNwal={}0-A5Mh(d@IyMi+=srZRO(>G_Z$ zcms;c1xeI5F&ufuSgxTV9^(@s`P3&Oa41KIX;C1m>$u}_*hcCQF^OYifo;-gMkOW5 zj`ML)&=ToXWhP8fsJk5*vCsm+qjtJ&_z7?0?e4_eOU&iL9d^E!3YlG2ioGC`z*zBX zjd90&5g~)LOtU_q-ZWNBfMA73&_F?7ykVUq(-fgD*l&@k`ykt`3Dv+|GRm`>W6!fTaDRZ}j|`3WdBt;&5{d>$oGgAoOp zu8+(?cn^`1G0oZ(`$I&H8TFCpP>^nndh+&jSn`n?Ltml6YF7yq{@WyZaBv8+#{ps8 z>U-r-@3D26Tv~CVRbJ(}v>REW>>Vj(>EurN z3R#iVp8)?3a0k2{OwagZ@{4@Xp9t$(lB@9!aUZsIS{u4cjH7?xop*rV<_ElcXrThP zogp>fBfcp<+-bGcyK6eqVpBO%WwyF2dbx~B7!T-QWS=T6pjJcbe8d0+96^Kt1Aqe{ zK!Hh6&qn$FTjD;8;JC=|Cd5)h;F{KHhNzuzEdB}7?l4D<$qox1_tt- zLGhI%@l8l@qs#p#!T(+|0GJ5mJ4N9GqEP#dpel{B|F?SmS2*|CDCSxqUxgUom>fMA z0MdRG5#qm_m0n*;O^uc`k<@NZWCx-|M^(&w~^jLFQC#A{5S9) zBWB4xU5&8$2utjTo8cD<;qWA25Owkwle5l0OrB4F9Z!FE0`e-*Y2S4?(2t(ofcyD1du6YpC$e_8c36;)b{HL8UqH%p0Tjxke#hO zi635;{K0B~u)mx5-@{4t6ZRBjgpSw0vBWhhP2kCVV85#X;_wAD%sk6w{w?vB+*8mn z0K$Se^!0&tQ_B&+LNJvrIN4l9hiub)c?c|K3nN;U+^0R>alWspwGO1V~w+c0tu%Jhy;IO z89mMh*ni2QSNezC6O`Yz0LDCHgS|B7Cs%8)<{d&7jDL%!Zay0&_=V-O{|6TMD-Y_Z zr#8>nEBjT8MQuxWqi@!$){@VnX+YS2x9mSSoPl%({=(AmUClmx|K6zYf0nxHDe#P? zZT_S4ZzZ2~_$^nC?@ay~`*1bYa(U#6IcnXy>Ur^85r%3Ex!<;WMm---qJObM;R6ai zVc{(KuNL^PR;}S*K3uJOUQU8P88B$X^!IJ}uQmdr{KEQoadv66*`rLB0 z>UuS^4l49r^|qm+wSKB2z5UbicY5wu=k7G#i_Rc2gjP=w5R@vl`L9;pW*5K05~o+q zv4Ua|jsAlC3;X1zXlr9(H07k|h5Tf+LBN)0I7sf<(tJ5N*43&b|J<8jDCU1+&z|}u zoTfal_Ntpw|7(_|imR2SKUhlwd;VzrhpR~1+TmwC{~q(6G=GxI2kZ_Uod`gMfri%} zvY$@-gY`^Y&J`T>ya$o}x@FErKrZ5$$*<${>}`PHEEEzH{m#=?`|@)B;V&@W%oZqb zJIc3?@{9U+xBdtIi5Kcw58@rjOy5Bf>K^{8u_v89h!JQ74rpYR`-K=H_?PHWz^~J% zw)+7ReKqrN0rNNdtjzOj#p`SoH2L}k{@0KWD*0WFD$w{*$`4ZV$lCKSwam_Q_3vPXCcMf0sPn;VpETSpSqOMFA@OGtlxW{R8#P;S}ka75OCS zQd=0@lga+zPk%gJEB}q5e+K;P_vD*{^!~@C=gCvolOdm)@o&iA`D{@65P&rjq}UVL-^uqWf7%1; zS=^wm{df64^1v?8V+N)lh{GVCuD|CukB<2&u7 z2Sz{LG{1Y_2(x|^0U#2DWqvm1{|fH`-N$?r!%w{@TL#1#)SC3@!{p2od}qV|Q_z1f z;TDM^X6pvYr5EmHuIeNQO{34+K|=XXLG}H=>I}MC5sY~@hzOdG0p)O6y4gWCe}WA0 zjsL%ZPh^9zY;quaf|F59wvp!^$UXT~^8b~}NB^F4gCLtou=f9dz+e0cm^(4g5VjOQJ0?z(-AShNS9UACi91H>yl)#@J$pHW`02&J_i7+v@eX5A)COkr+L7p_e`>YeRX@#BIrDSr5 z9jud6Ea<}Hu57&(cxjtNii;`R4nD;%iga!ZUwr#WF(kB?y18Wy)&ZHI4*H4ww0$#0av`S?^!duA-2 z+006IvmxH0ONM>!!-3M~+vRPkV=Ff;1`QF;y#^Hkbr@6?Sg*s`=G#4D*D*{r_X8C* zx~AX=wv`G`5o6>R%6CcOb6h*RFIk7w%uRckI1-&=3}G`F)T^e{0|nXaitgmxQq#0o zn468PRY^7L0pz#iAUG2%4M`fNJ~VkWfAEYi zJh=vsiXiB4&|?ns!LE#Ldw0G@ATqJP6td#8UYJ%p2071#0w-`&9{VNTYHC$N zU}nXJ-62CR)2N8*z6?#IaS(Km?6$UAvZ%yN8g5CQTWj;RaS-j5kKmyR{Ee7+pyONw zfO@a^AS;TjwO*|J&@v*%V&6Un2^vG_(*%ZQ=g`YgS#2Nb1ccJ+dh}p1wi5VuC(s}K z76;=e;BWR4l(4j47asSDJ7vCHo1i7!KU9g|QX%R#QlHCczu1t0wZra_a=hIq4p@0r zSqdG@Tt~j<*zqon27GY2l|mp9V~!ZO*Nk45Ix9&}3(G>`^J7iB4%+u!l6VXvDVUNm zB1PI1R0WO@y9{dX2;8Y`(v(y4%cE9(=~&R0sw_8?_E#_0-Z*)F*Rv<8VLM12XhpKD z>za@3Ny`k!^I3H8jEv+GfQz)|0!2ZH#O!C42NMr&jJllnH)A-a!eWAS7O7~4vSvhoesOnR?PdoN>SN;Bvp7Bya#gzVib?1^1Y}>#o(s2~jleq{4%6D8Bp%6s#C&{N8gXo>XbGOrlJ9 z2#s-VKL5MXKGtA2jvkT>C_1mTa+AnM9zQFQ@-GB>yb^Mycr7Gpy2XmrsxO>^X^#kV z75WEwd^iIaNL65Y8KSl(lO~rFs2zWd$QQ_;)`txqAShUVlgUHUS!zQ+fq*C{ zLFM)>H)js8dA~h%0aZueo+5h{ti&=VfathQVaOa~-zJT#o#gnF2i+z zeA|}^;r46<3yXQBb;CXLkZ!XlSOOo&N~%)!_rszb(U$N0+#^2Kq2dLR8v7(E$cwHl zdW+LuOL=1)F{m&fFsxcn;SKWTr6zC7p29Qie38wJqugr#Y6K(G8HW&Jc^~pX`|8_I z02^C7*wNi>FN9P(lAF_lc|oQotY?UtaOP=PFx?sJXj#d4i02C=XkJwW`0g~=s z+v{~9p#7hV>^xi1=)xiw^IE9c2wgj)b260Mi|6mXTmofvywmN<_g(CSk+MsoK{FXI zK1|h@L|*bv*2=eM?y{Ue7%RF|B{t-D2G;@QM7Zy2ynmF4iD(<=`@Y&SBHU0i9v2K;!J47XW& z7j<_D(IqiyExGqeeo*Q!4 zQk(EadnY$|S$!)%XgdveYgSNz50+z{yW+0OBl{8Y>hZxS@^*A+c4>`$>5AzKH{%|S zS40M;b38V#MLOQ#1p)#}Upu#2zW?0=sIIR2b2Ye;YF`?L0#%|mTJXa%* zSA(iJiI||?9Ngy{d+0y%6kvN?JP4Jd?7w)eQMV^#BM^W9^N92l@F#dY1n38v4?a6t zu)l-HGiO$y5(~AL&nI2;*~Y9L=5rRQK+g0HYEI5BG1+dd4UGnuUHV1Az$dmA?zIv+RLC1 z%Nhwo=2DE34uDL1LCb7NGHRp1Vr9}vtSeZeSkWjuG3RE>-9@LIkF9qTYw8j%1U*!T!?Kf%17(>(DxkU;FancQ`!~VCM)j#Z(R~n`{#+bMqJg z0OgZID;X+`xih|ckVR59_Qn;bb@`Retp2w_Ve> zbf$6SLs~D9_Y|{kPb0lzN^9~oTQ`MFADzniMElhAi?km7+E}H?NMFtxJiwK(9!U?f zc%ljT<#x2dUwh`w^Titti)ViW5{}CkD2* z);hggFrFH9bf8njT$xzu`#c>bu5IYjh#!iz3J^|<*#6O%eOC_ zk}4VxU#JH+Lm(I)TmvTW_dg?-TOMO}7Lye{(35dGkuH0{jHkV}AAgXIaabX~zI#t$ zn>t3Azw5XC89?d8c`qpJt70@DK-C*o`5z|$LU<+oz1}&e?Y_*Z z=a4Pmx^AJ-vpi@nFy4=&=IRX3nwfB9;~Xl9QX!Bv%9&AP>bh8)X?^o4!7<~{M~z6w zc~`q?eq)1;8XH0-YtBL%Yp0(>Pl1V6(HfgMmiA7>nnjH*sm^Y7dmsQu#V11p)yIm` zYs7}gOv2E?rw^I1LIj!NGH6$r9$? zt>bMJfW6U!z%3tVfi}ucW-vNQCuv@&V4quURn^q4H2&Z@!wp3o`o7}ii#u>2MX_7%+E<0fK$^BV_KwGl z`q(}&@z^1`qQIoz!eEua8`3n*BC|0iv?Qb!D)B*JMBxO3?m~9%BOt_BvT)2P1t)D! zc=oUs17`lRp*zx3@l3i`g{VB_lMS`i~0L7;|BI^>!w%SUcB0t!EsnG zl_Y)|f0{bGsPR&syW9=4`UN-AbXI~L*t?Mrvm7KXZ1XqiM@)Vsi4pve7qr-Op>G?U zn=YdqF_d2`an?ZPUF(82_Ru%Mb$kgg_T5pZt@4nPOi-LTzGCsRV+TKzvES3)HJppa zW`5dJi)z+~x;A!*YSM2=vp~w}^O?%n~YJ5WD`Y@ILUR!Z!+o2yF;4;jvJ`?%$ zI)_COMqhu@L#BJ&S(Q?R^L}@xt2&QQku=tt7 z`lb7*N&I)xwFn67xyiEeK~jKplTz1!obzM%Zhkhxx@jf0;KuiTI6;260U&xa77m(Z znz>sW&DAQUglu%-HM)Ivl*|{f)odIadivYl(5ow~T>Ybie z6JcR5DjIkY0~bXYd40|AbgrA=#+#|%1mpHT)Svd_1GKZ$C`$P~Ng^y{Xuph{onHQ% z%f&EJZC8rU;E`eQM6l#t)$6Tp{1em|E5S_*Y;b_#J1CxTOs(QX?(d@8PLJO$(`iW= zSq06Dy}tO3acZ-i!M8HJ&S80UOF9?Qaq3QR5O!K-Jbs$QDtlv%jY7xw$-1Bzw9(Wc% z>!BY;ZyHsjApsi4BeD@0^)2uSYN>5Rb_a74FKcOn)LvQpBjYTyNF0%@?RjB{`l98| zcHEMcXSmXoeTbNM<>L0b=u87^0)OUO+E$<6rkYfh_4*<46Nxy}+jM1+V2?pc- zDxg&Wy0x52+#z3#sQ#S&_*!+u&LBB>WIH!d4=qwbcp zZL~V!{{R!SzD5K4;-`GpG3Rq^y6sF2n={1u8@g{s!lEEKcX@vqi-;Ao7YKBd!$Q&R zgryMQUz!hPDgYo4i@pOz0Z8g+M>)fZ){Bu57SIi^{-+?98>~?m%K{!sHG?4l-D$+CC4KFmWJzUyE>285 zev4j@{i98jD=4tsf5R;J1v4>ccm9-?V*daM)8G$kjLFou2f>5*2~^i8&8)1K?MiGy zCxfgP%?4Un$^wQ3u~^a~MM%@bOhinga{@awSwKPt&A}@jsp=DxVL|<>Ur?l{z$=;` z4Piwl3vnRdH4f3|Obe6`dI-v!R=9B*^=&j^#V!^}O30M#@hMXX>>=8(Q*f~DAtbXF ztJk0iRx+0D%8jE;N0X<-Kfx^7k-*tsiHC|nr6`i2mjB-#|;rp>Sus>{5O~f`}!cGcDqp zN65nfLuMb~$L7fb3I3Ot4ok5jHhLOAC8#Ni7TV&`Kl0z<5UwSe7Q6eMI@Q~(CP})! z{{SX}7{-7f_J1+ZMbmkjhi$Ag%7UYSP7H&1KXn$}$OJE!5#Q*YfChx0aCfUiXk!Bk zV_d}tZz|1=_8pL zCpNk!^>vIa&`huGLB+rz0{~Q@SnZMhQ7B27{6+ZEvAXR^7M!sw#vwhZ5(#k` zAJ4Wlumc~h$;m>qb`U@G3eRyjG(YM800u+^$Gi*fbnA`0#Q~Jtf7*iWL4Xhkdkts;k|E~t6q&hZ2RE>HY5NahF2${2FEhnlybN6=wmHz|zHoD9wwEZ$^AjBu64w3V_E%^YLh zK<`+o5X8H#SC;f+@{K5)X)w*Wr_(q~ge*5fjvBPpsLEi>qB^Gk07O7S5hQ3}^#aDQ z+9|deO&2(0y)|u;6YzUbq*5U)F3E;WP%D#e^8{X$pJRenVr`SnUj{-*_x#q)dvW_~ z+O{5R?v|e2MN9AiGV;59lwAR!2^V+>x!W-1?SyE zIL)LY#-i4QDiZ7#(*TFRnoelr4{3+8{$06u@I0Esqr*?7g{cfcaqnZHOU@xbPKl@Z z1V24i`td#6QX0Dnipv^H0s1Jr#~DpiRc^nGpj806wFI_})!}**LzS^E^$B~ePlmt% z&dU+*sl*hTX7QgN!lN)D5=5hx1qcbfxf;)f|p!u;QMlLHd4E zzLy<`ENwi*QdYnvam3R3KXiGR^ntjK^U_@`jlp64=v3Y@fiS)U{+6P|1Pm*`qeJnf zUd;z0CdVyKTT1YP{T4W>^oWI&;xUF>-xYZ51D8>|{7Nta5hO|{gjfeGNF5=MtRJfD{Ho zF)&=Gaq*>32_!CR6KA`9qwx)3%rO4m3(&Uy_0~GR3b~&W>ssGz+5Bn# z951Q_fyk7R`0MLU+jng$ z09j7C!F|TNm*4rupKkq+OJzK_(4zLCzcss02VcdrU}G06+^uWy0(IM}rC30LDQmm) z#ThUl0s#I(^Sp-0VGEBXD;T!{FNPVoX?m3+B0lWhIa{R)A(SxNWFEhc*Vdn8*&B}K z849eI?TvIFrmUKvZ@6;E{{UajY7I8$<3nQmP%_2H8O}p5V_F0N5CwqzGb{;H0B=P+ z;)~P{T23~oBOO%c_vfxE+b|Wd+`ITL#2Hosk~KlW*(8+svcc|<`%y{5$#R4DP9hNr zW3zWke6)e8&54%rMpQuBj@3TuQp?PmaMM$KGbZzvuw@3WEOhTm!|R1D!nf~6K$%Qq zn#~Gs-vtXCfMBmSYSO~QPY6D0G`GlDmu6n^S0NA~?oZ)lp*$xvjJS&nYlUc2ZGpmK zV>BNFp+~CL*Npx>*nzuPd13zmRiac17UxqV`e=|lULW3x{J~^Vb9BM`*qU=@10u^C z1Od>XNHDVQw^mive+*OooD~Kb0*^M6B)_)QwM9b0EbQN+^a2J70N6cr@vSuY1=h_n zdN5hrHJT7K3B~dfQXg7rah=Au|`}(ZM6f% zz(AlF1Gn0e5S*34%ro4rK@_Xphe`_Zd(bmcY^|*nG$wKEkol+f3lK$%GYUg$At-kw zd=#j?AJ~v}in)^m7sucl;FcuYuhuGd3uGe#m^SfGEi77iKjqH!Wq~3xC*ug79Jzb_ z&Vrr_N>BQS$~XR8IJ$%YWm=p~jLBh!s>cz{I*F6z{(+un@bTSiI{~Xr=(9htV=If% z0S;4n$U&=oy}fND0E59fcqltm0_NtUu}9`jhRJFPw1mw^z5yqGKVa2O?rR?BF(dC^`YC-RT*7}I9%-1u28T^ z&1v*tN22=h#VVqH)?laER84{Y#CnkVB#CiLc45W^$&y?E8ApUDHKo4czc?0H33c4 z^6^CeiMqR!ZqJ~W8F=TymcY(pNbnwl6#$iN2K9eh4dK#t2gPWq-dLo0M06GqtihU> zy)-Nr{-Tp9osj)enR62_lA_IIyY2%~lSXOSO#4(x;#uz9;`zs-5k?Y2lBBgASK>{( zQVH@86WQ2Q6!HrvbHwrDu{ySH=c;*vK!Y_uh8l~jTSVxj;Qdgw({gSmh}l0DWSD`u z{Icmg6(6Y)^~VF;=lMiAZJlgOlLtJhA_XQt<4)0W+76TFq|KG0DmJU6#k)3EdQAthig+VO$x)UGCnNKbkD zQeWa=cWPzcFh{Bn6(R==kGT0Ms*5+smS7eM&?@|N4BEe1WRKP*6(A}w1t1)fexjMO z^-q?ioqie=pc%5C4HzQow)0N`*oi&re@_1Zlp{<81d_#$#nI!^310>V$k0t1LWhh) z;ch8$3Daig9O(&AfECl)>ta_8?Lr`ojuX@hY-496CuNM(>`o#OaA+^_Ku3(opNMzL zfg(97ZDW07Fn34D=A4c>YM82?5A^4|Jy_q*EmK_mo$Gpa2901MonR;(R-E zG*AfD78g2x+B4t_9bv9*vFVG8rL)h+C*Ks1B1d#*QMSE`gi@B`a%JV9006KA2hkv; z;EoKeXT{=jeAXsnRo2$kN3QLg#wA@9Ah>96Rat20#+6fT0!z7jdL!b~&f<#7hUT zVC_$zP%uAjO7u+OpzDee1Y~YMRVA9Jt4LiQRFRuVuJ_F_5w6jIzd-h=37f3@*0Xf9 z^qp%|9SDa109TqbO`+nZU;$!uL~NaE9qi<3fvM{qJwi|li7M^UZlyR_6J>Dd7{haf zp{OjN?z7V;sW=+UsGEZJW?V&1uJa{zIWZ=g-b}D0MnvE70&D99g5!vJaH(CMRo;A1 zosZ-U-XfqrEQ71H5R)yWQ$3gJpMr6WsDL+VIX^2Xz{a5d)NC~Xm@#u>S69-o1I7xN zI?7UZzXzIoFb)-xc03wy_l#nda{X1NrYtEs7D(4SP^k0&0MhgruK@w{SVxi9g;*{l zJa%g~zY9?oQ??&!cuZkXJkrNMVDm-h$$>Afw0T9-`l(#6PyC9WEqO=1TLCV&{>4Hh z5{=Dk5Xf{-tay_Sr$(hExWPd)2?{x{7dv``MlUis9Ue+D%|^{4?i(+?McBoVOW6Ma zj`U%TD3LckBrrjw4Us7$@pyOl5#phu8A6NRx`sS{Aq))YVYzgDq@lL(9dNO%Hgx8I z*$7CEALD|-)F1;ZL4vEc7H*b-&|tlL`8|X1N$K7iVahky>qMT35h;AXhahY>p&;yKWLy)vQ^CQ z)?cHnLomLv)_g$F=_(E2v+07MW}-r9Krc+g^FqLB1*l+}5iFGHp<|mL(@o{PHcE69 z26hOhMF;8b_Wr2e0=Dt=zGBCwzSNVH(w%Nyf!Tdiz~~Yqkt)%pOYRN7@vyH5eRSiV zI(txWv$75m^qf`8kssYQYj}`0XF+M^kC%oj2d=w4PsBkX*jOS?v|!E1-ShBz4d2Hq zIjEEeKK47b1!1z0Ec=~D;GJ1teoXFlgIY>cOf0i^P+@#f0R@jli`oqgg}WW808GY| zYVLMIO@~pwpF}JoHIlv*ObdX$W&M2^ z29cn5iY6vIuy}Q#1Our329S7)Ngn8VYIG-6DxEi~d{tn&>VC$R0A1vwGzRV+%46i< z@k$e70V>5EiM;zfpF^P*{lotNZCB8^W~=nfXkF+5vtbCUCyK?x+d#*^tTz!#4rbOq zb*zUEn!3IC& zs==49H+?T5cw6 zrR_~a?Y}glR6;`j^f*9(#14T_hFL%j)rtzyUs2Fqd8jwCS$0~YQD%~cvad-48!4x& zM4LA!QqB$quNo5|zB=Bg(fa=YTw!WL1GGFI&k~Py4k8;`=DF&4Zp@1E{&8n7f=#>as7pGMXoM6Q4ut7>xrc3GF+UhSyHp5e}ISJD*1pJr9< zmtWO0U-l2sFoDP_$D1i$(=}@+L0_t}02Z=ckDx&`seq)IF1vqrfSP5O9dtX;XF%NP zvH|Z$iS(%USfj{_NBhcbvP7oGdIA721P9rZR1VK!&h&QmlrWl_QxhQ~YEh@ijY8BM zBIc44tPj98p@do;IyCW8{{VChy`j~Uy#R(UAlye&(FOrTBdsz~7#7Xg$;A-oiKhkH zj66p_hVCexwnCn^cv@Oikck_fmUn-QdwPJ#z`mi}8ktv#$pRh+pTHH-b)#6=FWZ{D z>usHtXE7?f`Y##JzznNPVbmr}a74!)YTQUh4cCp0J$G&kRRkSV?)|*|MkdkiVQ6xNd04MhV(V#ekoL>z(8$@d(+bvdgT)d z;*JhVt^WYE49o{xBS%{ww^!}0NBPAv<ZkN{!8^Fde-Sh{2!tg< z4+B|cqL@v3GzpOwNm;heP9H>2qOC-Ps6bJz3nk$3TPZ>;hfPis@QEs6gdsdN4?|=~ z*o{_mYilvYAAV2-hjK|jSHNtz5Q^&bqoMysZ&b=Cactv^< zZJy`FBti6K9`y)>LGTVv)Q>eyZZK;e)n%^;$hj+-<2BV)XqPl77nufT`L0HOy7R%N4gYc2% zjCQD)nS?%HL}h)#e9%D)sAtUp00si1VjB~92AQcT>S?q@XPQd4sYvR`_^CGQkUBsW zEC;^RoVdM4ASBA~e9!>>*?bk4P)G_rsVV~&mA`bN#QvAP=$1ll5Iw5y92l%!vO#Zp z4k~5=d&7J8=9hYr;OFq?60nPG50}MX za$m!L@9LuMlz^?QC@t95hg;D1SBi^=Gg2sKz#~H7gE$^Tzt7aN%eGuH^ddU6`Is<7 zNWOFQ#iN0Uf8hTBRE&WFYBcS@rb1er5iKP&xYG^j^>By4`v=q#EX7uCVwl)aLN_0- z--?I`LL`Yx+(lXmo3~bYjp)WmGGqOo-j1z219J|@J(@TGXOIe!{{RsDAYhRpH=}iB zZU#Yg)3tLNUIKjo01mba1;ofuK15R=?$0#NP9B7DtdtI^iv!wPqnoRu26 zfQoy0rvgMfGuoD!Ld+t-GmAYppj0+(V*qXYY9embPE$aqQVY^=po}+F(6(24T@+(2 zEO2AD;~3YVN+xXCZxtVa#R)+(iHUO_h-6`K2u}?_WFer*PDqNdcReH6WbOD@AD&{T zx3ZdY+H?A`1NdZ)3wmSRD3KdRaUq1iJ2uJDy}VmM9#p2@DWrjq1e5WFtY!>u!|f&76Cbb&GEz zQ`D`-C9~|!V`ZUii6;3!)k7i}woO_)%?BhsKivx{-KgSXg@h0=JB23gzzXUK`J>Oq zd66WY3C{ljQKD&Zu5fAz-$w5=9mN_)%F;(Vp(N51eA`-Q(-0vkds%H4&A%TN6qLn~ zT=`_{M$j->+;kD$8hR$3v%uYc9g3Mk1sc$2v(MoTzhqwM_ZPKWW0#Uo_~ZCwc?AZy z$C^F^9?|;g#RT9@7)3Uu>;&vfvFILEoq#23@Majd-ba<7000Gm!T6=U8O7G#=&9V= zmrVe4D2vhNKnd#&Ux^UQWv8z6f!jdA#^qlCOS*1WvzqKb*Tn%qCd3hbV_GUDoF5Q` zlqeN$+a77dB{sUBZ~7_mg1qhf#b=K9)4U2Yf-ilSuSPkESM0AJ|65+?{fD%NJKLq82JAyvVNV+C$N8)Hi5s}yU zQ+H;$sr?T%4TbV&KDdWy1E}Kj@>6825Xb^I;642UxFVAZm-x4O7_}x#g2%=C{xKb4 z^+Yu%lEYH##(5eo!+|#pf^}XkP75H38bG=6QhYdY0tjKwerZF3c(u%)%u*1v=ePAy zG!^(QE_XgY=+rKf=r2 zzC`zz@U6rK#gKcGLQu5O0QJS@F;N$pxW9@trxO4W9)ra|RfPS6vZGW4XapaQv`r@6 zX3qLG1R?sP%1(PSxpz6i4+cz7pJqUAY-FegE1i;(t(H(q5V)6V!>lO z7NX29P#yQ8$2>#+G*D@rz?iEka5n&$pB#!=s zd@YXUpCh$A!8t}>=pEn5QVFT9C8LO2a@$fS!AaSZPPK%)z-Gd6e9*udHp6$xQ?I?wcCWB-N;Xq&q zPpr>X?$9}SQc1?FFCUPAAVO|b7~*SAnC{Yt4h+ah_VIl$vh2?htYwK zQYp(3%MR4jX~jlKmjQ02Ph%yxf3L8U2~rrGi2R>1vQwigj|yig1(guKka{CNv`0)% zUI(BKY}C4M+j1ZW_lQVKZJzbD`Ew;x5FePcEJpbPXx%%_I+9R16Z>jHk}7m9m`M6qlg z&%#Wwlsy7G&cE7=W%sE`nER!@sF4U?vz_uz z-;_wmu__~^BAhq^Ca0khV|Kbs&!1{#v63vj%ygf^jPhrDm%^v%HQ(d|{2b;$J|=y} zt@M^T5%~3=nMbfo>{EAgi9jEf=n-I85^_6OJJZ7C2#n-A5Pn!sAz^gZw8M8wlnfm# zLcNf!@5McoGaJDE>4-ub5YMtxCdvcUx2P_leoy+S6r_?v%Ddq{#Vc&XMX6xXrSG4@>6m)ZT*+O({Mi2h2Z6Ke{{XkHEvTuD z(eYIx1r4?5nE3Q8Znn@EpZS^|AnDML$`lC)p&i(z3L>z!L&E%14|MJUpJDLrS6>I0 z`c=+mg~mhLVy}?n;m^O%sUDFCFDcWzLdf<-q?@z(csl8jnLI563t&ALy!r_uQWRQ~ z76kDWqDYr{C-80G_Ud0v7q|Qp`9;@hAJto_h3llD)l_UN-YMG~28s_v4}U=J#l$89 z54)w7e<-IW$k>EyEdvBkH?#9JRsv^~l--4XXo}{TaJ^<}r38r@osm^=-Wa@|^kTyz zX~i@G0C4^B1u0?q*r_21U;-_Eu|>3STL*306tF7cXgia?SLOe z6>>_B(Ek8$%aL>%Z80QoA=se;M7AR7{i+%RAUb#4ziPjza)jO&y_z$wO}f;!yMmJT zci}?J%qWU-IyUP;abgBSa&l1U*}R=Jps12SsCKV>hA9vaDxm3YB=&7g4yi%ea?w2X z3jqlo%2SMPm6BVD#WR-7nBf%P4{b_yqBAm{UXPo8S_5*!(vo3ya1eR)R$VVQkA{L| zOF2B7aQqNtpV5AmH)_Sae*}c{3Elqys-}Wxp341mO#^_et7BI6h=STI5%<9`6PSek z#Gn>3BRWEVUz8GsETZTT0>j#dITl_4`uaDJnUCIbUWY-8lw1s)Q4`!`JdHbBr2;Nl z60~E>BZ=`#vTNjSbatnvCbtRMhM>SKM!jE(RF%W65;+TO>}%eLh9L<50FJZ+WXb{y z9@Mi)L50x%sg6rdjgQ?&euR{69sJeIJ%WXd?+G86Q(%%3=sYRt$fgWNjOel?5GMZs zq4+QT-GZhrzZm`r!2bZ*e^p>LuB!FgRC9tNf(ZM_&+W|n4I9u_cL00O~jZMrE>^w9bMpYo(O#V8u2pTAS6aQ@AAFJ=Au=^-!(HY=($>$Uv|*asWJ{;GVDq;P(}6c$_<8>>UcR)W9X z>~lw*(PTLgzj2{MbmhAx^Asvka4Yj2=?MrVB`}PVp4eJvCOSy%QE);c1)1xf{{U=t z;K-D<9V#kC{v$!}#Q`k>Gj*L0Ijf2~zUIF?eb5f@Z+F>wqWD~imcV=c3}BV)8yf4H znV}qovA0sD=bEhc6rF@x*}7TxrpIOr1AI?wcx#A<6>se1>W;UA>l}#rpIb7q7f2vnOFM;7ZNSW@cj3u zLlxL`e@R9@AS==*!>`=s}^WoDtoAh(k(@5^Lg>L5;RAKfkd<#+>+Qy1w1h;_*dN0^U{?X2(t_FB_uj8Ykiw zLB-uaRlq~c{+@!u6z&+XjSvA75xS3Q{48fpY%k!Qoz)CzQ1SiVxCF^7(c|bvWR#8Q zip<>4ABT4xcH@P#B^vB<%XUHFyXZ25oX-omK?MJv-=TNym z<61CM4U&lgb823!I&F%K2omnP=>Gt}rn>TOuECY3rjA`U(scaFWnkSuI$+9&JB`{{SC<@Iv;?pq+TWf2Y@5?nLCloAOy2z}4NKd2^vm7rD7+zc4$cdJ&rOvEpBd;5LT2H*fAV9d;aUv0>pg|dFa znNE9G)xT{x>Ge<*U^Qu^5~y{D!_991oe?7bT9dpP(*nhEZ*Ce8BbA~p?gx5RY?&6= z9D)?pgZieq zZajj&54;-!6iAy>Sx&qXub+SXseYnM1XOOTN_*U@qIx)Jd2u!*VFN!V)-x z_aB92*}VS%g3c5ZTK>oM`sfO!a$4p*`WHJ%-k0^c1NHbLkkUj%zV_~FPd1xK4+Q&t z>|m5`e^H>q1VMpvd7rSrpe*b#?~e3`2pzJ1shAk6Nio|WNQsqN=x&>C>p+AMGH}Pu z+A?zw0NA7v5Jt%BS3r~?n+M#fmvl}854l>0?T-e(Q<-vPVUx2_suH_gy|)wonpgnI z&I60S)hUv5QDJxN*RCM2U_rAl;AUA`~;5pJofN^R+W+&SXj)Q@*daMm;!+)^TSiZV@UR-bv-}ZZ6YzugSHrS+zpq@yiius7T82U2@3xYo zY0`a`w}~>-`%l2QVBSK`>>cU0l!%n%=QIifAblErD_>Zx)d_fa1jQ3F<>PY0~X_2~FL=?6c-|7;mjBgD7siLAAy8i%Q zf=-|k(Of0>QT_=U6Y$gbM=5x2f$P8%aU)wlRqScP3G?^BxINsU_oc?XWVsji`~ZEB zjA|YVoMqxk{=~a_84}XO>ZUbesl0z@r8(yCP7{%Y=}_W>VPJm39!9JeHQ8IR&|GO4 zM#)SfNZ6JG#*IdZG?|a!eRxixrKU;JOL(SWV4wv=?x%lQL_iCwz*F(;5wgsx1^K5W zT7uANWCzVRG*fSOPP^?-=810|iGTK;NJ^cE%jT0!WdS#69?^m`*>)NCs2k@&e2z3Y zKC#TGbtDNfoWnrY!pC7Silp(!;KXcu6KXjV{{T91{1j)Id5_Yr9CxVo&=48aH)Qyv zET$t=BKa}&j9$F-^71^AEsznk+i{_76019F0Hj=f|Oq1zj6b zy{b!~d{0a##6~i;G~>lIhJIKuI*j&diy}7!*aDT7m619$OFPw_GE7+Dbv{17-e^46~-6h!7ZBf49!C;E&nK4N*7oPn;qs;_HFY>8WWo-yPgMex{ z0-)7?Sr%XG1wgI&fcre<`oFpO2FZLAJmQTdMJF8WV?y)|03~cjc-b?wZWRTVW+uu< zrP-)#s1PI=8c2+K;+r2N{i@i{Y?u5IYzPtEaZ5b_PKfu9TqC&R#Y~mRM>^IM3ma1W zsbNM5V13#$IKep9x|QOOU)p$#PDPz$p#u$h@*eah^JzZx{Ue+j_67s}YSoc#%OSROZeQ>{Fac_z9XP-y_B=3Cu+ z6kKFiGn3VphEfa+P73D1I|Vmj#t_LX+rU!kAO{w^{{U@5IA_)mk_Gw(nPPhj`V06Y z4nh-hnU{mezSHZ>1w|}K_Tp%HDacTvbn(=g%}+_AmJUoMaBrkgMQSB+)>~5 z48QGu-;Es@i1DA5qrhlEI06o^J?XPX!fc>Ut53Li%W*;lp&pRTpbBILVU4W#`-v8q zyOzi5dsI$@>vV72`ShJU1nDnlwgv4<2;r1Wyb^c&kq6Yc=0}N^{U{j6-vkchtlOGk z0AnCZ$i^y35+YLrZPf=toAI;_<{{xTQ46cDET0s3()ipH^A)NvMzY63-P(ZGk)2rs z*`*7Ca%svJa3BRS2abxen7Mr)SVe`WaS!UGQ#|N5=lC{MC5V9s=AsVUYzO!2ASVKr zK_^M45}6T!7?g`T{Sqpr0#x5LI6XNW$6C2ukC8Fc6d3^_@R~nnKmy#u?CpUo1Hs9JGkO5EDkN$hJWA znkWPag>Tw4HT2~WAeFhjkGWt_go2iQ)bcYf0x+B>_fx0$!egnAK)X(}THZ=lP6>ip zT{A~8@ZXo0KT&OimV6YD;=q7TCMBXl19PvE8kYIZY3q=X0fZBP^mp4 z#Oj0QplrL=SMG|DBnZi4u|^3ALw{bu=7BnkvN%8Gw zWTf3g4^v7}yAw;mNRsmAnU))2WN#+iKVT=wW}y40T42ioh2+O4>+Tcagy1-zK56v< zcpb+LZ?k_&M^Z5c5#=e}dppFce9*BR8Uh3H+dY<69cuFa5QlkdE&VrM29IzQv*${H z{{G?}9f5(LBvflPT@VO7ec}dlPzJ3Vc6X~(Zs=%tr;>mRI{Xdq_E_p62Y8|fcLEO4 zP^^g)lqKnuuh9?oohf{QQ~N{Su!(q5Q>zDR)pRZ0-+DyO1dHH@lCEq?X2RW8)qG-? z0PF!kd}SwAPV987xV$pujaaMbRakO9(}PK%6GI>e{hwMSW*dD~{Zx(zx|8@a0x~|2 z$fVrZ=E?`l>9PSR0>~cAr5LH&(jkqLdDeyDlQuj%dty-|^?AD8;hJg?PD&V50H{^*yGI%xGrDUi!*62^4q&Qf&@~&HPC%4oP z1Z<5f0}Pj&M~-VSM_-#h{>S>a+q@l?~7_a45CO&Ic~-0Uj*n zcX*)|gCI9EXqa814{|<^-jBgqO#~M7)77Q}yXY zzabc0XhyV^6y(7YL`x*JMT8KQ5*{dQE;)9R57|JVv0O&UxRux9AtZ+-sqzz85jNy8 zS8)4WB!pu}%?sNTP!8c`8#cc*K>(H$Tb4cOR008Yz+GBP5Ww$dbe)QWSZ2-CRr^r> zCC~=q1U8~#`TDg+86q^q8OM)-`KYX$oWJd9G|kiD=9(!4!4S^o#hvZ<7^@Xl%I3#v z77An;6?ccFXmu`uG`}iVGA2g<0F+VRq)h^h64mYZ7suFWoZ%UHsat6*5b^Yd(XcRJ zI`z#IT8_ab*5YC4(C?07aF^6%?exWRCRg4LJhY=q#w=5TflhvsuVc%fz8d3*rRKTu z#67;OVz`{~0`+I3Cd~1JiZICqqvl(VhMZev>LGV~1xuG$%iMnjKn1J8G$l$6y@sc; z{3bsX<1cVjj$4yv5czrZgfYR9whdBlZAlPUV?qY8ereMftRiB@C**2L9K)5TXXca% z0}i)WiSbSf^qo>q4Es~ZZe_Wv-fIIy%*JmiC@*db0JZj$IUyr6W9=D62sk2yk_f(o z(t;LI35ze91iPYhM@uRTfRxlr`23Up@oFR`NP##sNbxiTk`g>ni7Nv8Al0NV#?<+A z#-3|0bnZ0cytJYwYq6?b`1;26JK1UCXapdn&kv@3X})i$M&Gccw->S{zve88Li*5MIjWi644cwRdC(xA23& zgf%re{)hW$a3D`f6&Z_qC`J00t8#%F7UV)E*l%hj$thrJJ*rOSAZLg6pZ&(sNmv~ykvi%dQ{KZBR?@xc%j6LG)`Ta)cRTCrn)SJ{{Yt1Kd|Ei zJ$xphELfutL72E?N1mPTy7o=PhZL+jIe}6iK*%=-GwfeP@$+8CD)}-NHv{T_4df@VhNec?KLb& zC{jk#hC<~|M=xx3Rx?JhApIu2=pcf@_dI4T05xjuZ~=FTbvn)}!V&YIt2zc?{{Vy(=&%Omau=}tAc$oR?`NAXpbbjB9!j#+Eh6t6z)KyDjXH(T4yb zmfUpjSczz}^l^4dKvl4tBG*a&l-~g@#C87wR7E1Nhi!2CQKuP1iA7M)Lo2z8p5BJ%5w(;tO!ppBMv&g-D=5DGLrM%RJ zJ(Qw>EpeKJz|Lo;Y=??)fJQT=+izqO53O)dcMZa*eB&Tso?H8E8m)WSs*g#M5L}bOrZ$X-)g&{>Pt2 zeTlEkFfOh;(F=r_B%lBS0>|K}hyitUsZ)s^ z(K|wdct@~7?0*Xei`kg|ke%tFqs}3Fti$jbMtk+X3}CUars!<_6uH@x=(#L~B5f%_ z%8&#B^qjI1R$Yk*?Lovs6G&IEK-UOBLG%s>l)zBgi)>x*QHUa%75%?*RZJ=1a(1am zEwkbux{aD6RpsHPltW@(LAd%HIBnTMM1lmvs+_MsmhI0c8N#NdwZ_+0o4ru(Xqa@p zO-f$5Yn)E)8lrjmJv&jwQ5XW4A)@6jjoK+AE4UN>A4M7fm2ecUXou9@>b}uf+5ozX z%VP(iuUIRG7<$5zbeId7ofQu6!V-a;-yS(8dsbn3f5Cz6{4y{B2;nMPO=nOSyedWl zHvv7LUQ%fo%KrdR_Na=$*C}Dr?$5RTlu7;I!$ryFOB!!l=UP3-frR=MpCRC;Q~*Wh zBt9xEsV2JlHa!CjXi*|lpSd&vj|ODsjxP2WwenCj%uR;q81qbxy>nl2NL_Lw?x)(F z5On+0KVg!eI3DS+qV(3wDrQiL_WZbz!wff5KQsa;0hC_jmV|a(EjH z?O1Ln>KDA+fHzum`4p@e+2j4o0%8&Y?f} zF3}*eHv&&^e-5=$#Ai|J3PYeKXHmVF?d!^Ti*X^SiBN$mFivOOQi%d2LPYKMtpSSA zm0aFOwzTv{!6jTq;l)75DIaw-B@i+L5kNgsMLi**-#(tKVjw1f(fR8^6_FzDq=}s; z6kBQp4>B9RXk`sZ_n4=k5BGwSy*t<=j5|d#i~)V#OrG>@Uh&7ZJix|qp<}xTdWE{> z8o%tXfmVq8yxmzFk7g+3Bwz^OotvFw>Y*(N%8V2#P;3`u&PV#7000Gm)6so`O2q6O zw5&}tVZVS;NrFU3%H?HweE}XK2FI{ZwMcU}n;?$=0Pv5)Ll~mza*kx3sALEuR7>uc z_d5id>+T z((Ex0c&BHWcn2!UOxQ4RcN1x1+)|XlzQ-(fF81H^4?54n5S}U>a%M9_?0_S1*`B%Y z>VZv|kevqF^U{GDBdPB?Vc0zqoQDx;0hqV}*r1VYIm^wveiHgXMbNXm1xv!Mtg`Bl z;s`@*Bt(Xv1DccD%kNI)1d$07*CpIKBzdSfG8I4Yau zL9{eU5QL{>XiPHA{{V|M8!+z?&&q4t(*yv3mqSJSPAI}Ph98qbkPsULe!n>4o|sb4 znp#3*h3+2I=gNl(K9Q~}jRK005c4Ju>e4p=Os@w3`JX@;03}=$$qOdUgWR*UR>>ht zgmE@F55sa$4n)E?OT7u?65U;SU*cBRWv+PcN#2hp$AVJ4`=5Y-Mg)jLHPCqh^v&>}e0a&Vb19o|K%WskE9uemAL;qSTWQ7=${$iYXxj7(6ZD>qbF>QoCLY4*fq5 ztg_a~oe6tVlRT~&!1;7v#f8YoL?pq`Cu&K9fuUjN=kOL<0f~Rq$a_!=pvY@d*iq8=o{Kf>{#d4$rGJY$lUo^3#Q~S(e*)wd0@Q48UCrsEPAQ zLh(Y#y}UgU(+MhO)!Sj-mpEf`CKHv{W3^bC01boCaDZzNAH5D~wvvSp6TnLIG<*O7 z6B&Ew@ZW0q+c3e@3N}U%gd`4;KgFtbmJ6|6(H8|HQ#M`T55wp1Uk%B*bQ=~&jVOW0 zoX!rAPoG6(DZ{~`x7;_QfcEF90f8TW;K^9vu+iBxeK{p7=VQ$TkjM;1lkI9X!~lQ{ zAHXDLL9h(`6k7zrtPuC~1K_W(CoVkDs13D=a5|!3pkfjLRo7DR5!7(~!~)N&vu9l(7f21`Ci zaD1-R(+CX0XpzLvY7r!4ar^<5fe4#}^t56xYY@UuN;V-S1iQ;e{U2%j=r9|rN1Txf z?f(E=Z%9&9R{6ZW%?)%C&{1OUsjB)ahHCV6M&9aU(kdg$ONJM*tpE^M{5bHKgCj?H zFZio9Pzax8Txrw_pZJfsUdypt!2o`Q@-W1#juUr8j9 zf)+4zfAvxtggP0rSt@l<5PFmQQIjNzBj4|tJ`Y=8>YTa(EGV7Xc zzjm>7cbLlMWqSVr8;mh2i7cFqCrWsWVVK86_n<^-YKc4k3=S;NmBKYI!`8rfLv}rS zHQc`blUlcqBPPMSS8}So*C%tmXwBgLOBuBYgCGSR_8#?e zW?3Fg!vT$GBoIs%7mF)aiGY%G(LYoI6A0y!Pk5gH0Kj;HXCh*YrhX=sTDT00Bfaxe zI*P-SK%^JaiuQ~MlM+Szle|;|8qs-eO536$n@&$XC{UXKwvk#%Fk8|@fD*0(xqXTr z;Wp~2(lJ3MEg@z#b$WgzCXt1@3wL}qt(yZY4?@nnKaXLHL>Ebq*rV!EC6y97UHB%L zjA9VbGn=bN%waVei$MXoX}G6oxVW6k8wb((nDut?|;wKCv|P+sq9BQxM^cvGd) zMOS?=tI&v(aBmyX(89=eTy~l~(hvjo^RLm26Jn$SF%Kl)z+mfH^bRIs?P>t9Hd0RI zI`BjW>q%O(OFLkNPPe6ic?gB&$Q6v`Oj6Kb7uM++knMd3m=NH+;Pl#GJlidD+LVE_n05($oT zR?;GJg-Xv~-~f`-EmDt85t)A`%Arwiz?H24i0krdV}G~27Ily zdD^b%2qorG)H!u+y&d%cfD8xXIY45oi|&bd4e9U2p{AFy=lK$J!OY8UV5})chO;#9DJI%j(poPZ6?D=$ zF8LSKh4=Ar{AasDwqb0eFF(ZvdlLXZ5)O{NeId%)uYioOW#|xe3M(S611~On{{SMK z&J2h`J*ey#671c$lH{m5QZ|WaM<);AO2qpbx~E*3+)b4dA%XA;=0m6hWd__3xHEfu*z^6^%Zd$VCXtMvX%NM!97F_V+rDTqoo62i_lcaQbx#Q0ITq6ux{g;+MW)Ccblo~mdv1KSJvrb^Po6KVed zhLoVZyw?gbC5}!gySoFp*h$<9!xxSpACmaTP(7)1r!w}8zSM;!g!{4&D%Aa$%>!s< z+B&t4L6%&*yuP$xL=4JXpKV&&m5J6?x?D~J#Rk8f7}ed~{8x%}AOcmO#^mI(fWfJE zv7tkp2{IQB3?6mgcM&alF5>maAw&P^vXYkY**+kjjg-wCv6R3{= z03%GD(BK-Dhq>!S*1$UsPSn4~FHRW~-kK|{k=$R}Dp7X}ZNmrZj~(gmkLrc6TlItZ zddP)@b}vK=*;rpIPgGfc=oRV(00UWlH^G^{u|-i~p>1&f)`U4i0k9stauu3c{{X`T zYe5z?jpgBRf8vb2iGUxCDl#C!p6!>m^%-VK9llWR?fkgI)`fiBj;B3oiIbqhaWs26 zP=JCr6JNs%<(o|s==(a!QZXHy!0Nng)S_A~#Mu29blQeVJ$DHl7+oSBX$~ufE(Sr} zY2bx{%s7<*GUnUb)Nri5zDg*gE`dBFDcXf0X3CE=1lcC?J?OzP#0TjP)fE*E)S;9* z(BcZkg7rNnX)ETSvu*D&6l8F)g8Q^%g}y>}VewEZOx_Nt2HB&nr2d9HihvRma_(L1 zss|m!O!bWZ1X?g)ajn@D2H#sFSuA(Dhh zP$uv!j+p!j?=tp|M{(?HQ#nSq8-d(@cA{1=paKK=jRX5x_34*c-mjajBiF&?>3_qp zDTExACFwQ!m$8$Y1WKXrL^FYVWUPw(YqDf`e+7#MNFL1%`Xa*QVNgD~1bdW8118aQ zxkx}5Bt0aiNV^218>jqIm}`HgDo(Kt0ekH4R(0j2vqhroVECX2j=?yGY6Jj_yLS%0E~~UL|snChUeqQoRTwKh$9V0DsJ` z(rSb7^Fc?J#2a5`6ZF$=%03a?JZbo{tS*R~16~*FO)&ermd{e<9LDO&?7B1jiMI(v z0oJk6LD+S2+pQP2%5DJW+Tfua%hz4?A3$CLl0-j!tLpen#n zE#V@=B%6u0?e?n!+k39Oc>>zM&D?sxF9v!zO{!BjW#kg}uxCuxcBdPTyM*^3ve2T^@!Lbd~corL%p-dRDg4h1nq`)&fyJQ}} z$7F+_E8sf#qR&b~ZJQ%K?ZpU3=>;;qsMiP-0Kgg^k&~njk+JxD(_u_5h!d$VRFI@s z!m$mposL^UE!p5rHPq$}=pcjn#NaKNF%mMD6OlUKRnW zwE{sZ)63_roHdZ^qH)2+7>T$`POw_?f9ZXuwA!Huc>gr zs|$~c8YXNgj7eePX!ZaNM&s$fR|1gX4UIQxm9k<~TZgS3-6%|72p{%;o`Rxeymc-1 zYn|i;TqbNMd*XJX5JpE|Obas9rkl84})dv$`%?2k(sP&O=hJ$^9W5~Pi zt9J}k;!ok5ySU4uUHGXR39~dp$Gu2U4dS;hMe_wgel<4jj2a%yu9SeM1v4^()lX;`o%b}If;oP(IyEFawgG4TF|`eVLU}ZAmu1gYSDJgA$% zur>qg+L2)fRn4sGG;xZM=t>^bKP!| zPtDTQvQ(lJZ7NSfqnXA+1&4SCmapB)u^8;U(^07TxoTPK#Hq|}ynjT(-_!1c00W0NKym27zgS9n_LJXGmm%^%q|% zw7M3@kJS70%|^0G0n59(-KA0Z2%VZl?DIm7@LL?>@SM`KXjUaJ-i1Z%QY@EJVZglg ziVNFm0se%_Z|dqZNKw(rNdfK`Z-GS9RyL?k+dU&&SY|OhCN;D;nLj2sZ4 z00IN;X-9Tm169zho+L$B6f*s=R$B)`C%wDawtvAO$Sbmbo@!KzM(Rc|nmYBMT|&jM zIv+l{`oh0#Y!F7s!QzovSpxiVb!VTqk>sbjTn@rLzKDC1V ze_CO(1f#A>op}WU-b84lDG6v|7Kf#7;u`k+_|}BD%qww$C{U}-KtGt)_tGl~2J6bWm zlm&bcTrF^E%})|#RHuSZda*Ft-h+8_KGd+K1kVwKp44rI0RU_tfDkOKDKR=+F|{r6 zypkv_%YojBANVyAvrxBmtguc|s{#{d)HT(v55uBp)$|c1E5`BKnM*K;(+=r~bO?~f z^F%5*!kH9U-9ZE&asL1-t=$0CZCZ~%6<8Q#_R^<6B5n!(!{huNWm+gHkja8vf&Mjh zsFzTMwkDf3Ykc}7KB2(nXhm5imvOl2l-72Y_E8<%^GEAofNvpDKhbNj$4_{p>=PP^ zC*Y;ZA&0Y5%(R1NmMfanTuZZSS~!njd#Iljt902;I}L4sVLg={1|dMUNd*tWKm$xg z-Kp0oC4h5#Q-DN>b@4STx-HR^PyH0nvr~i|mP%eA0;oEF%_$k=6?R&*09V1{~Y#^fnu&|NK63_?$YF-73b-UMp;;s2)faqlQDbvakCm^#OiYHsB1fM>H zrB0s#jjc5-Ln@0&Yk#Iz8E&xrQo3jv05k1V9+(`0A=rYJIEqU~u;f?YdbGS<&=D~3 z-{!SLMTp35O6#|v)&xg?p}G0RT5^95bTk7iy+`>BT3M3^Ie)^!i=arGB{ng&sO!17 z(@S8mS0lM+<>AS^G|*4GAPZt!BU_Yi6d__0Q(A~RplHy7C}cCD{4ErP21hjG4mpF4 zXMGAGg8|E`xmQ)Y^ex381C^$#(4HX$*{0hNjd>qxCZdWCzridL)OZ@GN*lIVlF<~7 zVE$;Pd_*19+GtJvlJzy$c1C!f@N%J<0;9U4w10vxn|zHz`rOWYMX?m`UPm z8bE$&mYm5-`Ud8~gC4sKulGq`Nv(im-MvqV#LuR^f>Xka$Uk%$V!W#HC#Y`pq_xFY$^;4 zV&_VQ9eJqAP?fQa9Qq0ZtOXf=_l@&&oElACbgoj$C;b}GE`r~_3;rOwATC`W_8M&6 zh)kX%hSYo2nnY$hr(I=!zJ!swpp?5v<8qc-5)w&AVL8%cr4;qav1N_cUmH+=gF7tl zShlRrA+@52a)MNVA$XL~v)Jsd;g2Hq}=RUpa)j3|^tOt|T zVuolua0BSNFOUp<46r3u0HsDft6*01ryXm_cbq65>5whJW+Zx1E-g(bjga!-_6YeJ?0fUjVM=2nHa27s? zRRrX6`xhj6pa=;TXHiKtG;;=1#wUu714qvN-E=kb)5u6sj>7||f1EklsY z5M5QHlem4#?BrJs(7@9+L`(pfAF|Z^jR;7Tu1$ikstoiyx(Xmdl0*iWv1-dbI|l)q9g-# zX>EOfJ#Uv|S`I6u5oL+L{Vib0G8Zm~1;55DmLZ!CM-8eBUSc~KUGt7v%?}-c$GjLF7nu45W3Lim1=sqfZqXsMU{rBxbSzQbK$Os44p(S` z0EZ%o`WEx*00OW{UN4sw8&aMPtjm^b85r!oc^nq&KosEs*bIM-AdU_o*%8S?G7^>( zmbYwlrJeXPmpBei%>&kA`7paaS^_}{6A#dv8zVe{{U<0J(qvox1}8w4TX;4LBT@NUInupgD+!R!9am#=oG9}q~~(A ze^h8`VE`GsF~uGwr2wmJlLnE)-kb!G!Dqmy88WOg4U+DW_4zT6{1vW(<8V*uI=iEQ`oE1ShOxNAD7oq`R zKYPBB<(L${!FB0RwIxc=4xX3fsM=xHOs#d!Iiev-BJ~;$2tUWLPieC57ao+ks#AbC z)3*FIms}KX-2VWqQOJ%YM{HfUw=|(EwmU=7u>Ts1t^8 z@3lRrF(dGU%LcUzJ}p+Ob!B?}7T^S}Q$Nax=1c~sQx1~#6pAH&>3NFOftW9FSK`;>+V6xK9Tj=6g=hPg}=@ZtNi#?@>N5u>~t^1 z7i3bo@@3`rpa1|M4L`_X1~@UamMr0(BCI=eVDp_xD!Mj~#4z@y}mWyV3%UVfl0lo2)2!B8q_;ra>h zN5Kh~IJvUh9MsbgID%ylz=@#w{DpwsvOC2YFeFAq^yeQUXvLbfMH1qXwH(fuoHQH)mZtBM+v@FD0V^n5 zDZb@dlz}p2o?cy}?2FpKfnppeYFVc{(pk8j;?x0mMUcv9Zn&iG%E+~T#t*AYu%zC* z!!$Crk&-iE871=3B>}KYpuo!aX|<;er~!WHfNr`wh0ls86|$?osETwg1U^I{Uo9BE3z2_=-$|xX(7F@3A@-rqVt1^fd2rkMSxKL^JfGc zeT;+AVgCSYPNeX|;Hhq--j#$5IxgD|06zrF8dQKPPYDW5vX(ov>S{v8I=1W{wIli< zjNe@8G#P=JQQ?20f~LXc*#7`6O1DE(k;vAq5S=(NcchjSscT>8DlD#J39KD@jTsok zGQ50%tgO|?agX7`KlaSY)gqUBK+tw?m$e6*hkP^UvuuduI#@fS#W1cLVlL(n6m-OZ zAV17Z(gw4XeEMn&dLZtfl{N(SL%5G>S_A5juoT(Y z{{We%z`eowDla2JUi28sf5vOvl>X?s=p#f34D9_DGet>Ug1wzb?!B6q?u%H?e};vg z3BDym18E8F!BC@^Q+usGBTr%h^Fu{8o?_`zUe?sSciAs>QqQzc;P46uhOYc%l z5d-NecAx?R1OEVnGS5PD$wo2`WqiV<<3CB_)Q=td#**A0>Y7TXX%&1ltJ+7wQXAqE z;;KRe7X1_%W9%z?*Zjxsr2cgO0A)@Z9sdBkOM@-1`{;Zek{YfP`>6L*79MxwN>NQw h^8U3rS&=*3`_jk`r9GxSsDM`K<|-`hfd2pl|JfvvwJ87q literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/ui-states/empty-app-state.svg b/xhiveframework/public/images/ui-states/empty-app-state.svg new file mode 100644 index 0000000..b7e346f --- /dev/null +++ b/xhiveframework/public/images/ui-states/empty-app-state.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/xhiveframework/public/images/ui-states/empty.png b/xhiveframework/public/images/ui-states/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..de2a89350854b07203c53051eacbe4e9170864f4 GIT binary patch literal 50732 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/ui-states/event-empty-state.svg b/xhiveframework/public/images/ui-states/event-empty-state.svg new file mode 100644 index 0000000..ef54e60 --- /dev/null +++ b/xhiveframework/public/images/ui-states/event-empty-state.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/xhiveframework/public/images/ui-states/grid-empty-state.svg b/xhiveframework/public/images/ui-states/grid-empty-state.svg new file mode 100644 index 0000000..8a4473d --- /dev/null +++ b/xhiveframework/public/images/ui-states/grid-empty-state.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/xhiveframework/public/images/ui-states/list-empty-state.svg b/xhiveframework/public/images/ui-states/list-empty-state.svg new file mode 100644 index 0000000..067fbf5 --- /dev/null +++ b/xhiveframework/public/images/ui-states/list-empty-state.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/xhiveframework/public/images/ui-states/notification-empty-state.svg b/xhiveframework/public/images/ui-states/notification-empty-state.svg new file mode 100644 index 0000000..0515fd6 --- /dev/null +++ b/xhiveframework/public/images/ui-states/notification-empty-state.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/xhiveframework/public/images/ui-states/search-empty-state.svg b/xhiveframework/public/images/ui-states/search-empty-state.svg new file mode 100644 index 0000000..4e1317f --- /dev/null +++ b/xhiveframework/public/images/ui-states/search-empty-state.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/xhiveframework/public/images/ui-states/success-color.png b/xhiveframework/public/images/ui-states/success-color.png new file mode 100644 index 0000000000000000000000000000000000000000..39028d0038c4b510721d8fb43cb84407e20edda5 GIT binary patch literal 64316 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àK%(4t5nJ<v{vu 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/ui/ajax-loader.gif b/xhiveframework/public/images/ui/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..148005da279b97c0acc697d48a815645977dc723 GIT binary patch literal 1737 zcmYk+ZA?>H8o=>msePlC7H(UlOeuva6-udX=eG2X-nQJ9g5||K3}WrHKr7Zx(JnHq zYm9Sl?~UuSa${USIL0u>7~?RkfNPD*`ob8-IL0xKV;t8tt}(_n#yG|p*O+y6r*3#Y zo}4H7<@}T5Z^5mNF)7XZ9@^9BHL{rdI2d-q18QMcRe^ZC;0^r1tCZr{Fr`0(NC z>T0c4YcLp46m_{=$z<~M>C@%q<(W+8@#Du&o;;bKpWn4>S4T%jBoaAu=FHWrS8X<% zOePx}8#{LFSU4Qswr$(Jd-rIX7KucIgM$YS97rS*Cr_Tlaah-n{wj*|S@>Zf)JV^~8x2Gcz-7ZEam$UA?`%eSLi% zkH_I~T)1#yWMsr>G(LLtsJ6Ct_wL<2Jv};|PN`H*OiYAAq2l7=ojZ393=Ejf=8G3E zUcP+!!Gi~U=R0Kh!QmGv~cD#M_U#IdvW!_9YHXI*NeZFCMP!&tY zMpHwv0sV$_VhB|Jn+*hSFIK)O3^fz0y;w)HQEw<%^7csgZvS0G!2RP60Ld&xDXLb~ z(-g0~x>gTSn}re-e(+&k1GE^ND9Pd!D`^x#_JM-Jk}=?HazeIHGHr4Z{<1uku9sN;jT0*bq@ZR zy5l(W&#&;^(fMKyE012XSU8p!+6zAE5w2aQpn;?GDKjD1idkWAsSnflj1#qo+1HjNgl{r4E%i_$QP7s;6!vR z8hE*_oFdUOVKPNoBkKn{rBZL#Mj?+>4}=`_V10)I_KOn?5P!A~>PlR>U^)qn0Yi?^ z*F&Y*l%qb9`>$rBac>T_kw``2EM;^4K>9zbzzB=QfHX)^%I(#9J(%6yaGS>Yip)z@ z@t=GD;?C;|7VF0Ix+bks9I8zDY0xO=)+}GYHW><1SXCgUz@46TMePof&={?B;?p)e zZ0y!DARsJ&K^;FQ+4u=eC2TpQ_+vPf`i>z!$UvE+FBkl*75cTVT&T7R;!&%b*|ZEJ z8={SB1GGE`n<=E3#a9zf6Po_lgbEifEnPCSq8#DnjqJt7_lc3%P+X~zG|_-xw>d61 z2TF`G%I6Ts+$IxC@S(Vj#H|iH9aNRLp}SwffcV-JbXkdlVSeGHzfRj z;-!m$?-Lu`joLa1hR}e(to^|X-e!S|k}9Hp0=22jy1OwFb-Nm9k2&54o&0Z@p)Ls| zd?p6CxPyP4Ig3ZQP&&S_ss^g$zU%9$HNRND; z4%e^AlsXu!YD1y$%HE%U`TFj}`^22Zx}EvN7H5;)Wmg+$U?Y5d7Ur>bswq_3ATPx0w-9QSx`*AL7f4I3iUbdbvlHZ_8bb`@^p|+G4|{ zQXHj8)Py5_KA)4)&^Dymj*!?g{WyWSOt7jpG!FPOA8f6zn?k&eurN$ap}~GAtUohu zt2hbPuhCBVdL=MkxMfPB!yv1*XEQC6^A^BR$FQ-}iDRisxq_-wwI|zFMYzh?xSzLj Wc_{4u<=a1<$G!l-{_f-Z-~1P&zb^m) literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/ui/bot.png b/xhiveframework/public/images/ui/bot.png new file mode 100644 index 0000000000000000000000000000000000000000..02aeea037196928000ead041dee8ba434d360a73 GIT binary patch literal 14886 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 + + + + + + + + + + \ No newline at end of file diff --git a/xhiveframework/public/images/ui/bubble-tea-smile.svg b/xhiveframework/public/images/ui/bubble-tea-smile.svg new file mode 100644 index 0000000..457fd70 --- /dev/null +++ b/xhiveframework/public/images/ui/bubble-tea-smile.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/xhiveframework/public/images/ui/bubble-tea-sorry.svg b/xhiveframework/public/images/ui/bubble-tea-sorry.svg new file mode 100644 index 0000000..2f30583 --- /dev/null +++ b/xhiveframework/public/images/ui/bubble-tea-sorry.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/xhiveframework/public/images/up.png b/xhiveframework/public/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..949027e61e82b7b0e2c1c3f6eb546aeb1ac7de95 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1Gy`C7mq@>1p6e@)1uy?F?HkDkQ9l^BJ(XWyvwVo}BUtu55k=czi z<}zj}ai}nVG*_r&J5uDK_k-)mJcl)`N9-q>-Z>~dNul}N1MaX%j~Vq74l1WJABkC# gvEqOlhd=@YXQ0^c^j|XjfDU8uboFyt=akR{0Aw~#l>h($ literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/workflow-builder.gif b/xhiveframework/public/images/workflow-builder.gif new file mode 100644 index 0000000000000000000000000000000000000000..0277c3edc8f7fd7506f83c23f531b502d88b2d94 GIT binary patch literal 785377 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 z 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

      egb8`-VtjW0|r!R8LYd2(0WBr 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

      XvaVjr^&%_yi=5mkALJq}_@5jywXXIm9tkloqTk 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 + + + + + + + diff --git a/xhiveframework/public/images/xhiveframework-framework-logo.png b/xhiveframework/public/images/xhiveframework-framework-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fa3e559cc65042f0638a6c70208e322278289feb GIT binary patch literal 8589 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 F{{Y3m`i=kq literal 0 HcmV?d00001 diff --git a/xhiveframework/public/images/xhiveframework-framework-logo.svg b/xhiveframework/public/images/xhiveframework-framework-logo.svg new file mode 100644 index 0000000..92791e3 --- /dev/null +++ b/xhiveframework/public/images/xhiveframework-framework-logo.svg @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/xhiveframework/public/images/xhiveframework-logo.png b/xhiveframework/public/images/xhiveframework-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fa3e559cc65042f0638a6c70208e322278289feb GIT binary patch literal 8589 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 F{{Y3m`i=kq literal 0 HcmV?d00001 diff --git a/xhiveframework/public/js/bootstrap-4-web.bundle.js b/xhiveframework/public/js/bootstrap-4-web.bundle.js new file mode 100644 index 0000000..f63136b --- /dev/null +++ b/xhiveframework/public/js/bootstrap-4-web.bundle.js @@ -0,0 +1,68 @@ +// multilevel dropdown +$(".dropdown-menu a.dropdown-toggle").on("click", function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + if (!$(this).next().hasClass("show")) { + $(this).parents(".dropdown-menu").first().find(".show").removeClass("show"); + } + var $subMenu = $(this).next(".dropdown-menu"); + $subMenu.toggleClass("show"); + + $(this) + .parents("li.nav-item.dropdown.show") + .on("hidden.bs.dropdown", function () { + $(".dropdown-submenu .show").removeClass("show"); + }); + + return false; +}); + +xhiveframework.get_modal = function (title, content) { + return $( + `

      ` + ); +}; + +xhiveframework.ui.Dialog = class Dialog extends xhiveframework.ui.Dialog { + get_primary_btn() { + return this.$wrapper.find(".modal-footer .btn-primary"); + } + + get_secondary_btn() { + return this.$wrapper.find(".modal-footer .btn-secondary"); + } + + set_primary_action(label, click) { + this.$wrapper.find(".modal-footer").removeClass("hidden"); + return super.set_primary_action(label, click).removeClass("hidden"); + } + + set_secondary_action(click) { + return super.set_secondary_action(click).removeClass("hidden"); + } + + make() { + super.make(); + if (this.fields) { + this.$wrapper.find(".section-body").addClass("w-100"); + } + } +}; diff --git a/xhiveframework/public/js/controls.bundle.js b/xhiveframework/public/js/controls.bundle.js new file mode 100644 index 0000000..ddd9d61 --- /dev/null +++ b/xhiveframework/public/js/controls.bundle.js @@ -0,0 +1,4 @@ +import "air-datepicker/dist/js/datepicker.min.js"; +import "./xhiveframework/form/controls/datepicker_i18n.js"; +import "./xhiveframework/ui/capture.js"; +import "./xhiveframework/form/controls/control.js"; diff --git a/xhiveframework/public/js/data_import_tools.bundle.js b/xhiveframework/public/js/data_import_tools.bundle.js new file mode 100644 index 0000000..51f970a --- /dev/null +++ b/xhiveframework/public/js/data_import_tools.bundle.js @@ -0,0 +1 @@ +import "./xhiveframework/data_import"; diff --git a/xhiveframework/public/js/desk.bundle.js b/xhiveframework/public/js/desk.bundle.js new file mode 100644 index 0000000..a80324d --- /dev/null +++ b/xhiveframework/public/js/desk.bundle.js @@ -0,0 +1,110 @@ +import "./xhiveframework/provide.js"; +import "./xhiveframework/translate.js"; +import "./xhiveframework/class.js"; +import "./xhiveframework/polyfill.js"; +import "./xhiveframework/assets.js"; +import "./xhiveframework/format.js"; +import "./xhiveframework/form/formatters.js"; +import "./xhiveframework/dom.js"; +import "./xhiveframework/ui/messages.js"; +import "./xhiveframework/ui/keyboard.js"; +import "./xhiveframework/ui/colors.js"; +import "./xhiveframework/ui/sidebar.js"; +import "./xhiveframework/ui/link_preview.js"; + +import "./xhiveframework/request.js"; +import "./xhiveframework/socketio_client.js"; +import "./xhiveframework/utils/utils.js"; +import "./xhiveframework/event_emitter.js"; +import "./xhiveframework/router.js"; +import "./xhiveframework/router_history.js"; +import "./xhiveframework/defaults.js"; +import "./xhiveframework/roles_editor.js"; +import "./xhiveframework/module_editor.js"; +import "./xhiveframework/microtemplate.js"; + +import "./xhiveframework/ui/page.html"; +import "./xhiveframework/ui/page.js"; +import "./xhiveframework/ui/slides.js"; +// import "./xhiveframework/ui/onboarding_dialog.js"; +import "./xhiveframework/ui/find.js"; +import "./xhiveframework/ui/iconbar.js"; +import "./xhiveframework/form/layout.js"; +import "./xhiveframework/form/section.js"; +import "./xhiveframework/form/tab.js"; +import "./xhiveframework/form/column.js"; +import "./xhiveframework/ui/field_group.js"; +import "./xhiveframework/form/link_selector.js"; +import "./xhiveframework/form/multi_select_dialog.js"; +import "./xhiveframework/ui/dialog.js"; +import "./xhiveframework/ui/capture.js"; +import "./xhiveframework/ui/app_icon.js"; +import "./xhiveframework/ui/theme_switcher.js"; + +import "./xhiveframework/model/model.js"; +import "./xhiveframework/db.js"; +import "./xhiveframework/model/meta.js"; +import "./xhiveframework/model/sync.js"; +import "./xhiveframework/model/create_new.js"; +import "./xhiveframework/model/perm.js"; +import "./xhiveframework/model/workflow.js"; +import "./xhiveframework/model/user_settings.js"; + +import "./xhiveframework/utils/user.js"; +import "./xhiveframework/utils/common.js"; +import "./xhiveframework/utils/urllib.js"; +import "./xhiveframework/utils/pretty_date.js"; +import "./xhiveframework/utils/tools.js"; +import "./xhiveframework/utils/datetime.js"; +import "./xhiveframework/utils/number_format.js"; +import "./xhiveframework/utils/help.js"; +import "./xhiveframework/utils/help_links.js"; +import "./xhiveframework/utils/address_and_contact.js"; +import "./xhiveframework/utils/preview_email.js"; +import "./xhiveframework/utils/file_manager.js"; +import "./xhiveframework/utils/diffview"; +import "./xhiveframework/utils/datatable.js"; + +import "./xhiveframework/upload.js"; +import "./xhiveframework/ui/tree.js"; + +import "./xhiveframework/views/container.js"; +import "./xhiveframework/views/breadcrumbs.js"; +import "./xhiveframework/views/factory.js"; +import "./xhiveframework/views/pageview.js"; + +import "./xhiveframework/ui/toolbar/awesome_bar.js"; +// import "./xhiveframework/ui/toolbar/energy_points_notifications.js"; +import "./xhiveframework/ui/notifications/notifications.js"; +import "./xhiveframework/ui/toolbar/search.js"; +import "./xhiveframework/ui/toolbar/tag_utils.js"; +import "./xhiveframework/ui/toolbar/search.html"; +import "./xhiveframework/ui/toolbar/search_utils.js"; +import "./xhiveframework/ui/toolbar/about.js"; +import "./xhiveframework/ui/toolbar/navbar.html"; +import "./xhiveframework/ui/toolbar/toolbar.js"; +// import "./xhiveframework/ui/toolbar/notifications.js"; +import "./xhiveframework/views/communication.js"; +import "./xhiveframework/views/translation_manager.js"; +import "./xhiveframework/views/workspace/workspace.js"; +import "./xhiveframework/views/workspace/blocks/index.js"; + +import "./xhiveframework/widgets/widget_group.js"; + +import "./xhiveframework/ui/sort_selector.html"; +import "./xhiveframework/ui/sort_selector.js"; + +import "./xhiveframework/change_log.html"; +import "./xhiveframework/ui/workspace_loading_skeleton.html"; +import "./xhiveframework/ui/workspace_sidebar_loading_skeleton.html"; +import "./xhiveframework/desk.js"; +import "./xhiveframework/query_string.js"; + +// import "./xhiveframework/ui/comment.js"; + +import "./xhiveframework/utils/energy_point_utils.js"; +import "./xhiveframework/utils/dashboard_utils.js"; +import "./xhiveframework/ui/chart.js"; +import "./xhiveframework/ui/datatable.js"; +import "./xhiveframework/ui/driver.js"; +import "./xhiveframework/scanner"; diff --git a/xhiveframework/public/js/dialog.bundle.js b/xhiveframework/public/js/dialog.bundle.js new file mode 100644 index 0000000..d928ad9 --- /dev/null +++ b/xhiveframework/public/js/dialog.bundle.js @@ -0,0 +1,7 @@ +import "./xhiveframework/dom.js"; +import "./xhiveframework/form/formatters.js"; +import "./xhiveframework/form/layout.js"; +import "./xhiveframework/ui/field_group.js"; +import "./xhiveframework/form/link_selector.js"; +import "./xhiveframework/form/multi_select_dialog.js"; +import "./xhiveframework/ui/dialog.js"; diff --git a/xhiveframework/public/js/form.bundle.js b/xhiveframework/public/js/form.bundle.js new file mode 100644 index 0000000..c500940 --- /dev/null +++ b/xhiveframework/public/js/form.bundle.js @@ -0,0 +1,16 @@ +import "./xhiveframework/form/templates/address_list.html"; +import "./xhiveframework/form/templates/contact_list.html"; +import "./xhiveframework/form/templates/form_dashboard.html"; +import "./xhiveframework/form/templates/form_footer.html"; +import "./xhiveframework/form/templates/form_links.html"; +import "./xhiveframework/form/templates/form_sidebar.html"; +import "./xhiveframework/form/templates/print_layout.html"; +import "./xhiveframework/form/templates/report_links.html"; +import "./xhiveframework/form/templates/set_sharing.html"; +import "./xhiveframework/form/templates/timeline_message_box.html"; +import "./xhiveframework/form/templates/users_in_sidebar.html"; + +import "./xhiveframework/views/formview.js"; +import "./xhiveframework/form/form.js"; +import "./xhiveframework/meta_tag.js"; +import "./xhiveframework/doctype/"; diff --git a/xhiveframework/public/js/form_builder/FormBuilder.vue b/xhiveframework/public/js/form_builder/FormBuilder.vue new file mode 100644 index 0000000..63ed4ba --- /dev/null +++ b/xhiveframework/public/js/form_builder/FormBuilder.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/AddFieldButton.vue b/xhiveframework/public/js/form_builder/components/AddFieldButton.vue new file mode 100644 index 0000000..0fce41f --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/AddFieldButton.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Autocomplete.vue b/xhiveframework/public/js/form_builder/components/Autocomplete.vue new file mode 100644 index 0000000..c5bc0f2 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Autocomplete.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Column.vue b/xhiveframework/public/js/form_builder/components/Column.vue new file mode 100644 index 0000000..3fbc67e --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Column.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Dropdown.vue b/xhiveframework/public/js/form_builder/components/Dropdown.vue new file mode 100644 index 0000000..0c4b108 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Dropdown.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/EditableInput.vue b/xhiveframework/public/js/form_builder/components/EditableInput.vue new file mode 100644 index 0000000..3459346 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/EditableInput.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Field.vue b/xhiveframework/public/js/form_builder/components/Field.vue new file mode 100644 index 0000000..1b419f1 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Field.vue @@ -0,0 +1,214 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/FieldProperties.vue b/xhiveframework/public/js/form_builder/components/FieldProperties.vue new file mode 100644 index 0000000..b1f9f0c --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/FieldProperties.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/SearchBox.vue b/xhiveframework/public/js/form_builder/components/SearchBox.vue new file mode 100644 index 0000000..2b22002 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/SearchBox.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Section.vue b/xhiveframework/public/js/form_builder/components/Section.vue new file mode 100644 index 0000000..443642d --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Section.vue @@ -0,0 +1,390 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Sidebar.vue b/xhiveframework/public/js/form_builder/components/Sidebar.vue new file mode 100644 index 0000000..1ae9c0f --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Sidebar.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/Tabs.vue b/xhiveframework/public/js/form_builder/components/Tabs.vue new file mode 100644 index 0000000..881891f --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/Tabs.vue @@ -0,0 +1,337 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/AttachControl.vue b/xhiveframework/public/js/form_builder/components/controls/AttachControl.vue new file mode 100644 index 0000000..6d8718d --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/AttachControl.vue @@ -0,0 +1,20 @@ + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/ButtonControl.vue b/xhiveframework/public/js/form_builder/components/controls/ButtonControl.vue new file mode 100644 index 0000000..00ff6a6 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/ButtonControl.vue @@ -0,0 +1,28 @@ + + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/CheckControl.vue b/xhiveframework/public/js/form_builder/components/controls/CheckControl.vue new file mode 100644 index 0000000..656c1b4 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/CheckControl.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/CodeControl.vue b/xhiveframework/public/js/form_builder/components/controls/CodeControl.vue new file mode 100644 index 0000000..15b32ee --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/CodeControl.vue @@ -0,0 +1,70 @@ + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/DataControl.vue b/xhiveframework/public/js/form_builder/components/controls/DataControl.vue new file mode 100644 index 0000000..dbf4521 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/DataControl.vue @@ -0,0 +1,96 @@ + + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/FetchFromControl.vue b/xhiveframework/public/js/form_builder/components/controls/FetchFromControl.vue new file mode 100644 index 0000000..4635e7a --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/FetchFromControl.vue @@ -0,0 +1,100 @@ + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/GeolocationControl.vue b/xhiveframework/public/js/form_builder/components/controls/GeolocationControl.vue new file mode 100644 index 0000000..a7222c1 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/GeolocationControl.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/ImageControl.vue b/xhiveframework/public/js/form_builder/components/controls/ImageControl.vue new file mode 100644 index 0000000..5e29787 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/ImageControl.vue @@ -0,0 +1,14 @@ + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/LinkControl.vue b/xhiveframework/public/js/form_builder/components/controls/LinkControl.vue new file mode 100644 index 0000000..f798194 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/LinkControl.vue @@ -0,0 +1,90 @@ + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/RatingControl.vue b/xhiveframework/public/js/form_builder/components/controls/RatingControl.vue new file mode 100644 index 0000000..28dd2cc --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/RatingControl.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/SelectControl.vue b/xhiveframework/public/js/form_builder/components/controls/SelectControl.vue new file mode 100644 index 0000000..82e7478 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/SelectControl.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/SignatureControl.vue b/xhiveframework/public/js/form_builder/components/controls/SignatureControl.vue new file mode 100644 index 0000000..f5d361e --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/SignatureControl.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/TableControl.vue b/xhiveframework/public/js/form_builder/components/controls/TableControl.vue new file mode 100644 index 0000000..81ec82b --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/TableControl.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/xhiveframework/public/js/form_builder/components/controls/TextControl.vue b/xhiveframework/public/js/form_builder/components/controls/TextControl.vue new file mode 100644 index 0000000..0947c27 --- /dev/null +++ b/xhiveframework/public/js/form_builder/components/controls/TextControl.vue @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/xhiveframework/public/js/print_format_builder/PrintFormat.vue b/xhiveframework/public/js/print_format_builder/PrintFormat.vue new file mode 100644 index 0000000..a08286d --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/PrintFormat.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/xhiveframework/public/js/print_format_builder/PrintFormatBuilder.vue b/xhiveframework/public/js/print_format_builder/PrintFormatBuilder.vue new file mode 100644 index 0000000..d436b4f --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/PrintFormatBuilder.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/xhiveframework/public/js/print_format_builder/PrintFormatControls.vue b/xhiveframework/public/js/print_format_builder/PrintFormatControls.vue new file mode 100644 index 0000000..89b6e9e --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/PrintFormatControls.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/xhiveframework/public/js/print_format_builder/PrintFormatSection.vue b/xhiveframework/public/js/print_format_builder/PrintFormatSection.vue new file mode 100644 index 0000000..f9d847c --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/PrintFormatSection.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/xhiveframework/public/js/print_format_builder/print_format_builder.bundle.js b/xhiveframework/public/js/print_format_builder/print_format_builder.bundle.js new file mode 100644 index 0000000..989825e --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/print_format_builder.bundle.js @@ -0,0 +1,62 @@ +import { createApp, watch } from "vue"; +import PrintFormatBuilderComponent from "./PrintFormatBuilder.vue"; + +class PrintFormatBuilder { + constructor({ wrapper, page, print_format }) { + this.$wrapper = $(wrapper); + this.page = page; + this.print_format = print_format; + + this.page.clear_actions(); + this.page.clear_icons(); + this.page.clear_custom_actions(); + + this.page.set_title(__("Editing {0}", [this.print_format])); + this.page.set_primary_action(__("Save"), () => { + this.$component.$store.save_changes(); + }); + let $toggle_preview_btn = this.page.add_button(__("Show Preview"), () => { + this.$component.toggle_preview(); + }); + let $reset_changes_btn = this.page.add_button(__("Reset Changes"), () => + this.$component.$store.reset_changes() + ); + this.page.add_menu_item(__("Edit Print Format"), () => { + xhiveframework.set_route("Form", "Print Format", this.print_format); + }); + this.page.add_menu_item(__("Change Print Format"), () => { + xhiveframework.set_route("print-format-builder-beta"); + }); + + let app = createApp(PrintFormatBuilderComponent, { print_format_name: print_format }); + SetVueGlobals(app); + this.$component = app.mount(this.$wrapper.get(0)); + + watch( + () => this.$component.$store.dirty, + (dirty) => { + if (dirty.value) { + this.page.set_indicator(__("Not Saved"), "orange"); + $toggle_preview_btn.hide(); + $reset_changes_btn.show(); + } else { + this.page.clear_indicator(); + $toggle_preview_btn.show(); + $reset_changes_btn.hide(); + } + }, + { deep: true } + ); + + watch( + () => this.$component.show_preview, + (value) => { + $toggle_preview_btn.text(value ? __("Hide Preview") : __("Show Preview")); + } + ); + } +} + +xhiveframework.provide("xhiveframework.ui"); +xhiveframework.ui.PrintFormatBuilder = PrintFormatBuilder; +export default PrintFormatBuilder; diff --git a/xhiveframework/public/js/print_format_builder/store.js b/xhiveframework/public/js/print_format_builder/store.js new file mode 100644 index 0000000..5ffea90 --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/store.js @@ -0,0 +1,159 @@ +import { create_default_layout, pluck } from "./utils"; +import { watch, ref, inject, computed, nextTick } from "vue"; + +export function getStore(print_format_name) { + // variables + let letterhead_name = ref(null); + let print_format = ref(null); + let letterhead = ref(null); + let doctype = ref(null); + let meta = ref(null); + let layout = ref(null); + let dirty = ref(false); + let edit_letterhead = ref(false); + + // methods + function fetch() { + return new Promise((resolve) => { + xhiveframework.model.clear_doc("Print Format", print_format_name); + xhiveframework.model.with_doc("Print Format", print_format_name, () => { + let _print_format = xhiveframework.get_doc("Print Format", print_format_name); + xhiveframework.model.with_doctype(_print_format.doc_type, () => { + meta.value = xhiveframework.get_meta(_print_format.doc_type); + print_format.value = _print_format; + layout.value = get_layout(); + nextTick(() => (dirty.value = false)); + edit_letterhead.value = false; + resolve(); + }); + }); + }); + } + function update({ fieldname, value }) { + print_format.value[fieldname] = value; + } + function save_changes() { + xhiveframework.dom.freeze(__("Saving...")); + + layout.value.sections = layout.value.sections + .filter((section) => !section.remove) + .map((section) => { + section.columns = section.columns.map((column) => { + column.fields = column.fields + .filter((df) => !df.remove) + .map((df) => { + if (df.table_columns) { + df.table_columns = df.table_columns.map((tf) => { + return pluck(tf, [ + "label", + "fieldname", + "fieldtype", + "options", + "width", + "field_template", + ]); + }); + } + return pluck(df, [ + "label", + "fieldname", + "fieldtype", + "options", + "table_columns", + "html", + "field_template", + ]); + }); + return column; + }); + return section; + }); + + print_format.value.format_data = JSON.stringify(layout.value); + + xhiveframework + .call("xhiveframework.client.save", { + doc: print_format.value, + }) + .then(() => { + if (letterhead.value && letterhead.value._dirty) { + return xhiveframework + .call("xhiveframework.client.save", { + doc: letterhead.value, + }) + .then((r) => (letterhead.value = r.message)); + } + }) + .then(() => fetch()) + .always(() => { + xhiveframework.dom.unfreeze(); + }); + } + function reset_changes() { + fetch(); + } + function get_layout() { + if (print_format.value) { + if (typeof print_format.value.format_data == "string") { + return JSON.parse(print_format.value.format_data); + } + return print_format.value.format_data; + } + return null; + } + function get_default_layout() { + return create_default_layout(meta.value, print_format.value); + } + function change_letterhead(_letterhead) { + return xhiveframework.db.get_doc("Letter Head", _letterhead).then((doc) => { + letterhead.value = doc; + }); + } + + // watch + watch(layout, () => { + dirty.value = true; + }); + watch(print_format, () => { + dirty.value = true; + }); + + return { + letterhead_name, + print_format, + letterhead, + doctype, + meta, + layout, + dirty, + edit_letterhead, + fetch, + update, + save_changes, + reset_changes, + get_layout, + get_default_layout, + change_letterhead, + }; +} + +export function useStore() { + // inject store + let store = ref(inject("$store")); + + // computed + let print_format = computed(() => { + return store.value.print_format; + }); + let layout = computed(() => { + return store.value.layout; + }); + let letterhead = computed(() => { + return store.value.letterhead; + }); + let meta = computed(() => { + return store.value.meta; + }); + + return { print_format, layout, letterhead, meta, store }; +} diff --git a/xhiveframework/public/js/print_format_builder/utils.js b/xhiveframework/public/js/print_format_builder/utils.js new file mode 100644 index 0000000..bc5fe2c --- /dev/null +++ b/xhiveframework/public/js/print_format_builder/utils.js @@ -0,0 +1,152 @@ +export function create_default_layout(meta, print_format) { + let layout = { + header: get_default_header(meta), + sections: [], + }; + + let section = null, + column = null; + + function set_column(df) { + if (!section) { + set_section(); + } + column = get_new_column(df); + section.columns.push(column); + } + + function set_section(df) { + section = get_new_section(df); + column = null; + layout.sections.push(section); + } + + function get_new_section(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + columns: [], + }; + } + + function get_new_column(df) { + if (!df) { + df = { label: "" }; + } + return { + label: df.label || "", + fields: [], + }; + } + + for (let df of meta.fields) { + if (df.fieldname) { + // make a copy to avoid mutation bugs + df = JSON.parse(JSON.stringify(df)); + } else { + continue; + } + + if (df.fieldtype === "Section Break") { + set_section(df); + } else if (df.fieldtype === "Column Break") { + set_column(df); + } else if (df.label) { + if (!column) set_column(); + + if (!df.print_hide) { + let field = { + label: df.label, + fieldname: df.fieldname, + fieldtype: df.fieldtype, + options: df.options, + }; + + let field_template = get_field_template(print_format, df.fieldname); + if (field_template) { + field.label = `${__(df.label, null, df.parent)} (${__("Field Template")})`; + field.fieldtype = "Field Template"; + field.field_template = field_template.name; + field.fieldname = df.fieldname = "_template"; + } + + if (df.fieldtype === "Table") { + field.table_columns = get_table_columns(df); + } + + column.fields.push(field); + section.has_fields = true; + } + } + } + + // remove empty sections + layout.sections = layout.sections.filter((section) => section.has_fields); + + return layout; +} + +export function get_table_columns(df) { + let table_columns = []; + let table_fields = xhiveframework.get_meta(df.options).fields; + let total_width = 0; + for (let tf of table_fields) { + if ( + !["Section Break", "Column Break"].includes(tf.fieldtype) && + !tf.print_hide && + df.label && + total_width < 100 + ) { + let width = + typeof tf.width == "number" && tf.width < 100 ? tf.width : tf.width ? 20 : 10; + table_columns.push({ + label: tf.label, + fieldname: tf.fieldname, + fieldtype: tf.fieldtype, + options: tf.options, + width, + }); + total_width += width; + } + } + return table_columns; +} + +function get_field_template(print_format, fieldname) { + let templates = print_format.__onload.print_templates || {}; + for (let template of templates) { + if (template.field === fieldname) { + return template; + } + } + return null; +} + +function get_default_header(meta) { + return `
      +

      ${meta.name}

      +

      {{ doc.name }}

      +
      `; +} + +export function pluck(object, keys) { + let out = {}; + for (let key of keys) { + if (key in object) { + out[key] = object[key]; + } + } + return out; +} + +export function get_image_dimensions(src) { + return new Promise((resolve) => { + let img = new Image(); + img.onload = function () { + resolve({ width: this.width, height: this.height }); + }; + img.src = src; + }); +} diff --git a/xhiveframework/public/js/report.bundle.js b/xhiveframework/public/js/report.bundle.js new file mode 100644 index 0000000..1b01674 --- /dev/null +++ b/xhiveframework/public/js/report.bundle.js @@ -0,0 +1,8 @@ +import "./xhiveframework/views/reports/report_factory.js"; +import "./xhiveframework/views/reports/report_view.js"; +import "./xhiveframework/views/reports/query_report.js"; +import "./xhiveframework/views/reports/print_grid.html"; +import "./xhiveframework/views/reports/print_tree.html"; +import "./xhiveframework/ui/group_by/group_by.html"; +import "./xhiveframework/ui/group_by/group_by.js"; +import "./xhiveframework/views/reports/report_utils.js"; diff --git a/xhiveframework/public/js/sentry.bundle.js b/xhiveframework/public/js/sentry.bundle.js new file mode 100644 index 0000000..3493861 --- /dev/null +++ b/xhiveframework/public/js/sentry.bundle.js @@ -0,0 +1,23 @@ +import * as Sentry from "@sentry/browser"; + +Sentry.init({ + dsn: xhiveframework.boot.sentry_dsn, + release: xhiveframework?.boot?.versions?.xhiveframework, + autoSessionTracking: false, + initialScope: { + // don't use xhiveframework.session.user, it's set much later and will fail because of async loading + user: { id: xhiveframework.boot.sitename }, + tags: { xhiveframework_user: xhiveframework.boot.user.name ?? "Unidentified" }, + }, + beforeSend(event, hint) { + // Check if it was caused by xhiveframework.throw() + if ( + hint.originalException instanceof Error && + hint.originalException.stack && + hint.originalException.stack.includes("xhiveframework.throw") + ) { + return null; + } + return event; + }, +}); diff --git a/xhiveframework/public/js/telemetry.bundle.js b/xhiveframework/public/js/telemetry.bundle.js new file mode 100644 index 0000000..250a1c5 --- /dev/null +++ b/xhiveframework/public/js/telemetry.bundle.js @@ -0,0 +1 @@ +import "./telemetry/index.js"; diff --git a/xhiveframework/public/js/telemetry/index.js b/xhiveframework/public/js/telemetry/index.js new file mode 100644 index 0000000..5b0c053 --- /dev/null +++ b/xhiveframework/public/js/telemetry/index.js @@ -0,0 +1,86 @@ +import "../lib/posthog.js"; + +class TelemetryManager { + constructor() { + this.enabled = false; + + this.project_id = xhiveframework.boot.posthog_project_id; + this.telemetry_host = xhiveframework.boot.posthog_host; + this.site_age = xhiveframework.boot.telemetry_site_age; + if (cint(xhiveframework.boot.enable_telemetry) && this.project_id && this.telemetry_host) { + this.enabled = true; + } + } + + initialize() { + if (!this.enabled) return; + let disable_decide = !this.should_record_session(); + try { + posthog.init(this.project_id, { + api_host: this.telemetry_host, + autocapture: false, + capture_pageview: false, + capture_pageleave: false, + advanced_disable_decide: disable_decide, + }); + posthog.identify(xhiveframework.boot.sitename); + this.send_heartbeat(); + this.register_pageview_handler(); + } catch (e) { + console.trace("Failed to initialize telemetry", e); + this.enabled = false; + } + } + + capture(event, app, props) { + if (!this.enabled) return; + posthog.capture(`${app}_${event}`, props); + } + + disable() { + this.enabled = false; + } + + can_enable() { + if (cint(navigator.doNotTrack)) { + return false; + } + let posthog_available = Boolean(this.telemetry_host && this.project_id); + let sentry_available = Boolean(xhiveframework.boot.sentry_dsn); + return posthog_available || sentry_available; + } + + send_heartbeat() { + const KEY = "ph_last_heartbeat"; + const now = xhiveframework.datetime.system_datetime(true); + const last = localStorage.getItem(KEY); + + if (!last || moment(now).diff(moment(last), "hours") > 12) { + localStorage.setItem(KEY, now.toISOString()); + this.capture("heartbeat", "xhiveframework", { xhiveframework_version: xhiveframework.boot?.versions?.xhiveframework }); + } + } + + register_pageview_handler() { + if (this.site_age && this.site_age > 6) { + return; + } + + xhiveframework.router.on("change", () => { + posthog.capture("$pageview"); + }); + } + + should_record_session() { + let start = xhiveframework.boot.sysdefaults.session_recording_start; + if (!start) return; + + let start_datetime = xhiveframework.datetime.str_to_obj(start); + let now = xhiveframework.datetime.now_datetime(); + // if user allowed recording only record for first 2 hours, never again. + return xhiveframework.datetime.get_minute_diff(now, start_datetime) < 120; + } +} + +xhiveframework.telemetry = new TelemetryManager(); +xhiveframework.telemetry.initialize(); diff --git a/xhiveframework/public/js/user_profile_controller.bundle.js b/xhiveframework/public/js/user_profile_controller.bundle.js new file mode 100644 index 0000000..1bf0b51 --- /dev/null +++ b/xhiveframework/public/js/user_profile_controller.bundle.js @@ -0,0 +1 @@ +import "../../desk/page/user_profile/user_profile_controller"; diff --git a/xhiveframework/public/js/video_player.bundle.js b/xhiveframework/public/js/video_player.bundle.js new file mode 100644 index 0000000..cd3f972 --- /dev/null +++ b/xhiveframework/public/js/video_player.bundle.js @@ -0,0 +1,3 @@ +import Plyr from "plyr/dist/plyr.polyfilled"; + +xhiveframework.Plyr = Plyr; diff --git a/xhiveframework/public/js/web_form.bundle.js b/xhiveframework/public/js/web_form.bundle.js new file mode 100644 index 0000000..6994d9c --- /dev/null +++ b/xhiveframework/public/js/web_form.bundle.js @@ -0,0 +1,7 @@ +import "./controls.bundle.js"; +import "./dialog.bundle.js"; +import "./lib/moment.js"; +import "./xhiveframework/ui/keyboard.js"; +import "./xhiveframework/utils/datetime.js"; +import "./xhiveframework/web_form/webform_script.js"; +import "./bootstrap-4-web.bundle.js"; diff --git a/xhiveframework/public/js/workflow_builder/WorkflowBuilder.vue b/xhiveframework/public/js/workflow_builder/WorkflowBuilder.vue new file mode 100644 index 0000000..1c54c49 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/WorkflowBuilder.vue @@ -0,0 +1,369 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/components/ActionNode.vue b/xhiveframework/public/js/workflow_builder/components/ActionNode.vue new file mode 100644 index 0000000..040b23a --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/ActionNode.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/components/ConnectionLine.vue b/xhiveframework/public/js/workflow_builder/components/ConnectionLine.vue new file mode 100644 index 0000000..33ecbe5 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/ConnectionLine.vue @@ -0,0 +1,34 @@ + + diff --git a/xhiveframework/public/js/workflow_builder/components/Properties.vue b/xhiveframework/public/js/workflow_builder/components/Properties.vue new file mode 100644 index 0000000..f8a9fbe --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/Properties.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/components/Sidebar.vue b/xhiveframework/public/js/workflow_builder/components/Sidebar.vue new file mode 100644 index 0000000..d83ab6d --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/Sidebar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/components/StateNode.vue b/xhiveframework/public/js/workflow_builder/components/StateNode.vue new file mode 100644 index 0000000..373c7c1 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/StateNode.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/components/TransitionEdge.vue b/xhiveframework/public/js/workflow_builder/components/TransitionEdge.vue new file mode 100644 index 0000000..b1a4e13 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/components/TransitionEdge.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/xhiveframework/public/js/workflow_builder/globals.js b/xhiveframework/public/js/workflow_builder/globals.js new file mode 100644 index 0000000..0356bdc --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/globals.js @@ -0,0 +1,56 @@ +import AttachControl from "../form_builder/components/controls/AttachControl.vue"; +import ButtonControl from "../form_builder/components/controls/ButtonControl.vue"; +import CheckControl from "../form_builder/components/controls/CheckControl.vue"; +import CodeControl from "../form_builder/components/controls/CodeControl.vue"; +import DataControl from "../form_builder/components/controls/DataControl.vue"; +import GeolocationControl from "../form_builder/components/controls/GeolocationControl.vue"; +import ImageControl from "../form_builder/components/controls/ImageControl.vue"; +import LinkControl from "../form_builder/components/controls/LinkControl.vue"; +import RatingControl from "../form_builder/components/controls/RatingControl.vue"; +import SelectControl from "../form_builder/components/controls/SelectControl.vue"; +import SignatureControl from "../form_builder/components/controls/SignatureControl.vue"; +import TableControl from "../form_builder/components/controls/TableControl.vue"; +import TextControl from "../form_builder/components/controls/TextControl.vue"; +import TextEditorControl from "../form_builder/components/controls/TextEditorControl.vue"; + +export function registerGlobalComponents(app) { + app.component("AttachControl", AttachControl) + .component("AttachImageControl", AttachControl) + .component("AutocompleteControl", DataControl) + .component("BarcodeControl", DataControl) + .component("ButtonControl", ButtonControl) + .component("CheckControl", CheckControl) + .component("CodeControl", CodeControl) + .component("ColorControl", DataControl) + .component("CurrencyControl", DataControl) + .component("DataControl", DataControl) + .component("DateControl", DataControl) + .component("DatetimeControl", DataControl) + .component("DurationControl", DataControl) + .component("DynamicLinkControl", DataControl) + .component("FloatControl", DataControl) + .component("GeolocationControl", GeolocationControl) + .component("HeadingControl", ButtonControl) + .component("HTMLControl", DataControl) + .component("HTMLEditorControl", CodeControl) + .component("IconControl", DataControl) + .component("ImageControl", ImageControl) + .component("IntControl", DataControl) + .component("JSONControl", CodeControl) + .component("LinkControl", LinkControl) + .component("LongTextControl", TextControl) + .component("MarkdownEditorControl", CodeControl) + .component("PasswordControl", DataControl) + .component("PercentControl", DataControl) + .component("PhoneControl", DataControl) + .component("ReadOnlyControl", DataControl) + .component("RatingControl", RatingControl) + .component("SelectControl", SelectControl) + .component("SignatureControl", SignatureControl) + .component("SmallTextControl", TextControl) + .component("TableControl", TableControl) + .component("TableMultiSelectControl", DataControl) + .component("TextControl", TextControl) + .component("TextEditorControl", TextEditorControl) + .component("TimeControl", DataControl); +} diff --git a/xhiveframework/public/js/workflow_builder/store.js b/xhiveframework/public/js/workflow_builder/store.js new file mode 100644 index 0000000..3f69a0a --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/store.js @@ -0,0 +1,228 @@ +import { defineStore } from "pinia"; +import { ref } from "vue"; +import { get_workflow_elements, validate_transitions } from "./utils"; +import { useManualRefHistory, onKeyDown } from "@vueuse/core"; + +export const useStore = defineStore("workflow-builder-store", () => { + let workflow_name = ref(null); + let workflow_doc = ref(null); + let workflow_doc_fields = ref([]); + let workflow = ref({ elements: [], selected: null }); + let workflowfields = ref([]); + let statefields = ref([]); + let transitionfields = ref([]); + let ref_history = ref(null); + + async function fetch() { + await xhiveframework.model.clear_doc("Workflow", workflow_name.value); + await xhiveframework.model.with_doc("Workflow", workflow_name.value); + + workflow_doc.value = xhiveframework.get_doc("Workflow", workflow_name.value); + await xhiveframework.model.with_doctype(workflow_doc.value.document_type); + + if (!workflowfields.value.length) { + await xhiveframework.model.with_doctype("Workflow"); + workflowfields.value = xhiveframework.get_meta("Workflow").fields; + } + + if (!statefields.value.length) { + await xhiveframework.model.with_doctype("Workflow Document State"); + statefields.value = xhiveframework.get_meta("Workflow Document State").fields; + } + + if (!transitionfields.value.length) { + await xhiveframework.model.with_doctype("Workflow Transition"); + transitionfields.value = xhiveframework.get_meta("Workflow Transition").fields; + } + + if (!workflow_doc_fields.value.length) { + let doc_type = workflow_doc.value.document_type; + await xhiveframework.model.with_doctype(doc_type); + workflow_doc_fields.value = xhiveframework.meta + .get_docfields(doc_type, null, { + fieldtype: ["not in", xhiveframework.model.no_value_type], + }) + .sort((a, b) => { + if (a.label && b.label) { + return a.label.localeCompare(b.label); + } + }) + .map((df) => ({ + label: `${df.label || __("No Label")} (${df.fieldtype})`, + value: df.fieldname, + })); + } + + const workflow_data = + (workflow_doc.value.workflow_data && + typeof workflow_doc.value.workflow_data == "string" && + JSON.parse(workflow_doc.value.workflow_data)) || + []; + + workflow.value.elements = get_workflow_elements(workflow_doc.value, workflow_data); + + setup_undo_redo(); + setup_breadcrumbs(); + } + + function reset_changes() { + fetch(); + } + + async function save_changes() { + xhiveframework.dom.freeze(__("Saving...")); + + try { + let doc = workflow_doc.value; + doc.states = get_updated_states(); + doc.transitions = get_updated_transitions(); + validate_workflow(doc); + const workflow_data = clean_workflow_data(); + doc.workflow_data = JSON.stringify(workflow_data); + await xhiveframework.call("xhiveframework.client.save", { doc }); + xhiveframework.toast("Workflow updated successfully"); + fetch(); + } catch (e) { + console.error(e); + } finally { + xhiveframework.dom.unfreeze(); + } + } + + function validate_workflow(doc) { + if (doc.is_active && (!doc.states.length || !doc.transitions.length)) { + let message = "Workflow must have atleast one state and transition"; + xhiveframework.throw({ + message: __(message), + title: __("Missing Values Required"), + indicator: "orange", + }); + } + } + + function clean_workflow_data() { + return workflow.value.elements.map((el) => { + const { + selected, + dragging, + resizing, + data, + events, + initialized, + sourceNode, + targetNode, + ...obj + } = el; + + if (el.type == "action") { + obj.data = { + from_id: data.from_id, + to_id: data.to_id, + }; + } + + return obj; + }); + } + + function setup_breadcrumbs() { + let breadcrumbs = ` +
    • ${__("Workflow")}
    • +
    • ${__(workflow_name.value)}
    • +
    • ${__("Workflow Builder")}
    • + `; + xhiveframework.breadcrumbs.clear(); + xhiveframework.breadcrumbs.$breadcrumbs.append(breadcrumbs); + } + + function get_state_df(data) { + let doc_status_map = { + Draft: 0, + Submitted: 1, + Cancelled: 2, + }; + data.doc_status = doc_status_map[data.doc_status]; + return data; + } + + function get_updated_states() { + let states = []; + workflow.value.elements.forEach((element) => { + if (element.type == "state") { + element.data.workflow_builder_id = element.id; + states.push(get_state_df(element.data)); + } + }); + return states; + } + + function get_transition_df(data) { + return data; + } + + function get_updated_transitions() { + let transitions = []; + let actions = []; + + workflow.value.elements.forEach((element) => { + if (element.type == "action") { + element.data.workflow_builder_id = element.id; + actions.push(element); + } + }); + + actions.forEach((action) => { + let states = workflow.value.elements.filter((e) => e.type == "state"); + let state = states.find((state) => state.data.state == action.data.from); + let next_state = states.find((state) => state.data.state == action.data.to); + let error = validate_transitions(state.data, next_state.data); + if (error) { + xhiveframework.throw({ + message: error, + title: __("Invalid Transition"), + }); + } + transitions.push( + get_transition_df({ + ...action.data, + state: action.data.from, + next_state: action.data.to, + }) + ); + }); + + return transitions; + } + + let undo_redo_keyboard_event = () => + onKeyDown(true, (e) => { + if (!ref_history.value) return; + if (e.ctrlKey || e.metaKey) { + if (e.key === "z" && !e.shiftKey && ref_history.value.canUndo) { + ref_history.value.undo(); + } else if (e.key === "z" && e.shiftKey && ref_history.value.canRedo) { + ref_history.value.redo(); + } + } + }); + + function setup_undo_redo() { + ref_history.value = useManualRefHistory(workflow, { clone: true }); + undo_redo_keyboard_event(); + } + + return { + workflow_name, + workflow_doc, + workflow_doc_fields, + workflow, + workflowfields, + statefields, + transitionfields, + ref_history, + fetch, + reset_changes, + save_changes, + setup_undo_redo, + }; +}); diff --git a/xhiveframework/public/js/workflow_builder/utils.js b/xhiveframework/public/js/workflow_builder/utils.js new file mode 100644 index 0000000..6f1fcb9 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/utils.js @@ -0,0 +1,189 @@ +export function get_workflow_elements(workflow, workflow_data) { + let elements = []; + let states = {}; + let actions = {}; + let transitions = {}; + + let x = 150; + let y = 100; + + workflow_data.forEach((node) => { + if (node.type == "state") { + states[node.id] = node; + } else if (node.type == "action") { + actions[node.id] = node; + } else if (node.type == "transition") { + transitions[`edge-${node.source}-${node.target}`] = node; + + if (node.source.startsWith("action-")) { + const action = actions[node.source]; + if (!action.data.to_id) { + action.data.to_id = node.target; + } + node.sourceNode = action; + node.targetNode = states[node.target]; + } else { + const action = actions[node.target]; + if (!action.data.from_id) { + action.data.from_id = node.source; + } + node.targetNode = action; + node.sourceNode = states[node.source]; + } + } + }); + + function state_obj(id, data) { + let state = states[id]; + + if (state) { + state.data = data; + } else { + state = { + id: id.toString(), + type: "state", + position: { x, y }, + data, + }; + } + + Object.assign(state, { + initialized: true, + selected: false, + dragging: false, + resizing: false, + }); + return (states[id] = state); + } + + function action_obj(id, data, position) { + let action = actions[id]; + + if (action) { + data.from_id = action.data.from_id; + (data.to_id = action.data.to_id), (action.data = data); + } else { + action = { + id, + type: "action", + position, + data, + }; + } + + Object.assign(action, { + initialized: true, + selected: false, + dragging: false, + resizing: false, + }); + return (actions[id] = action); + } + + function transition_obj(id, source, target) { + let transition = transitions[id]; + + if (!transition) { + transition = { + id, + type: "transition", + source: source.toString(), + target: target.toString(), + sourceHandle: "right", + targetHandle: "left", + updatable: true, + animated: true, + }; + } + + Object.assign(transition, { + initialized: true, + selected: false, + dragging: false, + resizing: false, + }); + return (transitions[id] = transition); + } + + let state_id = Math.max(...workflow.states.map((state) => state.workflow_builder_id || 0)); + + workflow.states.forEach((state, i) => { + x += 400; + let doc_status_map = { + 0: "Draft", + 1: "Submitted", + 2: "Cancelled", + }; + + const id = state.workflow_builder_id || ++state_id; + elements.push( + state_obj(id, { + ...state, + doc_status: doc_status_map[state.doc_status], + }) + ); + }); + + let action_id = Math.max( + ...workflow.transitions.map( + (transition) => transition.workflow_builder_id?.replace("action-", "") || 0 + ) + ); + + workflow.transitions.forEach((transition, i) => { + const id = transition.workflow_builder_id || "action-" + ++action_id; + let action = actions[id]; + let source, target; + + if (action && action.data.from_id && action.data.to_id) { + source = states[action.data.from_id]; + target = states[action.data.to_id]; + } else { + source = Object.values(states).filter( + (state) => state.data?.state == transition.state + )[0]; + target = Object.values(states).filter( + (state) => state.data?.state == transition.next_state + )[0]; + } + + let position = { x: source.position.x + 250, y: y + 20 }; + let data = { + ...transition, + from_id: source.id, + to_id: target.id, + from: transition.state, + to: transition.next_state, + }; + + elements.push(action_obj(id, data, position)); + elements.push(transition_obj("edge-" + source.id + "-" + id, source.id, id)); + elements.push(transition_obj("edge-" + id + "-" + target.id, id, target.id)); + }); + + return elements; +} + +export function validate_transitions(state, next_state) { + let message; + if (state.doc_status == "Cancelled") { + message = __("Cannot change state of Cancelled Document ({0} State)", [ + state.state, + ]); + } + + if (state.doc_status == "Submitted" && next_state.doc_status == "Draft") { + message = __( + "Submitted document cannot be converted back to draft while transitioning from {0} State to {1} State", + [state.state, next_state.state] + ); + } + + if (state.doc_status == "Draft" && next_state.doc_status == "Cancelled") { + message = __( + "Cannot cancel before submitting while transitioning from {0} State to {1} State", + [state.state, next_state.state] + ); + } + return message; +} diff --git a/xhiveframework/public/js/workflow_builder/workflow_builder.bundle.js b/xhiveframework/public/js/workflow_builder/workflow_builder.bundle.js new file mode 100644 index 0000000..ab4d921 --- /dev/null +++ b/xhiveframework/public/js/workflow_builder/workflow_builder.bundle.js @@ -0,0 +1,69 @@ +import { createApp } from "vue"; +import { createPinia } from "pinia"; +import { useStore } from "./store"; +import WorkflowBuilderComponent from "./WorkflowBuilder.vue"; +import { registerGlobalComponents } from "./globals.js"; + +class WorkflowBuilder { + constructor({ wrapper, page, workflow }) { + this.$wrapper = $(wrapper); + this.page = page; + this.workflow = workflow; + + this.page.set_indicator("Beta", "orange"); + + this.init(); + } + + init() { + // set page title + this.page.set_title(__("Editing {0}", [this.workflow])); + + this.setup_page_actions(); + this.setup_app(); + } + + setup_page_actions() { + // clear actions + this.page.clear_actions(); + this.page.clear_menu(); + this.page.clear_custom_actions(); + + // setup page actions + this.primary_btn = this.page.set_primary_action(__("Save"), () => + this.store.save_changes() + ); + + this.reset_changes_btn = this.page.add_button(__("Reset Changes"), () => { + this.store.reset_changes(); + }); + + this.go_to_doctype_btn = this.page.add_menu_item(__("Go to Workflow"), () => + xhiveframework.set_route("Form", "Workflow", this.workflow) + ); + } + + setup_app() { + // create a pinia instance + let pinia = createPinia(); + + // create a vue instance + let app = createApp(WorkflowBuilderComponent, { workflow: this.workflow }); + SetVueGlobals(app); + app.use(pinia); + + // create a store + this.store = useStore(); + this.store.workflow_name = this.workflow; + + // register global components + registerGlobalComponents(app); + + // mount the app + this.$workflow_builder = app.mount(this.$wrapper.get(0)); + } +} + +xhiveframework.provide("xhiveframework.ui"); +xhiveframework.ui.WorkflowBuilder = WorkflowBuilder; +export default WorkflowBuilder; diff --git a/xhiveframework/public/js/xhiveframework-web.bundle.js b/xhiveframework/public/js/xhiveframework-web.bundle.js new file mode 100644 index 0000000..0bcebfc --- /dev/null +++ b/xhiveframework/public/js/xhiveframework-web.bundle.js @@ -0,0 +1,26 @@ +import "./libs.bundle.js"; +import "./xhiveframework/class.js"; +import "./xhiveframework/polyfill.js"; +import "./xhiveframework/provide.js"; +import "./xhiveframework/translate.js"; +import "./xhiveframework/form/formatters.js"; +import "./xhiveframework/format.js"; +import "./xhiveframework/utils/number_format.js"; +import "./xhiveframework/utils/utils.js"; +import "./xhiveframework/utils/common.js"; +import "./xhiveframework/ui/messages.js"; +import "./xhiveframework/utils/pretty_date.js"; +import "./xhiveframework/utils/datetime.js"; +import "./xhiveframework/microtemplate.js"; +import "./xhiveframework/query_string.js"; + +import "./xhiveframework/upload.js"; + +import "./xhiveframework/model/meta.js"; +import "./xhiveframework/model/model.js"; +import "./xhiveframework/model/perm.js"; + +import "./bootstrap-4-web.bundle"; + +import "../../website/js/website.js"; +import "./xhiveframework/socketio_client.js"; diff --git a/xhiveframework/public/js/xhiveframework/assets.js b/xhiveframework/public/js/xhiveframework/assets.js new file mode 100644 index 0000000..f84285c --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/assets.js @@ -0,0 +1,213 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +// library to mange assets (js, css, models, html) etc in the app. +// will try and get from localStorage if latest are available +// depends on xhiveframework.versions to manage versioning + +xhiveframework.require = function (items, callback) { + if (typeof items === "string") { + items = [items]; + } + items = items.map((item) => xhiveframework.assets.bundled_asset(item)); + + return new Promise((resolve) => { + xhiveframework.assets.execute(items, () => { + resolve(); + callback && callback(); + }); + }); +}; + +xhiveframework.assets = { + check: function () { + // if version is different then clear localstorage + if (window._version_number != localStorage.getItem("_version_number")) { + xhiveframework.assets.clear_local_storage(); + console.log("Cleared App Cache."); + } + + if (localStorage._last_load) { + let not_updated_since = new Date() - new Date(localStorage._last_load); + // Evict cache every 2 days + // Evict cache if page is reloaded within 10 seconds. Which could be user trying to + // refresh if things feel broken. + if ((not_updated_since < 5000 && is_reload()) || not_updated_since > 2 * 86400000) { + xhiveframework.assets.clear_local_storage(); + } + } else { + xhiveframework.assets.clear_local_storage(); + } + + xhiveframework.assets.init_local_storage(); + }, + + init_local_storage: function () { + localStorage._last_load = new Date(); + localStorage._version_number = window._version_number; + if (xhiveframework.boot) localStorage.metadata_version = xhiveframework.boot.metadata_version; + }, + + clear_local_storage: function () { + $.each( + ["_last_load", "_version_number", "metadata_version", "page_info", "last_visited"], + function (i, key) { + localStorage.removeItem(key); + } + ); + + // clear assets + for (var key in localStorage) { + if ( + key.indexOf("desk_assets:") === 0 || + key.indexOf("_page:") === 0 || + key.indexOf("_doctype:") === 0 || + key.indexOf("preferred_breadcrumbs:") === 0 + ) { + localStorage.removeItem(key); + } + } + console.log("localStorage cleared"); + }, + + // keep track of executed assets + executed_: [], + + // pass on to the handler to set + execute: function (items, callback) { + var to_fetch = []; + for (var i = 0, l = items.length; i < l; i++) { + if (!xhiveframework.assets.exists(items[i])) { + to_fetch.push(items[i]); + } + } + if (to_fetch.length) { + xhiveframework.assets.fetch(to_fetch, function () { + xhiveframework.assets.eval_assets(items, callback); + }); + } else { + xhiveframework.assets.eval_assets(items, callback); + } + }, + + eval_assets: function (items, callback) { + for (var i = 0, l = items.length; i < l; i++) { + // execute js/css if not already. + var path = items[i]; + if (xhiveframework.assets.executed_.indexOf(path) === -1) { + // execute + xhiveframework.assets.handler[xhiveframework.assets.extn(path)](xhiveframework.assets.get(path), path); + xhiveframework.assets.executed_.push(path); + } + } + callback && callback(); + }, + + // check if the asset exists in + // localstorage + exists: function (src) { + if (xhiveframework.assets.executed_.indexOf(src) !== -1) { + return true; + } + if (xhiveframework.boot.developer_mode) { + return false; + } + if (xhiveframework.assets.get(src)) { + return true; + } else { + return false; + } + }, + + // load an asset via + fetch: function (items, callback) { + // this is virtual page load, only get the the source + // *without* the template + + if (items.length === 0) { + callback(); + return; + } + + const version_string = + xhiveframework.boot.developer_mode || window.dev_server ? Date.now() : window._version_number; + + async function fetch_item(item) { + // Add the version to the URL to bust the cache for non-bundled assets + let url = new URL(item, window.location.origin); + + if (!item.includes(".bundle.") && !url.searchParams.get("v")) { + url.searchParams.append("v", version_string); + } + const response = await fetch(url.toString()); + const body = await response.text(); + xhiveframework.assets.add(item, body); + } + + xhiveframework.dom.freeze(); + const fetch_promises = items.map(fetch_item); + Promise.all(fetch_promises).then(() => { + xhiveframework.dom.unfreeze(); + callback(); + }); + }, + + add: function (src, txt) { + if ("localStorage" in window) { + try { + xhiveframework.assets.set(src, txt); + } catch (e) { + // if quota is exceeded, clear local storage and set item + xhiveframework.assets.clear_local_storage(); + xhiveframework.assets.set(src, txt); + } + } + }, + + get: function (src) { + return localStorage.getItem("desk_assets:" + src); + }, + + set: function (src, txt) { + localStorage.setItem("desk_assets:" + src, txt); + }, + + extn: function (src) { + if (src.indexOf("?") != -1) { + src = src.split("?").slice(-1)[0]; + } + return src.split(".").slice(-1)[0]; + }, + + handler: { + js: function (txt, src) { + xhiveframework.dom.eval(txt); + }, + css: function (txt, src) { + xhiveframework.dom.set_style(txt); + }, + }, + + bundled_asset(path, is_rtl = null) { + if (!path.startsWith("/assets") && path.includes(".bundle.")) { + if (path.endsWith(".css") && is_rtl) { + path = `rtl_${path}`; + } + path = xhiveframework.boot.assets_json[path] || path; + return path; + } + return path; + }, +}; + +function is_reload() { + try { + return window.performance + ?.getEntriesByType("navigation") + .map((nav) => nav.type) + .includes("reload"); + } catch (e) { + // Safari probably + return true; + } +} diff --git a/xhiveframework/public/js/xhiveframework/build_events/BuildError.vue b/xhiveframework/public/js/xhiveframework/build_events/BuildError.vue new file mode 100644 index 0000000..1eb20bf --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/build_events/BuildError.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/xhiveframework/public/js/xhiveframework/build_events/BuildSuccess.vue b/xhiveframework/public/js/xhiveframework/build_events/BuildSuccess.vue new file mode 100644 index 0000000..2fa3848 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/build_events/BuildSuccess.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/xhiveframework/public/js/xhiveframework/build_events/build_events.bundle.js b/xhiveframework/public/js/xhiveframework/build_events/build_events.bundle.js new file mode 100644 index 0000000..d24be62 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/build_events/build_events.bundle.js @@ -0,0 +1,66 @@ +import { createApp } from "vue"; +import BuildError from "./BuildError.vue"; +import BuildSuccess from "./BuildSuccess.vue"; + +let $container = $("#build-events-overlay"); +let success = null; +let error = null; + +xhiveframework.realtime.on("build_event", (data) => { + if (data.success) { + // remove executed cache for rebuilt files + let changed_files = data.changed_files; + if (Array.isArray(changed_files)) { + for (let file of changed_files) { + if (file.includes(".bundle.")) { + let parts = file.split(".bundle."); + if (parts.length === 2) { + let filename = parts[0].split("/").slice(-1)[0]; + + xhiveframework.assets.executed_ = xhiveframework.assets.executed_.filter( + (asset) => !asset.includes(`${filename}.bundle`) + ); + } + } + } + } + // update assets json + xhiveframework.call("xhiveframework.sessions.get_boot_assets_json").then((r) => { + if (r.message) { + xhiveframework.boot.assets_json = r.message; + + if (xhiveframework.hot_update) { + xhiveframework.hot_update.forEach((callback) => { + callback(); + }); + } + } + }); + show_build_success(data); + } else if (data.error) { + show_build_error(data); + } +}); + +function show_build_success(data) { + if (error) { + error.hide(); + } + + if (!success) { + let target = $('
      ').appendTo($container).get(0); + success = createApp(BuildSuccess).mount(target); + } + success.show(data); +} + +function show_build_error(data) { + if (success) { + success.hide(); + } + if (!error) { + let target = $('
      ').appendTo($container).get(0); + error = createApp(BuildError).mount(target); + } + error.show(data); +} diff --git a/xhiveframework/public/js/xhiveframework/change_log.html b/xhiveframework/public/js/xhiveframework/change_log.html new file mode 100644 index 0000000..10e8c5d --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/change_log.html @@ -0,0 +1,18 @@ +{% for (var i=0, l=change_log.length; i < l; i++) { + var app_info = change_log[i]; %} + {% if (i > 0) { %}
      {% } %} +
      +

      + {{ app_info.title }} + {{ __("updated to {0}", [app_info.version]) }} +

      +
      + {% for (var x=0, y=app_info.change_log.length; x < y; x++) { + var version_info = app_info.change_log[x]; + if(version_info) { %} +

      {{ xhiveframework.markdown(version_info[1]) }}

      + {% } + } %} +
      +
      +{% } %} diff --git a/xhiveframework/public/js/xhiveframework/class.js b/xhiveframework/public/js/xhiveframework/class.js new file mode 100644 index 0000000..56665ee --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/class.js @@ -0,0 +1,92 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +/* + +Inheritence "Class" +------------------- +see: http://ejohn.org/blog/simple-javascript-inheritance/ +To subclass, use: + + var MyClass = Class.extend({ + init: function + }) + +*/ +// https://stackoverflow.com/a/15052240/5353542 + +/* Simple JavaScript Inheritance for ES 5.1 + * based on http://ejohn.org/blog/simple-javascript-inheritance/ + * (inspired by base2 and Prototype) + * MIT Licensed. + */ +(function (global) { + "use strict"; + var fnTest = /xyz/.test(function () { + xyz; + }) + ? /\b_super\b/ + : /.*/; + + // The base Class implementation (does nothing) + function Class() {} + + // Create a new Class that inherits from this class + Class.extend = function (props) { + var _super = this.prototype; + + // Set up the prototype to inherit from the base class + // (but without running the init constructor) + var proto = Object.create(_super); + + // Copy the properties over onto the new prototype + for (var name in props) { + // Check if we're overwriting an existing function + proto[name] = + typeof props[name] === "function" && + typeof _super[name] == "function" && + fnTest.test(props[name]) + ? (function (name, fn) { + return function () { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, props[name]) + : props[name]; + } + + // The new constructor + var newClass = + typeof proto.init === "function" + ? proto.hasOwnProperty("init") + ? proto.init // All construction is actually done in the init method + : function SubClass() { + _super.init.apply(this, arguments); + } + : function EmptyClass() {}; + + // Populate our constructed prototype object + newClass.prototype = proto; + + // Enforce the constructor to be what we expect + proto.constructor = newClass; + + // And make this class extendable + newClass.extend = Class.extend; + + return newClass; + }; + + // export + global.Class = Class; +})(window); diff --git a/xhiveframework/public/js/xhiveframework/color_picker/color_picker.js b/xhiveframework/public/js/xhiveframework/color_picker/color_picker.js new file mode 100644 index 0000000..73a260e --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/color_picker/color_picker.js @@ -0,0 +1,203 @@ +import utils from "./utils"; + +class Picker { + constructor(opts) { + this.parent = opts.parent; + this.width = opts.width; + this.height = opts.height; + this.set_color(opts.color); + this.swatches = opts.swatches; + this.setup_picker(); + } + + refresh() { + this.set_selector_position(true); + this.update_color_map(); + } + + setup_picker() { + let color_picker_template = document.createElement("template"); + color_picker_template.innerHTML = ` +
      +
      + ${__("SWATCHES")}
      +
      +
      + ${__("COLOR PICKER")}
      +
      +
      +
      +
      +
      +
      +
      + `.trim(); + this.color_picker_wrapper = + color_picker_template.content.firstElementChild.cloneNode(true); + this.parent.appendChild(this.color_picker_wrapper); + this.color_map = this.color_picker_wrapper.getElementsByClassName("color-map")[0]; + this.color_selector_circle = this.color_map.getElementsByClassName("color-selector")[0]; + this.hue_map = this.color_picker_wrapper.getElementsByClassName("hue-map")[0]; + this.swatches_wrapper = this.color_picker_wrapper.getElementsByClassName("swatches")[0]; + this.hue_selector_circle = this.hue_map.getElementsByClassName("hue-selector")[0]; + this.refresh(); + this.setup_events(); + this.setup_swatches(); + } + + setup_events() { + this.setup_hue_event(); + this.setup_color_event(); + } + + setup_swatches() { + let swatch_template = document.createElement("template"); + swatch_template.innerHTML = '
      '; + this.swatches.forEach((color) => { + let swatch = swatch_template.content.firstElementChild.cloneNode(true); + this.swatches_wrapper.appendChild(swatch); + const set_values = () => { + this.set_color(color); + this.set_selector_position(); + this.update_color_map(); + }; + swatch.addEventListener("click", () => { + set_values(); + }); + swatch.onkeydown = (e) => { + const key_code = e.keyCode; + if ([13, 32].includes(key_code)) { + e.preventDefault(); + set_values(); + } + }; + swatch.style.backgroundColor = color; + }); + } + + set_selector_position(silent) { + this.hue = utils.get_hue(this.get_color()); + this.color_selector_position = this.get_pointer_coords(); + this.hue_selector_position = { + x: (this.hue * this.hue_map.offsetWidth) / 360, + y: this.hue_map.offsetHeight / 2, + }; + this.update_color_selector(silent); + this.update_hue_selector(silent); + } + + setup_color_event() { + let on_drag = (x, y) => { + this.color_selector_position.x = x; + this.color_selector_position.y = y; + this.update_color(); + this.update_color_selector(); + }; + this.setup_drag_event(this.color_map, on_drag); + } + + update_color() { + let x = this.color_selector_position.x; + let y = this.color_selector_position.y; + let w = this.color_map.offsetWidth; + let h = this.color_map.offsetHeight; + let color = utils.hsv_to_hex( + this.hue, + Math.round((x / w) * 100), + Math.round((1 - y / h) * 100) + ); + this.set_color(color); + } + + update_color_selector(silent) { + let x = this.color_selector_position.x; + let y = this.color_selector_position.y; + // set color selector position and background + this.color_selector_circle.style.top = + y - this.color_selector_circle.offsetHeight / 2 + "px"; + this.color_selector_circle.style.left = + x - this.color_selector_circle.offsetWidth / 2 + "px"; + this.color_map.style.color = this.get_color(); + !silent && this.on_change && this.on_change(this.get_color()); + } + + setup_hue_event() { + let on_drag = (x, y) => { + this.hue_selector_position.x = x; + this.hue = Math.round((x * 360) / this.hue_map.offsetWidth); + this.update_color_map(); + this.update_hue_selector(); + this.update_color(); + this.update_color_selector(); + }; + this.setup_drag_event(this.hue_map, on_drag); + } + + update_color_map() { + this.color_map.style.background = ` + linear-gradient(0deg, black, transparent), + linear-gradient(90deg, white, transparent), + hsl(${this.hue}, 100%, 50%) + `; + } + + update_hue_selector() { + let x = this.hue_selector_position.x - 1; + let y = this.hue_map.offsetHeight / 2 - 1; + // set color selector position and background + this.hue_selector_circle.style.top = y - this.hue_selector_circle.offsetHeight / 2 + "px"; + this.hue_selector_circle.style.left = x - this.hue_selector_circle.offsetWidth / 2 + "px"; + this.hue_map.style.color = `hsl(${this.hue}, 100%, 50%)`; + } + + get_pointer_coords() { + let h, s, v; + [h, s, v] = utils.get_hsv(this.get_color()); + let width = this.color_map.offsetWidth; + let height = this.color_map.offsetHeight; + let x = utils.clamp(0, (s * width) / 100, width); + let y = utils.clamp(0, (1 - v / 100) * height, height); + return { x, y }; + } + + setup_drag_event(element, callback) { + let on_drag = (event, force) => { + if (element.drag_enabled || force) { + event.preventDefault(); + event = event.touches ? event.touches[0] : event; + let element_bounds = element.getBoundingClientRect(); + let x = event.clientX - element_bounds.left; + let y = event.clientY - element_bounds.top; + x = utils.clamp(0, x, element_bounds.width); + y = utils.clamp(0, y, element_bounds.height); + callback(x, y); + } + }; + + element.addEventListener("mousedown", () => (element.drag_enabled = true)); + document.addEventListener("mouseup", () => (element.drag_enabled = false)); + document.addEventListener("mousemove", on_drag); + element.addEventListener("click", (event) => on_drag(event, true)); + + element.addEventListener("touchstart", () => (element.drag_enabled = true)); + element.addEventListener("touchend", () => (element.drag_enabled = false)); + element.addEventListener("touchcancel", () => (element.drag_enabled = false)); + element.addEventListener("touchmove", (event) => { + if (event.touches.length == 1) { + on_drag(event); + } else { + element.drag_enabled = false; + } + }); + } + + set_color(color) { + this.color = color || "#ffffff"; + } + + get_color() { + return this.color || "#ffffff"; + } +} + +export default Picker; diff --git a/xhiveframework/public/js/xhiveframework/color_picker/utils.js b/xhiveframework/public/js/xhiveframework/color_picker/utils.js new file mode 100644 index 0000000..70dfee2 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/color_picker/utils.js @@ -0,0 +1,115 @@ +export default { + get_hue(rgb) { + return this.get_hsv(rgb)[0]; + }, + + get_hsv(rgb) { + if (typeof rgb === "string") { + if (rgb.startsWith("#")) { + rgb = this.hex_to_rgb_values(rgb); + } else { + rgb = this.get_rgb_values(rgb); + } + } + return this.rgb_to_hsv(...rgb); + }, + + get_rgb_values(rgb_string) { + return rgb_string + .replace(/[()rgb\s]/g, "") + .split(",") + .map((s) => parseInt(s)); + }, + + rgb_to_hsv(r, g, b) { + r /= 255; + g /= 255; + b /= 255; + + let max = Math.max(r, g, b), + min = Math.min(r, g, b); + let h, + s, + v = max; + + let d = max - min; + s = max == 0 ? 0 : d / max; + + if (max == min) { + h = 0; // achromatic + } else { + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + + h /= 6; + } + return [Math.round(h * 360), Math.round(s * 100), Math.round(v * 100)]; + }, + + component_to_hex(c) { + const hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + }, + + rgb_to_hex(r, g, b) { + return ( + "#" + this.component_to_hex(r) + this.component_to_hex(g) + this.component_to_hex(b) + ); + }, + + hex_to_rgb_values(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]; + }, + + hsv_to_hex(h, s, v) { + s /= 100; + v /= 100; + h /= 360; + + let r, g, b; + let i = Math.floor(h * 6); + let f = h * 6 - i; + let p = v * (1 - s); + let q = v * (1 - f * s); + let t = v * (1 - (1 - f) * s); + + switch (i % 6) { + case 0: + (r = v), (g = t), (b = p); + break; + case 1: + (r = q), (g = v), (b = p); + break; + case 2: + (r = p), (g = v), (b = t); + break; + case 3: + (r = p), (g = q), (b = v); + break; + case 4: + (r = t), (g = p), (b = v); + break; + case 5: + (r = v), (g = p), (b = q); + break; + } + r = Math.round(r * 255); + g = Math.round(g * 255); + b = Math.round(b * 255); + return this.rgb_to_hex(r, g, b); + }, + + clamp(min, val, max) { + return val > max ? max : val < min ? min : val; + }, +}; diff --git a/xhiveframework/public/js/xhiveframework/data_import/data_exporter.js b/xhiveframework/public/js/xhiveframework/data_import/data_exporter.js new file mode 100644 index 0000000..684cb28 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/data_import/data_exporter.js @@ -0,0 +1,353 @@ +xhiveframework.provide("xhiveframework.data_import"); + +xhiveframework.data_import.DataExporter = class DataExporter { + constructor(doctype, exporting_for) { + this.doctype = doctype; + this.exporting_for = exporting_for; + xhiveframework.model.with_doctype(doctype, () => { + this.make_dialog(); + }); + } + + make_dialog() { + this.dialog = new xhiveframework.ui.Dialog({ + title: __("Export Data"), + fields: [ + { + fieldtype: "Select", + fieldname: "file_type", + label: __("File Type"), + options: ["Excel", "CSV"], + default: "CSV", + }, + { + fieldtype: "Select", + fieldname: "export_records", + label: __("Export Type"), + options: [ + { + label: __("All Records"), + value: "all", + }, + { + label: __("Filtered Records"), + value: "by_filter", + }, + { + label: __("5 Records"), + value: "5_records", + }, + { + label: __("Blank Template"), + value: "blank_template", + }, + ], + default: + this.exporting_for === "Insert New Records" ? "blank_template" : "all", + change: () => { + this.update_record_count_message(); + }, + }, + { + fieldtype: "HTML", + fieldname: "filter_area", + depends_on: (doc) => doc.export_records === "by_filter", + }, + { + fieldtype: "Section Break", + }, + { + fieldtype: "HTML", + fieldname: "select_all_buttons", + }, + { + label: __(this.doctype), + fieldname: this.doctype, + fieldtype: "MultiCheck", + columns: 2, + on_change: () => this.update_primary_action(), + options: this.get_multicheck_options(this.doctype), + }, + ...xhiveframework.meta.get_table_fields(this.doctype).map((df) => { + let doctype = df.options; + let child_fieldname = df.fieldname; + let label = df.reqd + ? // prettier-ignore + __('{0} ({1}) (1 row mandatory)', [__(df.label || df.fieldname, null, df.parent), __(doctype)]) + : __("{0} ({1})", [ + __(df.label || df.fieldname, null, df.parent), + __(doctype), + ]); + return { + label, + fieldname: child_fieldname, + fieldtype: "MultiCheck", + columns: 2, + on_change: () => this.update_primary_action(), + options: this.get_multicheck_options(doctype, child_fieldname), + }; + }), + ], + primary_action_label: __("Export"), + primary_action: (values) => this.export_records(values), + on_page_show: () => this.select_mandatory(), + }); + + this.make_filter_area(); + this.make_select_all_buttons(); + this.update_record_count_message(); + + this.dialog.show(); + } + + export_records() { + let method = "/api/method/xhiveframework.core.doctype.data_import.data_import.download_template"; + + let multicheck_fields = this.dialog.fields + .filter((df) => df.fieldtype === "MultiCheck") + .map((df) => df.fieldname); + + let values = this.dialog.get_values(); + + let doctype_field_map = Object.assign({}, values); + for (let key in doctype_field_map) { + if (!multicheck_fields.includes(key)) { + delete doctype_field_map[key]; + } + } + + let filters = null; + if (values.export_records === "by_filter") { + filters = this.get_filters(); + } + + open_url_post(method, { + doctype: this.doctype, + file_type: values.file_type, + export_records: values.export_records, + export_fields: doctype_field_map, + export_filters: filters, + }); + } + + make_filter_area() { + this.filter_group = new xhiveframework.ui.FilterGroup({ + parent: this.dialog.get_field("filter_area").$wrapper, + doctype: this.doctype, + on_change: () => { + this.update_record_count_message(); + }, + }); + } + + make_select_all_buttons() { + let for_insert = this.exporting_for === "Insert New Records"; + let section_title = for_insert + ? __("Select Fields To Insert") + : __("Select Fields To Update"); + let $select_all_buttons = $(` +
      +
      ${section_title}
      + + ${ + for_insert + ? `` + : "" + } + +
      + `); + xhiveframework.utils.bind_actions_with_object($select_all_buttons, this); + this.dialog.get_field("select_all_buttons").$wrapper.html($select_all_buttons); + } + + select_all() { + this.dialog.$wrapper.find(":checkbox").prop("checked", true).trigger("change"); + } + + select_mandatory() { + let mandatory_table_fields = xhiveframework.meta + .get_table_fields(this.doctype) + .filter((df) => df.reqd) + .map((df) => df.fieldname); + mandatory_table_fields.push(this.doctype); + + let multicheck_fields = this.dialog.fields + .filter((df) => df.fieldtype === "MultiCheck") + .map((df) => df.fieldname) + .filter((doctype) => mandatory_table_fields.includes(doctype)); + + let checkboxes = [].concat( + ...multicheck_fields.map((fieldname) => { + let field = this.dialog.get_field(fieldname); + return field.options + .filter((option) => option.danger) + .map((option) => option.$checkbox.find("input").get(0)); + }) + ); + + this.unselect_all(); + $(checkboxes).prop("checked", true).trigger("change"); + } + + unselect_all() { + let update_existing_records = + this.dialog.get_value("exporting_for") == "Update Existing Records"; + this.dialog.$wrapper + .find(`:checkbox${update_existing_records ? ":not([data-unit=name])" : ""}`) + .prop("checked", false) + .trigger("change"); + } + + update_record_count_message() { + let export_records = this.dialog.get_value("export_records"); + let count_method = { + all: () => xhiveframework.db.count(this.doctype), + by_filter: () => + xhiveframework.db.count(this.doctype, { + filters: this.get_filters(), + }), + blank_template: () => Promise.resolve(0), + "5_records": () => Promise.resolve(5), + }; + + count_method[export_records]().then((value) => { + let message = ""; + value = parseInt(value, 10); + if (value === 0) { + message = __("No records will be exported"); + } else if (value === 1) { + message = __("1 record will be exported"); + } else { + message = __("{0} records will be exported", [value]); + } + this.dialog.set_df_property("export_records", "description", message); + + this.update_primary_action(value); + }); + } + + update_primary_action(no_of_records) { + let $primary_action = this.dialog.get_primary_btn(); + + if (no_of_records != null) { + let label = ""; + if (no_of_records === 0) { + label = __("Export"); + } else if (no_of_records === 1) { + label = __("Export 1 record"); + } else { + label = __("Export {0} records", [no_of_records]); + } + $primary_action.html(label); + } else { + let parent_fields = this.dialog.get_value(this.doctype); + $primary_action.prop("disabled", parent_fields.length === 0); + } + } + + get_filters() { + return this.filter_group.get_filters().map((filter) => { + return filter.slice(0, 4); + }); + } + + get_multicheck_options(doctype, child_fieldname = null) { + if (!this.column_map) { + this.column_map = get_columns_for_picker(this.doctype); + } + + let autoname_field = null; + let meta = xhiveframework.get_meta(doctype); + if (meta.autoname && meta.autoname.startsWith("field:")) { + let fieldname = meta.autoname.slice("field:".length); + autoname_field = xhiveframework.meta.get_field(doctype, fieldname); + } + + let fields = child_fieldname ? this.column_map[child_fieldname] : this.column_map[doctype]; + + let is_field_mandatory = (df) => { + if (df.reqd && this.exporting_for == "Insert New Records") { + return true; + } + if (autoname_field && df.fieldname == autoname_field.fieldname) { + return true; + } + if (df.fieldname === "name") { + return true; + } + return false; + }; + + return fields + .filter((df) => { + if (autoname_field && df.fieldname === "name") { + return false; + } + return true; + }) + .map((df) => { + return { + label: __(df.label, null, df.parent), + value: df.fieldname, + danger: is_field_mandatory(df), + checked: false, + description: `${df.fieldname} ${df.reqd ? __("(Mandatory)") : ""}`, + }; + }); + } +}; + +export function get_columns_for_picker(doctype) { + let out = {}; + + const exportable_fields = (df) => { + let keep = true; + if (xhiveframework.model.no_value_type.includes(df.fieldtype)) { + keep = false; + } + if (["lft", "rgt"].includes(df.fieldname)) { + keep = false; + } + if (df.is_virtual) { + keep = false; + } + return keep; + }; + + // parent + let doctype_fields = xhiveframework.meta.get_docfields(doctype).filter(exportable_fields); + + out[doctype] = [ + { + label: __("ID"), + fieldname: "name", + fieldtype: "Data", + reqd: 1, + }, + ].concat(doctype_fields); + + // children + const table_fields = xhiveframework.meta.get_table_fields(doctype); + table_fields.forEach((df) => { + const cdt = df.options; + const child_table_fields = xhiveframework.meta.get_docfields(cdt).filter(exportable_fields); + + out[df.fieldname] = [ + { + label: __("ID"), + fieldname: "name", + fieldtype: "Data", + reqd: 1, + }, + ].concat(child_table_fields); + }); + + return out; +} diff --git a/xhiveframework/public/js/xhiveframework/data_import/import_preview.js b/xhiveframework/public/js/xhiveframework/data_import/import_preview.js new file mode 100644 index 0000000..eb41cb2 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/data_import/import_preview.js @@ -0,0 +1,352 @@ +import DataTable from "xhiveframework-datatable"; +import { get_columns_for_picker } from "./data_exporter"; + +xhiveframework.provide("xhiveframework.data_import"); + +xhiveframework.data_import.ImportPreview = class ImportPreview { + constructor({ wrapper, doctype, preview_data, frm, import_log, events = {} }) { + this.wrapper = wrapper; + this.doctype = doctype; + this.preview_data = preview_data; + this.events = events; + this.import_log = import_log; + this.frm = frm; + + xhiveframework.model.with_doctype(doctype, () => { + this.refresh(); + }); + } + + refresh() { + this.data = this.preview_data.data; + this.make_wrapper(); + this.prepare_columns(); + this.prepare_data(); + this.render_datatable(); + this.setup_styles(); + this.add_actions(); + } + + make_wrapper() { + this.wrapper.html(` +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + `); + xhiveframework.utils.bind_actions_with_object(this.wrapper, this); + + this.$table_preview = this.wrapper.find(".table-preview"); + } + + prepare_columns() { + this.columns = this.preview_data.columns.map((col, i) => { + let df = col.df; + let column_width = 120; + if (col.header_title === "Sr. No") { + return { + id: "srno", + name: "Sr. No", + content: "Sr. No", + editable: false, + focusable: false, + align: "left", + width: 60, + }; + } + + if (col.skip_import) { + let show_warnings_button = ``; + if (!col.df) { + // increase column width for unidentified columns + column_width += 50; + } + let column_title = ` + ${col.header_title || `${__("Untitled Column")}`} + ${!col.df ? show_warnings_button : ""} + `; + return { + id: xhiveframework.utils.get_random(6), + name: col.header_title || (df ? df.label : "Untitled Column"), + content: column_title, + skip_import: true, + editable: false, + focusable: false, + align: "left", + width: column_width, + format: (value) => `
      ${value}
      `, + }; + } + + let date_format = col.date_format + ? col.date_format + .replace("%Y", "yyyy") + .replace("%y", "yy") + .replace("%m", "mm") + .replace("%d", "dd") + .replace("%H", "HH") + .replace("%M", "mm") + .replace("%S", "ss") + .replace("%b", "Mon") + : null; + + let column_title = ` + ${col.header_title || df.label} + ${date_format ? `(${date_format})` : ""} + `; + + return { + id: df.fieldname, + name: col.header_title, + content: column_title, + df: df, + editable: false, + align: "left", + width: column_width, + }; + }); + } + + prepare_data() { + this.data = this.data.map((row) => { + return row.map((cell) => { + if (cell == null) { + return ""; + } + return cell; + }); + }); + } + + render_datatable() { + if (this.datatable) { + this.datatable.destroy(); + } + + this.datatable = new DataTable(this.$table_preview.get(0), { + data: this.data, + columns: this.columns, + layout: this.columns.length < 10 ? "fluid" : "fixed", + cellHeight: 35, + language: xhiveframework.boot.lang, + translations: xhiveframework.utils.datatable.get_translations(), + serialNoColumn: false, + checkboxColumn: false, + noDataMessage: __("No Data"), + disableReorderColumn: true, + }); + + let { max_rows_exceeded, max_rows_in_preview, total_number_of_rows } = this.preview_data; + if (max_rows_exceeded) { + let parts = [max_rows_in_preview, total_number_of_rows]; + this.wrapper.find(".table-message").html(` +
      + ${__("Showing only first {0} rows out of {1}", parts)} +
      + `); + } + + if (this.data.length === 0) { + this.datatable.style.setStyle(".dt-scrollable", { + height: "auto", + }); + } + + this.datatable.style.setStyle(".dt-dropdown", { + display: "none", + }); + } + + setup_styles() { + // import success checkbox + this.datatable.style.setStyle(`svg.import-success`, { + width: "16px", + fill: xhiveframework.ui.color.get_color_shade("green", "dark"), + }); + // make successfully imported rows readonly + let row_classes = this.datatable + .getRows() + .filter((row) => this.is_row_imported(row)) + .map((row) => row.meta.rowIndex) + .map((i) => `.dt-row-${i} .dt-cell`) + .join(","); + this.datatable.style.setStyle(row_classes, { + pointerEvents: "none", + backgroundColor: xhiveframework.ui.color.get_color_shade("gray", "extra-light"), + color: xhiveframework.ui.color.get_color_shade("gray", "dark"), + }); + } + + add_actions() { + let actions = [ + { + label: __("Map Columns"), + handler: "show_column_mapper", + condition: this.frm.doc.status !== "Success", + }, + { + label: __("Export Errored Rows"), + handler: "export_errored_rows", + condition: this.import_log.filter((log) => !log.success).length > 0, + }, + { + label: __("Show Warnings"), + handler: "show_warnings", + condition: this.preview_data.warnings.length > 0, + }, + ]; + + let html = actions + .filter((action) => action.condition) + .map((action) => { + return ` + `; + }); + + this.wrapper.find(".table-actions").html(html); + } + + export_errored_rows() { + this.frm.trigger("export_errored_rows"); + } + + show_warnings() { + this.frm.scroll_to_field("import_warnings"); + } + + show_column_warning(_, $target) { + let $warning = this.frm + .get_field("import_warnings") + .$wrapper.find(`[data-col=${$target.data("col")}]`); + xhiveframework.utils.scroll_to($warning, true, 30); + } + + show_column_mapper() { + let column_picker_fields = get_columns_for_picker(this.doctype); + let changed = []; + let fields = this.preview_data.columns.map((col, i) => { + let df = col.df; + if (col.header_title === "Sr. No") return []; + + let fieldname; + if (!df) { + fieldname = null; + } else if (col.map_to_field) { + fieldname = col.map_to_field; + } else if (col.is_child_table_field) { + fieldname = `${col.child_table_df.fieldname}.${df.fieldname}`; + } else { + fieldname = df.fieldname; + } + return [ + { + label: "", + fieldtype: "Data", + default: col.header_title, + fieldname: `Column ${i}`, + read_only: 1, + }, + { + fieldtype: "Column Break", + }, + { + fieldtype: "Autocomplete", + fieldname: i, + label: "", + max_items: Infinity, + options: [ + { + label: __("Don't Import"), + value: "Don't Import", + }, + ].concat(get_fields_as_options(this.doctype, column_picker_fields)), + default: fieldname || "Don't Import", + change() { + changed.push(i); + }, + }, + { + fieldtype: "Section Break", + }, + ]; + }); + // flatten the array + fields = fields.reduce((acc, curr) => [...acc, ...curr]); + let file_name = (this.frm.doc.import_file || "").split("/").pop(); + let parts = [file_name.bold(), this.doctype.bold()]; + fields = [ + { + fieldtype: "HTML", + fieldname: "heading", + options: ` +
      + ${__("Map columns from {0} to fields in {1}", parts)} +
      + `, + }, + { + fieldtype: "Section Break", + }, + ].concat(fields); + + let dialog = new xhiveframework.ui.Dialog({ + title: __("Map Columns"), + fields, + primary_action: (values) => { + let changed_map = {}; + changed.map((i) => { + let header_row_index = i - 1; + changed_map[header_row_index] = values[i]; + }); + if (changed.length > 0) { + this.events.remap_column(changed_map); + } + dialog.hide(); + }, + }); + dialog.$body.addClass("map-columns"); + dialog.show(); + } + + is_row_imported(row) { + let serial_no = row[0].content; + return this.import_log.find((log) => { + return log.success && JSON.parse(log.row_indexes || "[]").includes(serial_no); + }); + } +}; + +function get_fields_as_options(doctype, column_map) { + let keys = [doctype]; + xhiveframework.meta.get_table_fields(doctype).forEach((df) => { + keys.push(df.fieldname); + }); + // flatten array + return [].concat( + ...keys.map((key) => { + return column_map[key].map((df) => { + let label = __(df.label, null, df.parent); + let value = df.fieldname; + if (doctype !== key) { + let table_field = xhiveframework.meta.get_docfield(doctype, key); + label = `${__(df.label, null, df.parent)} (${__(table_field.label)})`; + value = `${table_field.fieldname}.${df.fieldname}`; + } + return { + label, + value, + description: value, + }; + }); + }) + ); +} diff --git a/xhiveframework/public/js/xhiveframework/data_import/index.js b/xhiveframework/public/js/xhiveframework/data_import/index.js new file mode 100644 index 0000000..8f49e4b --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/data_import/index.js @@ -0,0 +1,2 @@ +import "./import_preview"; +import "./data_exporter"; diff --git a/xhiveframework/public/js/xhiveframework/db.js b/xhiveframework/public/js/xhiveframework/db.js new file mode 100644 index 0000000..cabf89b --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/db.js @@ -0,0 +1,134 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.db = { + get_list: function (doctype, args) { + if (!args) { + args = {}; + } + args.doctype = doctype; + if (!args.fields) { + args.fields = ["name"]; + } + if (!("limit" in args)) { + args.limit = 20; + } + return new Promise((resolve) => { + xhiveframework.call({ + method: "xhiveframework.desk.reportview.get_list", + args: args, + type: "GET", + callback: function (r) { + resolve(r.message); + }, + }); + }); + }, + exists: function (doctype, name) { + return new Promise((resolve) => { + xhiveframework.db.get_value(doctype, { name: name }, "name").then((r) => { + r.message && r.message.name ? resolve(true) : resolve(false); + }); + }); + }, + get_value: function (doctype, filters, fieldname, callback, parent_doc) { + return xhiveframework.call({ + method: "xhiveframework.client.get_value", + type: "GET", + args: { + doctype: doctype, + fieldname: fieldname, + filters: filters, + parent: parent_doc, + }, + callback: function (r) { + callback && callback(r.message); + }, + }); + }, + get_single_value: (doctype, field) => { + return new Promise((resolve) => { + xhiveframework + .call({ + method: "xhiveframework.client.get_single_value", + args: { doctype, field }, + type: "GET", + }) + .then((r) => resolve(r ? r.message : null)); + }); + }, + set_value: function (doctype, docname, fieldname, value, callback) { + return xhiveframework.call({ + method: "xhiveframework.client.set_value", + args: { + doctype: doctype, + name: docname, + fieldname: fieldname, + value: value, + }, + callback: function (r) { + callback && callback(r.message); + }, + }); + }, + get_doc: function (doctype, name, filters) { + return new Promise((resolve, reject) => { + xhiveframework + .call({ + method: "xhiveframework.client.get", + type: "GET", + args: { doctype, name, filters }, + callback: (r) => { + xhiveframework.model.sync(r.message); + resolve(r.message); + }, + }) + .fail(reject); + }); + }, + insert: function (doc) { + return xhiveframework.xcall("xhiveframework.client.insert", { doc }); + }, + delete_doc: function (doctype, name) { + return new Promise((resolve) => { + xhiveframework.call("xhiveframework.client.delete", { doctype, name }, (r) => resolve(r.message)); + }); + }, + count: function (doctype, args = {}) { + let filters = args.filters || {}; + let limit = args.limit; + + // has a filter with childtable? + const distinct = + Array.isArray(filters) && + filters.some((filter) => { + return filter[0] !== doctype; + }); + + const fields = []; + + return xhiveframework.xcall("xhiveframework.desk.reportview.get_count", { + doctype, + filters, + fields, + distinct, + limit, + }); + }, + get_link_options(doctype, txt = "", filters = {}) { + return new Promise((resolve) => { + xhiveframework.call({ + type: "GET", + method: "xhiveframework.desk.search.search_link", + args: { + doctype, + txt, + filters, + }, + callback(r) { + resolve(r.message); + }, + }); + }); + }, +}; diff --git a/xhiveframework/public/js/xhiveframework/defaults.js b/xhiveframework/public/js/xhiveframework/defaults.js new file mode 100644 index 0000000..3a311d9 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/defaults.js @@ -0,0 +1,140 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +xhiveframework.defaults = { + get_user_default: function (key) { + let defaults = xhiveframework.boot.user.defaults; + let d = defaults[key]; + if (!d && xhiveframework.defaults.is_a_user_permission_key(key)) { + d = defaults[xhiveframework.model.scrub(key)]; + // Check for default user permission values + let user_default = this.get_user_permission_default(key, defaults); + if (user_default) d = user_default; + } + if ($.isArray(d)) d = d[0]; + + if (!xhiveframework.defaults.in_user_permission(key, d)) { + return; + } + + return d; + }, + + get_user_permission_default: function (key, defaults) { + let permissions = this.get_user_permissions(); + let user_default = null; + if (permissions[key]) { + permissions[key].forEach((item) => { + if (defaults[key] == item.doc) { + user_default = item.doc; + } + }); + + permissions[key].forEach((item) => { + if (item.is_default) { + user_default = item.doc; + } + }); + } + + return user_default; + }, + + get_user_defaults: function (key) { + var defaults = xhiveframework.boot.user.defaults; + var d = defaults[key]; + + if (xhiveframework.defaults.is_a_user_permission_key(key)) { + if (d && $.isArray(d) && d.length === 1) { + // Use User Permission value when only when it has a single value + d = d[0]; + } else { + d = defaults[key] || defaults[xhiveframework.model.scrub(key)]; + } + } + if (!$.isArray(d)) d = [d]; + + // filter out values which are not permitted to the user + d.filter((item) => { + if (xhiveframework.defaults.in_user_permission(key, item)) { + return item; + } + }); + return d; + }, + get_global_default: function (key) { + var d = xhiveframework.sys_defaults[key]; + if ($.isArray(d)) d = d[0]; + return d; + }, + get_global_defaults: function (key) { + var d = xhiveframework.sys_defaults[key]; + if (!$.isArray(d)) d = [d]; + return d; + }, + set_user_default_local: function (key, value) { + xhiveframework.boot.user.defaults[key] = value; + }, + get_default: function (key) { + var defaults = xhiveframework.boot.user.defaults; + var value = defaults[key]; + if (xhiveframework.defaults.is_a_user_permission_key(key)) { + if (value && $.isArray(value) && value.length === 1) { + value = value[0]; + } else { + value = defaults[xhiveframework.model.scrub(key)]; + } + } + + if (!xhiveframework.defaults.in_user_permission(key, value)) { + return; + } + + if (value) { + try { + return JSON.parse(value); + } catch (e) { + return value; + } + } + }, + + is_a_user_permission_key: function (key) { + return key.indexOf(":") === -1 && key !== xhiveframework.model.scrub(key); + }, + + in_user_permission: function (key, value) { + let user_permission = this.get_user_permissions()[xhiveframework.model.unscrub(key)]; + + if (user_permission && user_permission.length) { + return user_permission.some((perm) => { + return perm.doc === value; + }); + } else { + // there is no user permission for this doctype + // so we can allow this doc i.e., value + return true; + } + }, + + get_user_permissions: function () { + return this._user_permissions || {}; + }, + + update_user_permissions: function () { + const method = "xhiveframework.core.doctype.user_permission.user_permission.get_user_permissions"; + xhiveframework.call(method).then((r) => { + if (r.message) { + this._user_permissions = Object.assign({}, r.message); + } + }); + }, + + load_user_permission_from_boot: function () { + if (xhiveframework.boot.user.user_permissions) { + this._user_permissions = Object.assign({}, xhiveframework.boot.user.user_permissions); + } else { + xhiveframework.defaults.update_user_permissions(); + } + }, +}; diff --git a/xhiveframework/public/js/xhiveframework/desk.js b/xhiveframework/public/js/xhiveframework/desk.js new file mode 100644 index 0000000..eaae625 --- /dev/null +++ b/xhiveframework/public/js/xhiveframework/desk.js @@ -0,0 +1,554 @@ +// Copyright (c) 2015, XhiveFramework Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt +/* eslint-disable no-console */ + +// __('Modules') __('Domains') __('Places') __('Administration') # for translation, don't remove + +xhiveframework.start_app = function () { + if (!xhiveframework.Application) return; + xhiveframework.assets.check(); + xhiveframework.provide("xhiveframework.app"); + xhiveframework.provide("xhiveframework.desk"); + xhiveframework.app = new xhiveframework.Application(); +}; + +$(document).ready(function () { + if (!xhiveframework.utils.supportsES6) { + xhiveframework.msgprint({ + indicator: "red", + title: __("Browser not supported"), + message: __( + "Some of the features might not work in your browser. Please update your browser to the latest version." + ), + }); + } + xhiveframework.start_app(); +}); + +xhiveframework.Application = class Application { + constructor() { + this.startup(); + } + + startup() { + xhiveframework.realtime.init(); + xhiveframework.model.init(); + + this.load_bootinfo(); + this.load_user_permissions(); + this.make_nav_bar(); + this.set_favicon(); + this.set_fullwidth_if_enabled(); + this.add_browser_class(); + this.setup_energy_point_listeners(); + this.setup_copy_doc_listener(); + + xhiveframework.ui.keys.setup(); + + xhiveframework.ui.keys.add_shortcut({ + shortcut: "shift+ctrl+g", + description: __("Switch Theme"), + action: () => { + if (xhiveframework.theme_switcher && xhiveframework.theme_switcher.dialog.is_visible) { + xhiveframework.theme_switcher.hide(); + } else { + xhiveframework.theme_switcher = new xhiveframework.ui.ThemeSwitcher(); + xhiveframework.theme_switcher.show(); + } + }, + }); + + xhiveframework.ui.add_system_theme_switch_listener(); + const root = document.documentElement; + + const observer = new MutationObserver(() => { + xhiveframework.ui.set_theme(); + }); + observer.observe(root, { + attributes: true, + attributeFilter: ["data-theme-mode"], + }); + + xhiveframework.ui.set_theme(); + + // page container + this.make_page_container(); + if ( + !window.Cypress && + xhiveframework.boot.onboarding_tours && + xhiveframework.boot.user.onboarding_status != null + ) { + let pending_tours = !xhiveframework.boot.onboarding_tours.every( + (tour) => xhiveframework.boot.user.onboarding_status[tour[0]]?.is_complete + ); + if (pending_tours && xhiveframework.boot.onboarding_tours.length > 0) { + xhiveframework.require("onboarding_tours.bundle.js", () => { + xhiveframework.utils.sleep(1000).then(() => { + xhiveframework.ui.init_onboarding_tour(); + }); + }); + } + } + this.set_route(); + + // trigger app startup + $(document).trigger("startup"); + + $(document).trigger("app_ready"); + + if (xhiveframework.boot.messages) { + xhiveframework.msgprint(xhiveframework.boot.messages); + } + + if (xhiveframework.user_roles.includes("System Manager")) { + // delayed following requests to make boot faster + setTimeout(() => { + this.show_change_log(); + this.show_update_available(); + }, 1000); + } + + if (!xhiveframework.boot.developer_mode) { + let console_security_message = __( + "Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand." + ); + console.log(`%c${console_security_message}`, "font-size: large"); + } + + this.show_notes(); + + if (xhiveframework.ui.startup_setup_dialog && !xhiveframework.boot.setup_complete) { + xhiveframework.ui.startup_setup_dialog.pre_show(); + xhiveframework.ui.startup_setup_dialog.show(); + } + + xhiveframework.realtime.on("version-update", function () { + var dialog = xhiveframework.msgprint({ + message: __( + "The application has been updated to a new version, please refresh this page" + ), + indicator: "green", + title: __("Version Updated"), + }); + dialog.set_primary_action(__("Refresh"), function () { + location.reload(true); + }); + dialog.get_close_btn().toggle(false); + }); + + // listen to build errors + this.setup_build_events(); + + if (xhiveframework.sys_defaults.email_user_password) { + var email_list = xhiveframework.sys_defaults.email_user_password.split(","); + for (var u in email_list) { + if (email_list[u] === xhiveframework.user.name) { + this.set_password(email_list[u]); + } + } + } + + // REDESIGN-TODO: Fix preview popovers + this.link_preview = new xhiveframework.ui.LinkPreview(); + } + + set_route() { + if (xhiveframework.boot && localStorage.getItem("session_last_route")) { + xhiveframework.set_route(localStorage.getItem("session_last_route")); + localStorage.removeItem("session_last_route"); + } else { + // route to home page + xhiveframework.router.route(); + } + xhiveframework.router.on("change", () => { + $(".tooltip").hide(); + }); + } + + set_password(user) { + var me = this; + xhiveframework.call({ + method: "xhiveframework.core.doctype.user.user.get_email_awaiting", + args: { + user: user, + }, + callback: function (email_account) { + email_account = email_account["message"]; + if (email_account) { + var i = 0; + if (i < email_account.length) { + me.email_password_prompt(email_account, user, i); + } + } + }, + }); + } + + email_password_prompt(email_account, user, i) { + var me = this; + const email_id = email_account[i]["email_id"]; + let d = new xhiveframework.ui.Dialog({ + title: __("Password missing in Email Account"), + fields: [ + { + fieldname: "password", + fieldtype: "Password", + label: __( + "Please enter the password for: {0}", + [email_id], + "Email Account" + ), + reqd: 1, + }, + { + fieldname: "submit", + fieldtype: "Button", + label: __("Submit", null, "Submit password for Email Account"), + }, + ], + }); + d.get_input("submit").on("click", function () { + //setup spinner + d.hide(); + var s = new xhiveframework.ui.Dialog({ + title: __("Checking one moment"), + fields: [ + { + fieldtype: "HTML", + fieldname: "checking", + }, + ], + }); + s.fields_dict.checking.$wrapper.html(''); + s.show(); + xhiveframework.call({ + method: "xhiveframework.email.doctype.email_account.email_account.set_email_password", + args: { + email_account: email_account[i]["email_account"], + password: d.get_value("password"), + }, + callback: function (passed) { + s.hide(); + d.hide(); //hide waiting indication + if (!passed["message"]) { + xhiveframework.show_alert( + { message: __("Login Failed please try again"), indicator: "error" }, + 5 + ); + me.email_password_prompt(email_account, user, i); + } else { + if (i + 1 < email_account.length) { + i = i + 1; + me.email_password_prompt(email_account, user, i); + } + } + }, + }); + }); + d.show(); + } + load_bootinfo() { + if (xhiveframework.boot) { + this.setup_workspaces(); + xhiveframework.model.sync(xhiveframework.boot.docs); + this.check_metadata_cache_status(); + this.set_globals(); + this.sync_pages(); + xhiveframework.router.setup(); + this.setup_moment(); + if (xhiveframework.boot.print_css) { + xhiveframework.dom.set_style(xhiveframework.boot.print_css, "print-style"); + } + xhiveframework.user.name = xhiveframework.boot.user.name; + xhiveframework.router.setup(); + } else { + this.set_as_guest(); + } + } + + setup_workspaces() { + xhiveframework.modules = {}; + xhiveframework.workspaces = {}; + for (let page of xhiveframework.boot.allowed_workspaces || []) { + xhiveframework.modules[page.module] = page; + xhiveframework.workspaces[xhiveframework.router.slug(page.name)] = page; + } + } + + load_user_permissions() { + xhiveframework.defaults.load_user_permission_from_boot(); + + xhiveframework.realtime.on( + "update_user_permissions", + xhiveframework.utils.debounce(() => { + xhiveframework.defaults.update_user_permissions(); + }, 500) + ); + } + + check_metadata_cache_status() { + if (xhiveframework.boot.metadata_version != localStorage.metadata_version) { + xhiveframework.assets.clear_local_storage(); + xhiveframework.assets.init_local_storage(); + } + } + + set_globals() { + xhiveframework.session.user = xhiveframework.boot.user.name; + xhiveframework.session.logged_in_user = xhiveframework.boot.user.name; + xhiveframework.session.user_email = xhiveframework.boot.user.email; + xhiveframework.session.user_fullname = xhiveframework.user_info().fullname; + + xhiveframework.user_defaults = xhiveframework.boot.user.defaults; + xhiveframework.user_roles = xhiveframework.boot.user.roles; + xhiveframework.sys_defaults = xhiveframework.boot.sysdefaults; + + xhiveframework.ui.py_date_format = xhiveframework.boot.sysdefaults.date_format + .replace("dd", "%d") + .replace("mm", "%m") + .replace("yyyy", "%Y"); + xhiveframework.boot.user.last_selected_values = {}; + } + sync_pages() { + // clear cached pages if timestamp is not found + if (localStorage["page_info"]) { + xhiveframework.boot.allowed_pages = []; + var page_info = JSON.parse(localStorage["page_info"]); + $.each(xhiveframework.boot.page_info, function (name, p) { + if (!page_info[name] || page_info[name].modified != p.modified) { + delete localStorage["_page:" + name]; + } + xhiveframework.boot.allowed_pages.push(name); + }); + } else { + xhiveframework.boot.allowed_pages = Object.keys(xhiveframework.boot.page_info); + } + localStorage["page_info"] = JSON.stringify(xhiveframework.boot.page_info); + } + set_as_guest() { + xhiveframework.session.user = "Guest"; + xhiveframework.session.user_email = ""; + xhiveframework.session.user_fullname = "Guest"; + + xhiveframework.user_defaults = {}; + xhiveframework.user_roles = ["Guest"]; + xhiveframework.sys_defaults = {}; + } + make_page_container() { + if ($("#body").length) { + $(".splash").remove(); + xhiveframework.temp_container = $("

      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 z

      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%0AG>d{36(CY?uN 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

    • 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≻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 ?U#0wX^H6-b027J5&QY~Lh;X8?yI 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

      fFa^^Eli6avL?{i{2Mh*-!E_)Q!wHRA zquz8d702tssgFPm+i8HpEZeD{q;<<-7Pz+ScIMaa$IT?(HxLLE618xdJtCn<1R9ZQ zwmamHEuQ*sQ`}@cl}Ihu8x#taLbX6S11hDWqND@UvZ9#P#hhOVb3UDvxfp}SLd0rg zF4eWE6d31J*sNnNMHicL1~JwFn=G57Ut*viQGnRJCjlk^MNxt%htK#X&DwE{B+eU( zyeJ%P1l=sr@53z8!Zagj;6pyY9w100y-wXQ165VS2pVqlqR%9a$NdHrNuAcSTy20t zFk#@lvgfqZmt1l_a$fFJv1%Wsc2`oqATgObP%*iJx%fPeF}a-jQ>Dc*xsv;+l0Sq} zv0hS{vLFIui+H*Z<}yAbqg^AT8mFbubt-#mU3SA_Ff)qmBEp?BH^uCH-DB7n^}D9g zS#@S(on6aqeI9$8hCS4XGgZs+oUT*^!XPgkNC<{~A3t6ginw8n03M1N9YtOkQl@Dw zj#XW|Z9t`FZye{!F^+!+c?dl4RUG0s1WByBZxTPD1o3^ExA{eXP#iyh00^2gW8yN# z>v@LmNwF@4ya1GHIPYmC=2Dr(w7IHNfz`Uq)`SgBoZpvVH(YSg)33JD%D&&_M7Wlo zVX`_Mk0;BeyZ!=$qJ(oQgof%Mt+^64D(Hv5wj8}PUW7(rB*`xJY$ z`nK$PpbvXhI6`r%SNW$0)l?$nZjep}{SN6@4XB~?$1S!N!-CJiPWVvjUzEL}g9xtn zda+#pLAw8b1N&by|NqWl{_`RJ$63Jabn>rRE)YQW?09z!Xdv|M3>()juRRqK)_*-R zn?UAdP3Wa9!QlmiVzN_FG5>}}=9&E2NL0w6DvFYtq5a179NM^g_$V%6u;*<0bMXd5 zqA?h)4$oFB_yU2TP$+~1R3gLaQ?hM0H^7iv_;dQ<;^KuGC9|gs|L;(W=6|46Z8#zf zAV5R_@-8xV0-VO3yb@W0%36bxxAwI`?K zEg6-tI-QvCe7XS#37w>+M&a@)><_w6^JRt4FcTmaKgB@b?Ru|jN4pdpQ}pS5di)PE zvuAOIB>1Vng;vh4DAWx$ttn:n&Y3J(F%MgS0j4AsOlPp;kC;bT%#hdlcJf?URo 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 literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-MediumItalic.woff2 b/xhiveframework/public/css/fonts/inter/Inter-MediumItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..96767155d900aae798a4208e57e29c915110e6d3 GIT binary patch literal 118392 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±Fcf(?i3(ZO=1NOA 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%0D`keGW^{m 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 I*G6sz0F{u7UjP6A literal 0 HcmV?d00001 diff --git a/xhiveframework/public/css/fonts/inter/Inter-Regular.woff2 b/xhiveframework/public/css/fonts/inter/Inter-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b8699af29b021cbbbdf82e18f5c4a2271d19b616 GIT binary patch literal 108488 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